Proper way to resolving assemblies from subfolders

后端 未结 2 841
难免孤独
难免孤独 2020-12-20 02:10

Here is how my application folders looks like:

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


        
相关标签:
2条回答
  • 2020-12-20 03:04

    I wrote this method to resolve assemblies. It is tweaked to fit my needs.

    It basically hooks a AssemblyResolve event to the current application domain to retrieve an requested assembly from a list of directories.

    There is no easy way to find where the assembly file that match the namespace to resolve, except by loading an assembly file and check to which namespace it belongs to.

    Plus, it discards some unwanted assemblies (like serializers, resources...) and detects dlls or exes that are not .NET assemblies.

    A better approach would consist in using the Global Assembly Cache, but we want our plugins to be fully moveable. So here it is.

    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");
                };
        }
    }
    

    Here is how it works:

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

    Everytime some assemblies needs to be resolved, it will get the one that is loaded in memory, otherwise it will search in any given folders.

    0 讨论(0)
  • 2020-12-20 03:09

    You can use "AssemblyResolve Event" (Method 3): https://support.microsoft.com/en-us/kb/837908

    0 讨论(0)
提交回复
热议问题