问题
I want to use a class from a different project in a separate solution in my current project dynamically. I thought the solution is to load the dll into my project. I used the following code to do my task and it worked.
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;
}
}
However, my problem now is that I want to unload the dll, which I can't do because there is no unload method in Assembly. The solution I found is that I must use AppDomain to load the assembly and then unload it.
Now here is my main problem. I keep getting FileNotFoundException
. Here is my code:
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
}
The last line is making the following exception 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.
I have tried the solutions on this link but nothing is working for me...I keep getting the same exception. After that I want to unload the domain, but I can't get through with the first problem of loading the dll. How to fix my code?
EDIT
When I copy the intended dll in the same bin folder of my project, it works. However, I don't want to copy the dll in my project. Is there a way to load it from its path without copying it to my bin folder?
回答1:
You define a GetAssembly
method in your proxy domain, which pulls the loaded Assembly
into the main domain. This renders the whole concept pointless, because even if you unload the proxy domain, your main domain will be eventually polluted by the loaded assembly.
Instead of returning the assembly just use it inside your proxy domain. If you want to push back some information into the main domain you must pass simple serializable types (or remote objects derived from MarshalByRefObject
) so the main domain remains clean.
This is how you should do it:
// 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;
}
And your proxy should look like this:
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.")
}
}
Usage:
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);
And finally for the FileNotFoundException
itself:
In an AppDomain
you can only load assemblies, which reside in the same or a subfolder of the main domain. Use this instead of Environment.CurrentDirectory
in the setup object:
var setup = new AppDomainSetup
{
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
PrivateBinPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
};
If you really want to load an assembly from any location, load it as a byte[]
:
var dll = Assembly.Load(File.ReadAllBytes(fullPathToDll));
来源:https://stackoverflow.com/questions/50588618/load-and-unload-a-dll-dynamically-into-my-project-using-appdomain