调用Assembly.Load(byte())时会触发AssemblyResolve事件

所以我有一个WPF项目正在拉动我的工作中另一个项目使用的dll。 这是一堆乱的依赖,我一直在使用这里的技术: http : //www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application将依赖项嵌入到单个可执行文件中。

现在,当我在其中一个依赖项中调用特定方法时,我点击了AssemblyResolve事件。 我的OnResolveAssembly事件运行,它将程序集作为嵌入式资源(很酷!),然后“返回Assembly.Load(assembyRawBytes)”。 如果我此时点击F11(在OnResolveAssembly的开头有一个断点),我会再次调用同一个事件。 这也是同一个程序集(args.Name是相同的)。

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

除了FileNotFoundException或BadImageFormatException之外,MSDN文档并没有真正说明Assembly.Load何时会失败。

在我调用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事件期间)来解决它:

 [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; } 

它现在看起来工作正常(“失败”程序集将在我的第二个片段中正常加载),但我有兴趣了解它为什么在第一个不起作用。

从byte []加载程序集是一个很好的方法来结束.dll地狱(你去的地方太多/复杂的依赖)。 这里的问题是,虽然您将dll加载到AppDomain,但是当您再次需要依赖类型时,它不会自动解析。 我在这里评论了这个问题: AssemblyResolve不会触发

简而言之,Assemblies被加载到AppDomains内部的不同“上下文”中。 Load(byte [])使用的上下文不会自动解析程序集。

解决方案是跟踪加载的组件并返回已加载的组件,而不是第二次加载它。 在我的回答中有一个这种方法的出发点: 当DisallowApplicationBaseProbing = true时需要连接AssemblyResolve事件

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

BTW。 加载程序集两次是获得相同但不兼容类型的方法。 曾经从MyAssembly将MyType的对象从同一个程序集转换为MyType并获得null吗?

那是一个温暖的“欢迎来到.dll地狱”。