使用 AppDomain 将 dll 动态加载和卸载到我的项目中
Load and unload a dll dynamically into my project using AppDomain
我想在当前项目的单独解决方案中动态地使用来自不同项目的 class。我认为解决方案是将 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中没有unload方法。我找到的解决方案是必须使用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.
我已经尝试了关于此 link 的解决方案,但对我来说没有任何效果...我总是遇到同样的异常。之后我想卸载域,但我无法解决第一个加载dll的问题。如何修复我的代码?
编辑
当我将预期的 dll 复制到我的项目的同一个 bin 文件夹中时,它可以工作。但是,我不想在我的项目中复制 dll。有没有办法从它的路径加载它而不将它复制到我的 bin 文件夹?
您在代理域中定义了一个 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));
我想在当前项目的单独解决方案中动态地使用来自不同项目的 class。我认为解决方案是将 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中没有unload方法。我找到的解决方案是必须使用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.
我已经尝试了关于此 link 的解决方案,但对我来说没有任何效果...我总是遇到同样的异常。之后我想卸载域,但我无法解决第一个加载dll的问题。如何修复我的代码?
编辑
当我将预期的 dll 复制到我的项目的同一个 bin 文件夹中时,它可以工作。但是,我不想在我的项目中复制 dll。有没有办法从它的路径加载它而不将它复制到我的 bin 文件夹?
您在代理域中定义了一个 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));