C# 在不同的 AppDomain 中加载/卸载程序集

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2132649/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 23:45:25  来源:igfitidea点击:

Loading/Unloading assembly in different AppDomain

c#reflectionappdomainmmc

提问by Scoregraphic

I need to execute a method in an assembly loaded during runtime. Now I want to unload those loaded assemblies after the method call. I know that I need a new AppDomain so I can unload the libraries. But here, the problem arises.

我需要在运行时加载的程序集中执行一个方法。现在我想在方法调用后卸载那些加载的程序集。我知道我需要一个新的 AppDomain 以便我可以卸载这些库。但在这里,问题出现了。

The assemblies going to load are plugins in my plugin framework. They have no entry point at all. All I know is that they contain some types which implement a given interface. The old, non-AppDomain-code looks like this (slightly shortened):

要加载的程序集是我的插件框架中的插件。他们根本没有切入点。我所知道的是它们包含一些实现给定接口的类型。旧的非 AppDomain 代码如下所示(略有缩短):

try
{
    string path = Path.GetFullPath("C:\library.dll");
    AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    Assembly asm = Assembly.LoadFrom(path);
    Type[] types = asm.GetExportedTypes();
    foreach (Type t in types)
    {
        if ((t.GetInterface("IStarter") != null) && !t.IsAbstract)
        {
            object tempObj = Activator.CreateInstance(t);
            MethodInfo info = t.GetMethod("GetParameters");
            if (info != null)
            {
                return info.Invoke(tempObj, null) as string;
            }
        }
    }
}
catch (Exception ex)
{
    MessageBox.Show(String.Format("Damn '{0}'.", ex.Message), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    if (args.Name.StartsWith("MyProject.View,"))
    {
        string path = Path.GetFullPath("C:\view.dll"));
        return Assembly.LoadFrom(path);
    }
    return null;
}

Now I want them to load in an own AppDomain:

现在我希望它们加载到自己的 AppDomain 中:

