Reputation:
I'm trying to load a DLL file into a separated app domain and invoke a method in the DLL file and get some response from it. The DLL file did not exist in the project bin
folder when the application starts, the DLL file is loaded from another folder. After I have done with the DLL file I want to unload the app domain that I have just created.
The steps:
Here is what I've tried so far
This is the code in 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";
}
}
}
Here is how I load the DLL file
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);
}
Here is something I've observed so far, I'm not sure if that is just me or I'm missing something
-If the "MyAssembly.dll" exists in the project bin folder before the application starts, I can load the dll file
-If the "MyAssembly.dll" did not exist in the project bin folder before application starts, instead it was loaded somewhere else other than the project bin folder, I cannot load the dll file. For example, the project bin folder is "C:\Main\MyMainProject\MyMainProject\bin", and DLL is loaded from C:\MyProject\MyAssembly.dll"
-If I move the "MyAssembly.dll" file into the bin folder (using File.Copy()
or File.Move()
), it somehow stop the rest of the code to be executed.
The error message I received
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
I know I can use Assembly.LoadFrom(@"PATH\TO\MY\DLL")
, but the problem with this one is I cannot unload the DLL
Upvotes: 2
Views: 882
Reputation:
After days of research, I finally got it working. Below is my final working code.
Useful reference links that helped me achieved this
C# reflection - load assembly and invoke a method if it exists
Using AppDomain in C# to dynamically load and unload dll
The code in MyAssembly.dll is same as in the question. I also realized that I can return an object type as well.
How I load the DLL file into separated app domain and unload the app domain
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);
}
}
My class that inherits 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;
}
}
A common error that you may run into
'xxx' is not marked as serializable
This could happen if you try to pass a custom class as parameter, like this
public void ExecuteAssembly(MyClass param1)
In this case, put a [Serializable]
to MyClass
, like this
[Serializable]
public class MyClass { }
Upvotes: 1