C # Загрузка байтового массива DLL в другой домен приложения выдает исключение System.IO.FileNotFoundException - PullRequest
0 голосов
/ 09 июня 2018

Я пытаюсь загрузить файл DLL, который был скопирован в байтовый массив, в новый домен приложений.

DLL содержит ссылки на такие вещи, как Windows.Forms и другие библиотеки DLL.Это те, которые не загружаются?Если да, то как вы предварительно загрузите их для этого конкретного домена?

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

Заставляет его работать.

Мне нужно, чтобы он находился в отдельном домене, потому что я планирую выгрузить этот домен приложений, чтобы выгрузить саму DLL.

Я попытался поиграть с событием "AssemblyResolve", как и все остальные, но оно предлагаетничего не делает.

Также причина, по которой мне нужно, чтобы это было из байтового массива, заключается в том, что я хочу иметь возможность переключать файл DLL во время выполнения и повторно загружать его в память.

Файлы DLL находятся в отдельной папке из файла .exe.Он находится в той же директории, только в одной папке.

Интересное открытие:

Если я добавлю файлы DLL в расположение файла .exe, он загрузит их изаблокируйте их и успешно загрузите в новый домен.Почему это работает, когда я передаю ему байтовый массив, а не местоположение файла?Должен ли я на самом деле взять байтовый массив и записать во временный файл?Я могу сделать это и удалить их, когда я закончу с ними, но это кажется пустой тратой времени, нет причины, по которой он не может сделать все это из памяти.

1 Ответ

0 голосов
/ 11 июня 2018

РЕШЕНИЕ:

Домены приложений плохо документированы и плохо объяснены, где бы я ни смотрел.Как будто люди пытаются скрыть это и держать это в тайне от публики.Очевидно, домены приложений не делят данные друг с другом, как переменные и другие ссылки на объекты.Вам необходимо установить SetData / GetData и DoCallBack между ними.Это было смутно упомянуто, но никто не дал реального решения.

Так что я сделал этот простой загрузчик плагинов, используя «LoadFrom», не загружая его в байтовый массив, и файл не блокируется, он читает его вновый AppDomain в память и немедленно разблокирует файл, но это нигде не упоминается, и это уже странное поведение, потому что в основном AppDomain он блокируется на файл как рак.

[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 при компиляции.

...