Динамическая загрузка DLL в отдельный домен приложения и выгрузка - PullRequest
2 голосов
/ 20 сентября 2019

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

Шаги:

  1. Создание нового домена приложения
  2. Загрузите мою DLL, которую я хочу, в домен приложения
  3. Вызовите метод и получите ответ
  4. Выгрузите домен приложения

Вот то, что я пробовал до сих пор

Это код в MyAssembly.dll

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace MyAssembly
{
    public class MyClass
    {
        public static string MyMethod()
        {
            return "Hello there, this is message from MyAssembly";
        }
    }
}

Вот как я загружаю файл DLL

using System.Diagnostic;
using System.IO;

private class ProxyClass : MarshalByRefObject
{
    public void LoadAssembly()
    {
        AppDomain dom;
        string domainName = "new:" + Guid.NewGuid();
        try
        {
            //Create the app domain
            dom = AppDomain.CreateDomain(domainName, null, new AppDomainSetup
                    {
                        PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"),
                        ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
                        ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
                        ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
                        ShadowCopyFiles = "true",
                        ShadowCopyDirectories = "true",
                        LoaderOptimization = LoaderOptimization.SingleDomain,
                    });

            string dllPath = @"C:\MyProject\MyAssembly.dll";//the path to my assembly file I want to load
            //load the assembly to the new app domain
            Assembly asm = dom.Load(File.ReadAllBytes(dllPath));//Error occurred at here

            Type baseClass = asm.GetType("MyAssembly.MyClass");
            MethodInfo targetMethod = baseClass.GetMethod("MyMethod");

            string result = targetMethod.Invoke(null, new object[]{});

            /*Do something to the result*/
        }
        catch(Exception ex)
        {
            Debug.WriteLine(ex.Message);
            Debug.WriteLine(ex.ToString());
        }
        finally
        {
            //Finally unload the app domain
            if (dom != null) AppDomain.Unload(dom);
        }
    }
}

public void BeginLoadDll()
    {
        ProxyClass proxy = new ProxyClass();
        proxy.LoadAssembly();

        //OR like this, which gave me same error message as well
        //var dom = AppDomain.CreateDomain("new:" + Guid.NewGuid(), null, new AppDomainSetup
        //    {
        //        PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"),
        //        ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
        //        ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
        //        ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
        //        ShadowCopyFiles = "true",
        //        ShadowCopyDirectories = "true",
        //        LoaderOptimization = LoaderOptimization.SingleDomain,
        //    });
        //ProxyClass proxy = (ProxyClass)dom.CreateInstanceAndUnwrap(
        //    typeof(ProxyClass).Assembly.FullName, typeof(ProxyClass).FullName);
        //pr.LoadAssembly(watcherData, filePath);
    }

Вот то, что я наблюдал до сих пор, яЯ не уверен, что это только у меня, или я что-то упустил

-Если «MyAssembly.dll» существует в папке проекта bin до запуска приложения, я могу загрузить файл DLL

-Если «MyAssembly.dll» не существовало в папке bin проекта до запуска приложения, вместо этого он был загружен в другое место, кроме папки bin проекта, я не могу загрузить файл dll.Например, папка bin проекта - «C: \ Main \ MyMainProject \ MyMainProject \ bin», а DLL загружается из C: \ MyProject \ MyAssembly.dll »

-Если я переместил« MyAssembly.dll »"файл в папку bin (используя File.Copy() или File.Move()), он каким-то образом останавливает выполнение остального кода.

Полученное сообщение об ошибке

Could not load file or assembly 'MyAssembly, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=2c20c56a5e1f4bd4' or one of its dependencies.
The system cannot find the file specified.

EDIT

Я знаю, что могу использовать Assembly.LoadFrom(@"PATH\TO\MY\DLL"), но проблема с этим в том, что я не могу выгрузить DLL

1 Ответ

1 голос
/ 23 сентября 2019

После нескольких дней исследований у меня наконец-то все заработало.Ниже мой окончательный рабочий код.

Полезные справочные ссылки, которые помогли мне достичь этого

https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.createinstanceandunwrap?view=netframework-4.8#System_AppDomain_CreateInstanceAndUnwrap_System_String_System_String_

C # отражение - загрузить сборку и вызвать метод, если онСуществует

Использование AppDomain в C # для динамической загрузки и выгрузки DLL

Код в MyAssembly.dll такой же, как в вопросе.Я также понял, что могу также возвращать тип объекта.

Как загрузить файл DLL в отдельный домен приложения и выгрузить домен приложения

public void MethodThatLoadDll()
{
    AppDomain dom = null;
    //declare this outside the try-catch block, so we can unload it in finally block

    try
    {
        string domName = "new:" + Guid.NewGuid();
        //assume that the domName is "new:50536e71-51ad-4bad-9bf8-67c54382bb46"

        //create the new domain here instead of in the proxy class
        dom = AppDomain.CreateDomain(, null, new AppDomainSetup
                    {
                        PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"),
                        ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
                        ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
                        ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
                        ShadowCopyFiles = "true",
                        ShadowCopyDirectories = "true",/*yes they are string value*/
                        LoaderOptimization = LoaderOptimization.SingleDomain,
                        DisallowBindingRedirects = false,
                        DisallowCodeDownload = true,
                    });
        ProxyClass proxy = (ProxyClass)dom.CreateInstanceAndUnwrap(
                    typeof(ProxyClass).Assembly.FullName, typeof(ProxyClass).FullName);
        string result = proxy.ExecuteAssembly("MyParam");
        /*Do whatever to the result*/
    }
    catch(Exception ex)
    {
        //handle the error here
    }
    finally
    {
        //finally unload the app domain
        if(dom != null) AppDomain.Unload(dom);
    }

}

Мой класс, который наследует MarshalByRefObject

private class ProxyClass : MarshalByRefObject
{
    //you may specified any parameter you want, if you get `xxx is not marked as serializable` error, see explanation below
    public string ExecuteAssembly(string param1)
    {
        /*
         * All the code executed here is under the new app domain that we just created above
         * We also have different session state here, so if you want data from main domain's session, you should pass it as a parameter
         */
        //load your DLL file here
        Debug.WriteLine(AppDomain.CurrentDomain.FriendlyName);
        //will print "new:50536e71-51ad-4bad-9bf8-67c54382bb46" which is the name that we just gave to the new created app domain

        Assembly asm = Assembly.LoadFrom(@"PATH/TO/THE/DLL");

        Type baseClass = asm.GetType("MyAssembly.MyClass");
        MethodInfo targetMethod = baseClass.GetMethod("MyMethod");

        string result = targetMethod.Invoke(null, new object[]{});

        return result;
    }
}

Распространенная ошибка, с которой вы можете столкнуться

'xxx' is not marked as serializable

Это может произойти, если вы попытаетесь передать пользовательский класс в качестве параметра, например,

public void ExecuteAssembly(MyClass param1)

В этом случае введите [Serializable] в MyClass, например

[Serializable]
public class MyClass { }
...