try
{
    string path = Path.GetFullPath("C:\library.dll");
    AppDomain domain = AppDomain.CreateDomain("TempDomain");
    domain.AssemblyResolve += CurrentDomain_AssemblyResolve;  // 1. Exception here!!
    domain.ExecuteAssembly(path);  // 2. Exception here!!
    domain.CreateInstanceFrom(...);  // 3. I have NO clue, how the type is named.
    domain.Load(...);  // 4. I have NO clue, how the assembly is named.
    domain.DoCallBack(...); // 5. Exception here!!
    // ...
}
catch (Exception ex)
{
    MessageBox.Show(String.Format("Damn '{0}'.", ex.Message), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

As you can see, I have put in 5 cases.

如您所见,我已经放入了 5 个案例。

  1. If I set the event handler, I get an exception that the assembly (it's an management console (mmc.exe) SnapIn. could not be found/loaded.

  2. ExecuteAssembly does not find an entry point (well, there is none).

  3. I have no clue how the type is named. How to load by interface?

  4. Similar to 3. How to get the name of an assembly?

  5. Same error as in 1.

  1. 如果我设置事件处理程序,我会收到一个异常,即无法找到/加载程序集(它是管理控制台 (mmc.exe) 管理单元)。

  2. ExecuteAssembly 没有找到入口点(好吧,没有)。

  3. 我不知道类型是如何命名的。如何通过接口加载?

  4. 类似于 3. 如何获取程序集的名称?

  5. 与 1 中的错误相同。

I think the problem could be the managment console somehow or I have just no clue what I'm doing wrong. Any help is appreciated.

我认为问题可能出在管理控制台上,或者我不知道我做错了什么。任何帮助表示赞赏。

UPDATE 1

更新 1

I have now tried using the posted proxy-solution.

我现在尝试使用发布的代理解决方案。

AppDomain domain = AppDomain.CreateDomain("TempDomain");
InstanceProxy proxy = domain.CreateInstanceAndUnwrap(Assembly.GetAssembly(
    typeof(InstanceProxy)).FullName, typeof(InstanceProxy).ToString()) as InstanceProxy;
if (proxy != null)
{
    proxy.LoadAssembly(path);
}
AppDomain.Unload(domain);

public class InstanceProxy : MarshalByRefObject
{
    public void LoadAssembly(string path)
    {
        AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
        Assembly asm = Assembly.LoadFrom(path);
        Type[] types = asm.GetExportedTypes();
        // ...see above...
    }
}

This does not work either. When trying to create the proxy object, I get an exception:

这也不起作用。尝试创建代理对象时,出现异常:

Could not load file or assembly 'MyProject.SnapIn, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
无法加载文件或程序集“MyProject.SnapIn,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null”或其依赖项之一。该系统找不到指定的文件。

The file in the error message is the on loaded into mmc (the SnapIn). Any idea how to fix this error? AppDomain.AssemblyResolve is not called (neither in the old or new domain).

错误消息中的文件是加载到 mmc(管理单元)中的文件。知道如何解决这个错误吗?AppDomain.AssemblyResolve 不被调用(无论是在旧域还是新域中)。

UPDATE 2

更新 2

I have now tried the solution with the AppDomainSetup. Now, the exception has changed to:

我现在已经尝试了 AppDomainSetup 的解决方案。现在,异常已更改为:

Could not load file or assembly 'file:///C:/Development/MyProject/bin/SnapIn/MyProject.SnapIn.DLL' or one of its dependencies. The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)
无法加载文件或程序集“file:///C:/Development/MyProject/bin/SnapIn/MyProject.SnapIn.DLL”或其依赖项之一。给定的程序集名称或代码库无效。(来自 HRESULT 的异常:0x80131047)

Any idea?

任何的想法?

采纳答案by BFree

Try this:

尝试这个:

namespace SeperateAppDomainTest
{
    class Program
    {
        static void Main(string[] args)
        {
            LoadAssembly();
        }

        public static void LoadAssembly()
        {
            string pathToDll = Assembly.GetExecutingAssembly().CodeBase;
            AppDomainSetup domainSetup = new AppDomainSetup { PrivateBinPath = pathToDll };
            var newDomain = AppDomain.CreateDomain("FooBar", null, domainSetup);
            ProxyClass c = (ProxyClass)(newDomain.CreateInstanceFromAndUnwrap(pathToDll, typeof(ProxyClass).FullName));
            Console.WriteLine(c == null);

            Console.ReadKey(true);
        }
    }

    public class ProxyClass : MarshalByRefObject { }

回答by Rubens Farias

Take a look into this previous answer: How to load an assembly into different AppDomain on Windows Mobile (.NET CF) ?. That answer creates a proxy class which runs into new AppDomain context so, there, you can have full control of your initialization.

看看之前的答案:如何将程序集加载到 Windows Mobile (.NET CF) 上的不同 AppDomain 中?. 该答案创建了一个代理类,它运行到新的 AppDomain 上下文中,因此,您可以完全控制您的初始化。

You could to create a Start()method into ServiceApplicationProxyclass and just call it normally from your hoster with a proxy.Start().

您可以在类中创建一个Start()方法ServiceApplicationProxy,然后使用proxy.Start().

回答by Khavasi

https://msdn.microsoft.com/en-us/library/3c4f1xde%28v=vs.110%29.aspx

https://msdn.microsoft.com/en-us/library/3c4f1xde%28v=vs.110%29.aspx

specifies that

指明

typeName Type: System.String

The fully qualified name of the requested type, including the namespace but not the assembly, as returned by the Type.FullName

property.

typeName 类型:System.String

The fully qualified name of the requested type, including the namespace but not the assembly, as returned by the Type.FullName

财产。

So try calling with Fully qualified name, instead of using typeof(InstanceProxy).ToString()use string/text "<<Namespace>>.InstanceProxy"

所以尝试使用完全限定名称调用,而不是 typeof(InstanceProxy).ToString()使用字符串/文本"<<Namespace>>.InstanceProxy"

as below

如下

InstanceProxy proxy = domain.CreateInstanceAndUnwrap(path, "<<Namespace>>.InstanceProxy") as InstanceProxy;