本文介绍了C#将DLL字节数组加载到其他AppDomain中会引发System.IO.FileNotFoundException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将已复制到字节数组中的DLL文件加载到新的AppDomain中.

I am trying to load a DLL file that has been copied into a byte array into a new AppDomain.

DLL确实包含对诸如Windows.Forms和其他dll之类的东西的引用.那些没有加载吗?如果是这样,您如何为该特定域预加载它们?

The DLL does contain references to things like Windows.Forms and other dlls. Are those the ones failing to load? If so how do you pre-load them for that specific domain?

AppDomainSetup Setup = new AppDomainSetup();
Setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
Setup.ApplicationName = "Plugin_" + DLLName + "" + PluginManager.PluginList.Count;
AppDomain Domain = AppDomain.CreateDomain("Domain_" + DLLName + "" + PluginManager.PluginList.Count, null, Setup);
Assembly Assembly = Domain.Load(buffer);

但是改变

Assembly Assembly = Domain.Load(buffer);

Assembly = AppDomain.CurrentDomain.Load(buffer);

使其正常工作.

我需要将它放在单独的域中,因为我计划卸载此AppDomain来卸载DLL本身.

I need it to be in a separate domain because I plan on unloading this AppDomain to unload the DLL itself.

我尝试过像每个人都在暗示的"AssemblyResolve"事件,但是它没有任何作用.

I tried playing around with the "AssemblyResolve" event like everyone out there is suggesting but it doesn't do anything.

我之所以需要它来自字节数组,是因为我希望能够在运行时切换DLL文件并将其重新加载到内存中.

Also the reason I need this to be from a byte array is because I want to be able to switch the DLL file at run-time and re-load it into memory.

DLL文件与.exe文件位于单独的文件夹中.它在同一目录中,只是其中一个文件夹.

The DLL files are in a separate folder from the .exe file. It's in the same directory just one folder inside.

有趣的发现:

如果我将DLL文件添加到.exe的文件位置,它将加载这些文件并将其锁定并成功加载到新域中.为什么当我给它提供一个字节数组而不是文件位置时,它以这种方式工作?我实际上是否必须采用字节数组并写入一个临时文件?当我用完它们后,我可以这样做并删除它们,但这似乎是在浪费时间,没有理由不能从内存中全部删除.

If I add the DLL files to the file location of the .exe it will load those and lock onto them and load successfully into the new domain. Why does it work this way when I feed it a byte array and not the file location? Do I actually have to take the byte array and write to a temporary file? I can do that and delete them when I'm done with them but it seems like a waste of time, there's no reason it can't do it all from memory.

推荐答案

解决方案:

无论我在哪里看,AppDomain的文档记录都不多,解释也很差.就像人们正在试图隐藏它,并使其成为公众大众的秘密一样.显然AppDomain不会像变量和其他对象引用那样在彼此之间共享数据.您需要在它们之间设置SetData/GetData和DoCallBack.有人模糊地提到了这一点,但是没有人给出真正的解决方案.

AppDomains are poorly documented and poorly explained no matter where I look. It's like people are trying to hide it and keep it a secret from the public mass. Apparently AppDomains do not share data between each other like variables and other object references. You need to SetData/GetData and DoCallBack between them. This was vaguely mentioned but no real solution was given by anyone.

因此,我做了一个简单的插件加载程序,使用"LoadFrom"而不将其加载到字节数组中,并且文件没有被锁定,它将其读入新的AppDomain到内存中并立即将文件解锁,但这在任何地方都没有提及并且已经是怪异的行为,因为在主AppDomain中,它像癌症一样锁定在文件上.

So I did this simple plugin loader, using "LoadFrom" without loading it into a byte array and the file does not get locked, it reads it into a new AppDomain into memory and unlocks the file immediately but this is not mentioned anywhere and is weird behavior already because in the main AppDomain it locks onto the file like a cancer.

[Serializable] //This is important
public class TPlugin
{
    public bool InitializeImmediately { get; set; }
    public AppDomainSetup AppSetup { get; set; }
    public Assembly Assembly { get; set; }
    public AppDomain AppDomain { get; set; }
    public string FilePath { get; set; }
    public object ClassInstance { get; set; }
    public Type ClassType { get; set; }

    public TPlugin(string path, bool Initialize = false)
    {
        FilePath = path;
        InitializeImmediately = Initialize;

        AppSetup = new AppDomainSetup();
        AppSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
        AppDomain = AppDomain.CreateDomain(FilePath, null, AppSetup);
        AppDomain.SetData("Plugin", this);
        AppDomain.DoCallBack(new CrossAppDomainDelegate(() =>
        {
            //We are now inside the new AppDomain, every other variable is now invalid since this AppDomain cannot see into the main one
            TPlugin plugin = AppDomain.CurrentDomain.GetData("Plugin") as TPlugin;
            if (plugin != null)
            {
                plugin.Assembly = Assembly.LoadFrom(plugin.FilePath);
                if(InitializeImmediately) //You cannot use the "Initialize" parameter here, it goes out of scope for this AppDomain
                {
                    plugin.ClassType = plugin.Assembly.GetExportedTypes()[0];
                    if (plugin.ClassType != null && plugin.ClassType.IsClass)
                    {
                        plugin.ClassInstance = Activator.CreateInstance(plugin.ClassType);
                        MethodInfo info = plugin.ClassType.GetMethod("Initializer");
                        info.Invoke(plugin.ClassInstance, null);
                    }
                }
            }
        }));
    }

    public object Execute(string FunctionName, params object[] args)
    {
        AppDomain.SetData("FunctionName", FunctionName);
        AppDomain.SetData("FunctionArguments", args);
        AppDomain.DoCallBack(CallBack);
        return AppDomain.GetData("FunctionReturn");
    }

    public void CallBack()
    {
        TPlugin plugin = AppDomain.CurrentDomain.GetData("Plugin") as TPlugin;

        if (plugin != null)
        {
            MethodInfo info = plugin.ClassType.GetMethod(AppDomain.CurrentDomain.GetData("FunctionName") as string);
            info.Invoke(plugin.ClassInstance, AppDomain.CurrentDomain.GetData("FunctionArgs") as object[]);
        }

        //This is how to return back since DoCallBack does not support returns.
        AppDomain.CurrentDomain.SetData("FunctionReturn", null);
    }
}

这是我的DLL模块:

public class GUIModule
{
    public bool Initializer()
    {
        Console.WriteLine("Initialized!");
        return true;
    }

    public bool Deinitializer()
    {
        Console.WriteLine("Deinitialized");
        return true;
    }
}

现在一切正常,甚至可以加载依赖项.编译时,GUIModule引用了Windows.Forms.

All works fine now, even loads the dependencies. GUIModule has a reference to Windows.Forms when compiled.

这篇关于C#将DLL字节数组加载到其他AppDomain中会引发System.IO.FileNotFoundException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-16 11:15