将 DLL 加载到单独的 AppDomain

Posted

技术标签:

【中文标题】将 DLL 加载到单独的 AppDomain【英文标题】:Loading DLL into separate AppDomain 【发布时间】:2010-12-22 16:39:45 【问题描述】:

我正在编写一个插件架构。我的插件 dll 位于运行插件管理器的子目录中。 我正在将插件加载到单独的 AppDomain 中,如下所示:

string subDir;//initialized to the path of the module's directory.
AppDomainSetup setup = new AppDomainSetup();
setup.PrivateBinPath = subDir;
setup.ApplicationBase = subDir;

AppDomain newDomain= AppDomain.CreateDomain(subDir, null, setup);

byte[] file = File.ReadAllBytes(dllPath);//dll path is a dll inside subDir
newDomain.Load(file);

但是。 newDomain.Load 返回当前域尝试加载的程序集。因为插件 dll 在子目录中,当前域不能也不应该看到这些 dll,并且当前域会抛出 FileLoadException "ex = "无法加载文件或程序集...或其依赖项之一。"

问题是,我们可以将程序集加载到单独的 AppDomain 中而不返回加载的程序集吗?

我知道我可以在当前域中为 AssemblyResolve 事件添加一个处理程序并返回 null,但我不想走这条路。

提前致谢。

【问题讨论】:

看看这个:***.com/questions/2715104/… 和这个:***.com/questions/2132649/… 如果您正在构建插件架构,也许这篇文章会很有用:msdn.microsoft.com/en-us/magazine/cc163476.aspx 【参考方案1】:

您也可以使用 DoCallBack - 这是我在 SO 上阅读了很多关于它的内容后整理的。这将创建一个 appdomain,检查程序集是否具有相同的签名公钥,加载程序集,执行静态方法,卸载 appdomain,然后删除 dll。

static void Main(string[] args)
    
        string unknownAppPath = @"path-to-your-dll";

        Console.WriteLine("Testing");
        try
        
            AppDomainSetup setup = new AppDomainSetup();
            setup.AppDomainInitializer = new AppDomainInitializer(TestAppDomain);
            setup.AppDomainInitializerArguments = new string[]  unknownAppPath ;
            AppDomain testDomain = AppDomain.CreateDomain("test", AppDomain.CurrentDomain.Evidence, setup);
            AppDomain.Unload(testDomain);
            File.Delete(unknownAppPath);
        
        catch (Exception x)
        
            Console.WriteLine(x.Message);
        
        Console.ReadKey(); 
    

    public static void TestAppDomain(string[] args)
    
        string unknownAppPath = args[0];
        AppDomain.CurrentDomain.DoCallBack(delegate()
        
            //check that the new assembly is signed with the same public key
            Assembly unknownAsm = AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(unknownAppPath));
            //get the new assembly public key
            byte[] unknownKeyBytes = unknownAsm.GetName().GetPublicKey();
            string unknownKeyStr = BitConverter.ToString(unknownKeyBytes);
            //get the current public key
            Assembly asm = Assembly.GetExecutingAssembly();
            AssemblyName aname = asm.GetName();
            byte[] pubKey = aname.GetPublicKey();
            string hexKeyStr = BitConverter.ToString(pubKey);
            if (hexKeyStr == unknownKeyStr)
            
                //keys match so execute a method
                Type classType = unknownAsm.GetType("namespace.classname");
                classType.InvokeMember("method-you-want-to-invoke", BindingFlags.InvokeMethod, null, null, null);
            
        );
    

【讨论】:

【参考方案2】:

正如下面给出的链接中指出的那样:

Loading/Unloading assembly in different AppDomain

Load Assembly in New AppDomain without loading it in Parent AppDomain

似乎在另一个AppDomain 对象上调用Load() 方法会导致该程序集也被加载到当前AppDomain 中。

解决方案是改用AppDomain 类的CreateInstanceFromAndUnwrap() 方法。您传递程序集的路径和要转换为的类型。

【讨论】:

以上是关于将 DLL 加载到单独的 AppDomain的主要内容,如果未能解决你的问题,请参考以下文章

将 DLL 加载到具有已知唯一公共接口的单独 AppDomain 中

从其他 .dll 加载服务并隔离运行

如果 XIB 在 DLL / 库中,则加载视图时出错

将 DLL 构建到单独的文件夹

将DLL构建到单独的文件夹

加载链接到使用空DllMain的DLL时,应用程序无法启动(0xC0000142)