C# 调用Assembly.Load(byte())时激发AssemblyResolve事件

C# 调用Assembly.Load(byte())时激发AssemblyResolve事件,c#,.net,C#,.net,所以我有一个WPF项目,它正在引入DLL,这些DLL被我工作中的另一个项目使用。这是一堆依赖项,我一直在使用这种技术:将依赖项嵌入到单个可执行文件中 现在,当我在一个依赖项中调用特定方法时,我点击AssemblyResolve事件。我的OnResolveAssembly事件运行时,它将程序集作为嵌入式资源查找(酷!),并执行“返回assembly.Load(assembyRawBytes)”。如果我在这一点上点击F11(在OnResolveAssembly的开始处有一个断点),我将得到对同一事件

所以我有一个WPF项目,它正在引入DLL,这些DLL被我工作中的另一个项目使用。这是一堆依赖项,我一直在使用这种技术:将依赖项嵌入到单个可执行文件中

现在,当我在一个依赖项中调用特定方法时,我点击AssemblyResolve事件。我的OnResolveAssembly事件运行时,它将程序集作为嵌入式资源查找(酷!),并执行“返回assembly.Load(assembyRawBytes)”。如果我在这一点上点击F11(在OnResolveAssembly的开始处有一个断点),我将得到对同一事件的另一个调用。它也适用于相同的程序集(args.Name相同)

如果我让它运行,我会遇到堆栈溢出,因为我似乎永远无法逃避这个递归事件调用

MSDN文档并没有真正说明Assembly.Load何时会失败,除非出现FileNotFoundException或BadImageFormatException

在调用Assembly.Load之前,我已经尝试过将OnResolveAssembly解钩,但随后我的应用程序神秘地死亡,即使在VS下,它也会失败

我可能在这里违反了几条规则,但是对于从哪里开始寻找问题的一些想法是受欢迎的

我将开始在有问题的DLL中查找,看看是否有关于它的错误的提示(可能是一个混合程序集?)

以下是我的OnResolveAssembly处理程序:

private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
    Assembly executingAssembly = Assembly.GetExecutingAssembly();
    AssemblyName assemblyName = new AssemblyName(args.Name);

    string path = assemblyName.Name + ".dll";

    if (assemblyName.CultureInfo.Equals(System.Globalization.CultureInfo.InvariantCulture) == false)
    {
        path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);
    }
    using (Stream stream = executingAssembly.GetManifestResourceStream(path))
    {
        if (stream == null)
            return null;

        byte[] assemblyRawBytes = new byte[stream.Length];
        stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
        assemblyDictionary.Add(assemblyName.Name, Assembly.Load(assemblyRawBytes));
        return assemblyDictionary[assemblyName.Name];
    }
}
目前,我已通过迭代所有资源并尝试Assembly.Load并将其存储在字典中以供检索(在OnResolveAssembly事件期间)来解决此问题:


它现在似乎工作得很好(在我的第二个代码段中,“失败”的程序集将加载得很好),但我有兴趣了解为什么它在第一个代码段中不工作。

从byte[]加载程序集是一个很好的方法,最终会进入.dll地狱(你会遇到太多/复杂的依赖项)。这里的问题是,尽管您将dll加载到AppDomain,但当您再次需要依赖类型的dll时,它不会自动解决。 我在这里评论了这个问题:

长话短说,程序集被加载到AppDomain内部的不同“上下文”中。Load(字节[])使用的上下文不会自动解析程序集

解决方案是跟踪加载的程序集,并返回已加载的程序集,而不是再次加载。在我对以下问题的回答中,这种方法有一个起点:

但我认为你的解决方法是正确的

顺便说一句,加载程序集两次是获得相同但不兼容类型的一种方法。是否曾将MyAssembly中的MyType对象强制转换为同一程序集中的MyType对象并得到null


这是一个热烈的“欢迎使用.dll地狱”。

添加了我的代码和我发现的一个解决方法。@JonathanYee你真的从调用
Assembly.Load(byte[])中得到
AssemblyResolve
了吗?这正是我正在寻找的情况,但我无法构建这样一个示例。你能提供更多的细节吗?
[STAThread]
public static void Main()
{
    AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
    Assembly executingAssembly = Assembly.GetExecutingAssembly();
    string[] resources = executingAssembly.GetManifestResourceNames();
    foreach (string resource in resources)
    {
        if (resource.EndsWith(".dll"))
        {
            using (Stream stream = executingAssembly.GetManifestResourceStream(resource))
            {
                if (stream == null)
                    continue;

                byte[] assemblyRawBytes = new byte[stream.Length];
                stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
                try
                {
                    assemblyDictionary.Add(resource, Assembly.Load(assemblyRawBytes));
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.Print("Failed to load: " + resource + " Exception: " + ex.Message);
                }
            }
        }
    }
    App.Main();
}

private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
    Assembly executingAssembly = Assembly.GetExecutingAssembly();
    AssemblyName assemblyName = new AssemblyName(args.Name);

    string path = assemblyName.Name + ".dll";

    if (assemblyDictionary.ContainsKey(path))
    {
        return assemblyDictionary[path];
    }
    return null;
}