Загружать и выгружать DLL динамически в мой проект с помощью AppDomain - PullRequest
0 голосов
/ 29 мая 2018

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

string dllPath = @"the path of my dll";
var DLL = Assembly.LoadFile(dllPath);
foreach (Type type in DLL.GetExportedTypes())
{
      if (type.Name == "targetClassName")
      {
          var c = Activator.CreateInstance(type);
          try
          {
              type.InvokeMember("myMethod", BindingFlags.InvokeMethod, null, c, new object[] { "Params" });
          }
          catch(Exception ex)
          {
             MessageBox.Show(ex.Message);
          }
          break;
      }
}

Однако теперь моя проблема заключается в том, что я хочу выгрузить dll, чего я не могу сделать, потому что в Assembly нет метода выгрузки,Решение, которое я нашел, состоит в том, что я должен использовать AppDomain, чтобы загрузить сборку и затем выгрузить ее.

Теперь вот моя главная проблема .Я продолжаю получать FileNotFoundException.Вот мой код:

public class ProxyDomain : MarshalByRefObject
{
    public Assembly GetAssembly(string assemblyPath)
    {
        try
        {
            return Assembly.LoadFrom(assemblyPath);
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException(ex.Message);
        }
    }
}

private void BuildButton_Click(object sender, EventArgs e)
{
    string dllPath = @"DllPath";
    string dir = @"directory Path of the dll";
    AppDomainSetup domaininfo = new AppDomainSetup();
    domaininfo.ApplicationBase = System.Environment.CurrentDirectory;
    Evidence adevidence = AppDomain.CurrentDomain.Evidence;
    AppDomain domain = AppDomain.CreateDomain("MyDomain", adevidence, domaininfo);

    Type Domtype = typeof(ProxyDomain);
    var value = (ProxyDomain)domain.CreateInstanceAndUnwrap(
         Domtype.Assembly.FullName,
         Domtype.FullName);

    var DLL = value.GetAssembly(dllPath);

    // Then use the DLL object as before
}

Последняя строка делает следующее исключение Could not load file or assembly 'dll, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

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

РЕДАКТИРОВАТЬ

Когда я копирую предполагаемую dll в ту же папку bin моего проекта, это работает.Тем не менее, я не хочу копировать DLL в моем проекте.Есть ли способ загрузить его из его пути, не копируя его в папку bin?

1 Ответ

0 голосов
/ 29 мая 2018

Вы определяете метод GetAssembly в прокси-домене, который перетаскивает загруженный Assembly в основной домен.Это делает всю концепцию бессмысленной, потому что даже если вы выгрузите прокси-домен, ваш основной домен будет в конечном итоге загрязнен загруженной сборкой.

Вместо того, чтобы возвращать сборку, просто используйте ее внутри прокси-домена.Если вы хотите вернуть некоторую информацию в основной домен, вы должны передать простые сериализуемые типы (или удаленные объекты, полученные из MarshalByRefObject), чтобы основной домен оставался чистым.

Вот как вы должны это сделать:

// This class provides callbacks to the host app domain.
// This is optional, you need only if you want to send back some information
public class DomainHost : MarshalByRefObject
{
    // sends any object to the host. The object must be serializable
    public void SendDataToMainDomain(object data)
    {
        Console.WriteLine($"Hmm, some interesting data arrived: {data}");
    }

    // there is no timeout for host
    public override object InitializeLifetimeService() => null;
}

И ваш прокси должен выглядеть так:

class AssemblyLoader : MarshalByRefObject
{
    private DomainHost host;

    public void Initialize(DomainHost host)
    {
        // store the remote host here so you will able to use it to send feedbacks
        this.host = host;
        host.SendData("I am just being initialized.")
    }

    // of course, if your job has some final result you can have a return value
    // and then you don't even may need the DomainHost.
    // But do not return any Type from the loaded dll (not mentioning the whole Assembly).
    public void DoWork()
    {
        host.SendData("Work started. Now I will load some dll.");
        // TODO: load and use dll
        host.SendData(42);

        host.SendData("Job finished.")
    }
}

Использование:

var domain = AppDomain.CreateDomain("SandboxDomain");
var loader = (AssemblyLoader)domain.CreateInstanceAndUnwrap(typeof(AssemblyLoader).Assembly.FullName, typeod(AssemblyLoader).FullName);

// pass the host to the domain (again, this is optional; just for feedbacks)
loader.Initialize(new DomainHost());

// Start the work.
loader.DoWork();

// At the end, you can unload the domain
AppDomain.Unload(domain);

И, наконец, для самого FileNotFoundException:

В AppDomain вы можете загружать только сборки, которые находятся в той же самой или подпапке основного домена.Используйте это вместо Environment.CurrentDirectory в объекте установки:

var setup = new AppDomainSetup
{
    ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
    PrivateBinPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
};

Если вы действительно хотите загрузить сборку из любого места, загрузите ее как byte[]:

var dll = Assembly.Load(File.ReadAllBytes(fullPathToDll));
...