从子文件夹解析程序集的正确方法

本文关键字:方法 程序集 文件夹 | 更新日期: 2023-09-27 18:24:48

以下是我的应用程序文件夹的样子:

Application:
+ App.exe
+ App.exe.config
Application/Plugins:
+ Plugin1 (folder)
Application/Plugins/Plugin1:
+ Plugin1.dll
+ SomeDll.dll

因此,主应用程序App.exe正在查找插件文件夹,并将{PluginName}.dll加载到内存中以运行它。这个插件通常使用它自己的依赖程序集,这些程序集必须加载(如SomeDll.dll)。看起来它有时会遇到严重的问题。我收到一个异常,例如找不到依赖程序集的依赖程序集,我不知道为什么。

例如,由于插件运行OwinSelfHost服务,我的插件必须加载大量额外的dll。

因此它必须加载例如:

System.Web.Http.Owin
Owin
Microsoft.Owin
Microsoft.Owin.Host.HttpListener
Microsoft.Owin.Hosting

当加载Microsoft.Owin.Hosting时,抛出无法加载Microsoft.Owin的异常

异常看起来像:

Could not load file or assembly 'Microsoft.Owin, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of it's dependencies. File not found.

从子文件夹解析程序集的正确方法

我编写这个方法来解析程序集。它经过了调整以适应我的需要。

它基本上将AssemblyResolve事件挂接到当前应用程序域,以从目录列表中检索请求的程序集。

除了加载程序集文件并检查它属于哪个命名空间之外,没有简单的方法可以找到与要解析的命名空间匹配的程序集文件

此外,它还会丢弃一些不需要的程序集(如序列化程序、资源…),并检测不是.NET程序集的dll或exe。

更好的方法是使用全局程序集缓存,但我们希望我们的插件是完全可移动的。就是这样。

public static class AssemblyResolver
{
    internal static void Hook(params string[] folders)
    {
        AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
            {
                // Check if the requested assembly is part of the loaded assemblies
                var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
                if (loadedAssembly != null)
                    return loadedAssembly;
                // This resolver is called when an loaded control tries to load a generated XmlSerializer - We need to discard it.
                // http://connect.microsoft.com/VisualStudio/feedback/details/88566/bindingfailure-an-assembly-failed-to-load-while-using-xmlserialization
                var n = new AssemblyName(args.Name);
                if (n.Name.EndsWith(".xmlserializers", StringComparison.OrdinalIgnoreCase))
                    return null;
                // http://stackoverflow.com/questions/4368201/appdomain-currentdomain-assemblyresolve-asking-for-a-appname-resources-assembl
                if (n.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase))
                    return null;
                string assy = null;
                // Find the corresponding assembly file
                foreach (var dir in folders)
                {
                    assy = new[] { "*.dll", "*.exe" }.SelectMany(g => Directory.EnumerateFiles(dir, g)).FirstOrDefault(f =>
                               {
                                   try { return n.Name.Equals(AssemblyName.GetAssemblyName(f).Name, StringComparison.OrdinalIgnoreCase); }
                                   catch (BadImageFormatException) { return false; /* Bypass assembly is not a .net exe */ }
                                   catch (Exception ex) { throw new ApplicationException("Error loading assembly " + f, ex); }
                               });
                    if (assy != null)
                        return Assembly.LoadFrom(assy);
                }
                throw new ApplicationException("Assembly " + args.Name + " not found");
            };
    }
}

以下是它的工作原理:

AssemblyResolver.Hook("'Plugins", "'CommonReferences");

每次需要解析某些程序集时,它都会得到加载在内存中的程序集,否则它会在任何给定的文件夹中搜索。

您可以使用"AssemblyResolve Event"(方法3):https://support.microsoft.com/en-us/kb/837908