"Could not load file or assembly" 当引用的 dll 文件在加载的程序集中时

"Could not load file or assembly" when the referenced dll file is in the loaded assemblies

我的方案结构如下:

我有 2 个项目。在编译时相互独立(无交叉引用)。第二个是 class 库,在运行时从第一个库加载。

那个库有它自己的 dll 依赖项,我想这就是我尝试使用这些 dll 执行代码时引发异常的原因。我认为这些引用的 dll 可能不会在运行时加载,除了 class 库 dll。因此,我在执行加载程序集的任何代码之前添加了一些代码来加载引用的程序集。

令我惊讶的是,当执行以下代码时,“有问题的”dll 已经包含在加载的程序集中,其位置是正确的,但错误仍然存​​在,我不知道如何进一步解决问题。

非常感谢任何帮助。

try
{
    Assembly a = Assembly.LoadFile(Path.GetFullPath(filename));

    //Try to find the type the derived plugin class
    foreach (Type t in a.GetTypes())
    {
        if (t.IsSubclassOf(typeof(PluginBase)))
        {
            Console.WriteLine("Plugin class detected! {0}", t.Name);


            //Get Referenced Assemblies
            AssemblyName[] l = a.GetReferencedAssemblies()                                     
            //Get Loaded Assemblies
            var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();

            foreach (AssemblyName a2 in l)
            {
                var asm = loadedAssemblies.FirstOrDefault(a => a.FullName == a2.FullName);

                if (asm == null)
                {
                    Assembly test = null;
                    try
                    {
                        //First try to load using the assembly name just in case its a system dll    
                        test = Assembly.Load(a2);
                        //Try to load the assemly from the plugin directory
                        if (test == null)
                        {
                            test = Assembly.LoadFrom(Path.Join(a.Location, a2.Name + ".dll"));
                        }
                    }
                    catch
                    {
                        Callbacks.Log($"Unable to load assembly {a2.Name}", LogVerbosityLevel.WARNING);

                    }

                    if (test != null)
                    {
                        Callbacks.Log($"Loaded Assembly {a2.Name}", LogVerbosityLevel.WARNING);
                        AppDomain.CurrentDomain.Load(test.GetName());
                    }

                }
            }


            object c = Activator.CreateInstance(t, new object[] { this });
            Plugins[Path.GetFileName(filename)] = c as PluginBase;
            //Call Dll initializers
            t.GetMethod("OnLoad").Invoke(c, new object[] { });
            break;
        }
    }
}
catch (Exception ex)
{
    Log("Error during loading of plugin " + filename, LogVerbosityLevel.INFO);
    Log("Exception type " + ex.Data, LogVerbosityLevel.INFO);
}

好吧,我想我明白了。因此,在我看到引用的程序集被加载之前,是因为我通过浏览包含 class 库及其引用的 dll 文件夹来迭代加载它们。主要问题是我太笨了,忘了把它们加载到调用AppDomain.CurrentDomain.Load(assemblyname)的AppDomain中。但是,当我尝试修复时,我意识到尝试只获取相关的 AssemblyName 对象是行不通的。我得到相同的 FileNotFoundException。

解决问题的方法是使用 Assembly.LoadFrom 而不是 Assembly.LoadFile。我通读了文档,它指出 LoadFile 以不同方式对待加载的程序集,无论它们是否完全相同的 dll 文件只是位于不同的位置。在我的例子中,只有一条路径是我尝试从中加载程序集的,但我想 LoadFile 在这种情况下也可以区分加载的程序集。但是,我仍然不确定为什么尝试使用来自 LoadFile 的 AssemblyName 与来自 LoadFrom 的相比会崩溃。我希望它们是相同的...

我还添加了一个故障安全机制来尝试只加载所需的 dll 文件。我只是希望所有将要加载的 dll 都带有一个文本标识符。因此,首先加载所需的 dll,并在调用任何代码之前,使用 AssemblyName 对象或实际路径加载其引用(如果第一次失败)。

到目前为止,一切似乎都运行很好,希望这能解决问题。

foreach (string filename in Directory.GetFiles("Plugins"))
    {
        if (!filename.EndsWith(("dll")))
            continue;
    
        if (!Path.GetFileName(filename).StartsWith(("Test")))
            continue;
    
        var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
        //Load Assembly
        try
        {
            Assembly a = Assembly.LoadFrom(Path.GetFullPath(filename));
            AppDomain.CurrentDomain.Load(a.GetName());
    
            //Try to find the type the derived plugin class
            foreach (Type t in a.GetTypes())
            {
                if (t.IsSubclassOf(typeof(PluginBase)))
                {
                    Console.WriteLine("Plugin class detected! {0}", t.Name);
    
                    //Load Referenced Assemblies
                    AssemblyName[] l = a.GetReferencedAssemblies();
                    loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
    
                    foreach (AssemblyName a2 in l)
                    {
                        var asm = loadedAssemblies.FirstOrDefault(a => a.FullName == a2.FullName);
    
                        if (asm == null)
                        {
                            Assembly test = null;
                            try
                            {
                                //First try to load using the assembly name just in case its a system dll    
                                test = Assembly.Load(a2);
                            } 
                            catch (FileNotFoundException ex)
                            {
                                try
                                {
                                    Callbacks.Log($"Unable to load assembly {a2.Name}, Looking in plugin directory...", LogVerbosityLevel.WARNING);
                                    test = Assembly.LoadFrom(Path.Join(Path.GetDirectoryName(a.Location), a2.Name + ".dll"));
                                } catch (Exception ex2)
                                {
                                    Callbacks.Log($"Unable to load assembly {a2.Name}, Error: {ex2.Message}", LogVerbosityLevel.WARNING);
                                }
                            }
    
                            if (test != null)
                            {
                                Callbacks.Log($"Loaded Assembly {a2.Name}", LogVerbosityLevel.WARNING);
                                AppDomain.CurrentDomain.Load(test.GetName());
                            }
    
                        }
                    }
    
    
                    object c = Activator.CreateInstance(t, new object[] { this });
                    Plugins[Path.GetFileName(filename)] = c as PluginBase;
                    //Call Dll initializers
                    t.GetMethod("OnLoad").Invoke(c, new object[] { });
                    break;
                }
            }
        }
        catch (Exception ex)
        {
            Log("Error during loading of plugin " + filename, LogVerbosityLevel.INFO);
            Log("Exception type " + ex.Data, LogVerbosityLevel.INFO);
        }
    
    }