【问题标题】:Loading/Unloading assembly in different AppDomain在不同的 AppDomain 中加载/卸载程序集
【发布时间】:2011-01-09 02:41:58
【问题描述】:

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

要加载的程序集是我的插件框架中的插件。他们根本没有切入点。我所知道的是它们包含一些实现给定接口的类型。旧的非 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;
}

现在我希望它们加载到自己的 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);
}

如你所见,我已经放入了 5 个箱子。

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

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

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

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

  5. 与 1 中的错误相同。

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

更新 1

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

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...
    }
}

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

无法加载文件或程序集“MyProject.SnapIn,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null”或其依赖项之一。系统找不到指定的文件。

错误消息中的文件是加载到 mmc(管理单元)中的文件。知道如何解决此错误吗? AppDomain.AssemblyResolve 未被调用(在旧域或新域中均未调用)。

更新 2

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

无法加载文件或程序集“file:///C:/Development/MyProject/bin/SnapIn/MyProject.SnapIn.DLL”或其依赖项之一。给定的程序集名称或代码库无效。 (HRESULT 异常:0x80131047)

有什么想法吗?

【问题讨论】:

  • 您是否尝试过创建一个继承自 MarshallByRef 的类来充当代理,并让它在新的应用程序域上下文中完成那些肮脏的工作?
  • 这个类是在要加载的程序集中还是在已经加载的程序集中(试图加载其他程序集的程序集)?
  • 不要使用 CreateInstanceAndUnwrap,使用 CreateInstanceFromAndUnwrap。
  • 啊,成功了。非常感谢。
  • 嗨,这是从架构的角度来看的。您能否详细说明您使用这种创建新应用程序域然后在调用几个成员后卸载的方法的场景?谢谢!

标签: c# reflection appdomain mmc


【解决方案1】:

查看之前的答案:How to load an assembly into different AppDomain on Windows Mobile (.NET CF) ?。该答案创建了一个代理类,该类运行到新的 AppDomain 上下文中,因此您可以在那里完全控制您的初始化。

您可以在ServiceApplicationProxy 类中创建一个Start() 方法,然后使用proxy.Start() 从您的主机正常调用它。

【讨论】:

  • 感谢您的回答。我已经尝试过了,但我仍然遇到错误。在原始问题中查看我的更新。
【解决方案2】:

试试这个:

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 { }

【讨论】:

    【解决方案3】:

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

    指定

    类型名称 类型:System.String

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

    财产。

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

    如下

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

    【讨论】:

    • typeof(InstanceProxy).FullName 会不会比硬编码命名空间 + 类型名更好?
    猜你喜欢
    • 2012-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-12
    • 1970-01-01
    • 1970-01-01
    • 2021-09-11
    • 2010-09-12
    相关资源
    最近更新 更多