在运行时加载程序集,Autocad 插件示例

Loading assembly at runtime, Autocad plugin example

在这个主题上花费了数小时(以及访问了数十页)后,我不得不寻求帮助。我已经看到很多关于这个主题的 post,但我无法解决我遇到的问题。

基本上,我想做的事情非常简单:将程序集从我的文件夹加载到应用程序。


这是一个简短的问题(所有其他细节在其余文本中解释) 我想使用 Assembly.LoadFrom 方法来加载我的程序集(项目引用了相同的程序集文件,但 CopyLocal: false),但是尽管在调用使用它的方法时加载了程序集,但程序会尝试从默认位置加载程序集.如果找到,则加载 2 个相同的程序集,程序使用最后一个,如果未找到,则引发 FileNotFoundException。 但是,当我在 Autocad 插件中使用相同的想法时,尽管找不到文件,但一切正常并且不会引发异常。


我有两个简单项目的测试解决方案:TestExe(控制台应用程序)和 TestDll(dll 库)。 我想在 TestExe 中使用 TestDll 中的类型,所以我添加了对 TestDll 的引用(我不是在引用 TestDll 项目,而是在指定位置引用文件),但我想在运行时手动加载 TestDll。为什么需要这样做在文末用 Autocad 示例进行了解释。

据我了解,Assembly.LoadFrom 方法可用于此目的。所以基本的想法是:在 TestExe 中的方法使用它之前加载 TestDll,所以当调用方法时我们已经加载了程序集。这样,无论默认目录中是否存在引用的dll,我们都已经加载了程序集并且将要使用它。

来自 MSDN:

If an assembly with the same identity is already loaded, LoadFrom returns the loaded assembly even if a different path was specified.

所以我明白,如果我加载一次 dll,同一程序集(也来自其他位置)的每次下一次加载都会知道它已经被添加,所以第一个将被使用。


项目:TestExe

//File: Program.cs
using System;
namespace TestExe
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(new ClassExe().ExeMethod());
        }
    }    
}
//File: ClassExe.cs
namespace TestExe
{
    class ClassExe
    {
        public string ExeMethod()
        {
            return new TestDll.ClassDll().DllMethod();
        }
    }
}

项目 TestDll

using System.Reflection;

namespace TestDll
{
    public class ClassDll
    {
        public string DllMethod()
        {
            return Assembly.GetExecutingAssembly().Location;
        }
    }
}

如您所见,任务很简单:显示被调用程序集的位置。

假设 TestDll.dll 被复制到应用程序文件夹 Extern\TestDll.dll.

如果我在 TestExe 项目中设置 属性 CopyLocal: false 对 TestDll 的引用,程序将失败并出现 FileNotFoundException。 (1) 是不是因为程序集只在默认目录(application dir and TestDll\TestDll.dll)中搜索?

然后我尝试手动加载程序集,然后再使用它:

static void Main(string[] args)
    {
        Assembly.LoadFrom(@"Extern\TestDll.dll");
        Console.WriteLine(new ClassExe().ExeMethod());
    }

但是我得到了同样的 FileNotFoundException,虽然 TestDll 已经加载

当我设置属性 CopyLocal: true 时,程序运行。但是,它会从默认位置再次加载 TestDll.dll,并忽略已经加载的程序集。

我管理程序按我想要的方式工作(使用程序集 Extern\TestDll.dll)的唯一方法是使用 AppDomain.AssemblyResolve 事件。


我有 Autocad 插件,它使用来自不同解决方案的 10 个不同的 dll。所以我有 plugin.dll 引用 Documents 文件夹中的 10 个 dll 文件,并设置 CopyLocal: true。但是,当部署给客户时,我只在应用程序文件夹中保留 plugin.dll,所有其他 dll 都在 Libraries 子目录中。在插件 class 的静态构造函数中,我将 Assembly.LoadFrom 从 Libraries 子目录加载,一切正常。

很抱歉post但我想详细解释一下。

感谢您的任何反馈:)

您可以通过检查程序集的加载上下文来尝试。 Here is an example and here 是一篇文章。

基本原理是 AutoCad 在同一 AppDomain 中加载其程序集和插件程序集,其中 acad.exe 的进程是 运行ning 因此它们对所有其他加载的程序集可见,因为它们是插件 dll 的引用。但是当你在 acad 进程中加载​​一个 dll 时,它对 .exe 是不可见的,反之亦然 - 它们 运行 在不同的 app domains 中并且看不到对方。

对于所有对此主题感兴趣的人,我已经找到了答案。

我再回顾一下:想法是从项目中引用 dll 并在代码中使用它,但后来部署应用程序时手动加载相同的 dll。所以加载引用的 dll 会失败,但在此之前,dll 将被手动加载,所以一切都应该有效。事实并非如此,因为尽管 dll 是手动加载的,但它仍然会搜索引用的文件,这会产生问题。

在关于 CLR Binder 的 this 广泛文章中,这一段解释了我的问题:

Now, let's talk about the location of the assembly, while identifying if the assembly loaded via LoadFrom() is the same as the Assembly loaded via Load(). Even if the types in two assemblies are identical, if the two assemblies are loaded from different paths, they are not considered identical as far as loader contexts are concerned. This leads to situations where the same assembly is loaded repeatedly in the same application domain, but into different contexts (Load and LoadFrom) and a type in the assembly in the Load context will not be allowed to be the same type in the LoadFrom context (even if they are the same assemblies as far as the assembly identities are concerned).

感谢大家的时间和帮助。