Using Appdomain in C# to Dynamically Load and Unload Dll

Using AppDomain in C# to dynamically load and unload dll

Thanks guys, here is link where i found answer to my quetion:

The MSDN forum description for load and unload of assemblies dynamically

The other dll can be dynamically loaded and unloaded using another class which does load assembly and and call methods in that assembly...
AppDomain.CreateInstanceAndUnwrap generally wants input as assemblies from current project or generally current namespace. to solve that i need Assembly.LoadFrom(); to be used in some other class and create AppDomain and create instance of this class using AppDomain object as given in link.

Thanks for ur replies guys.

Load and unload a dll dynamically into my project using AppDomain

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));

How to dynamically load and unload (reload) a .dll assembly

You cannot unload a single assembly, but you can unload an Appdomain. This means you need to create an app domain and load the assembly in the App domain.

Exmaple:

var appDomain = AppDomain.CreateDomain("MyAppDomain", null, new AppDomainSetup
{
ApplicationName = "MyAppDomain",
ShadowCopyFiles = "true",
PrivateBinPath = "MyAppDomainBin",
});

ShadowCopyFiles property will cause the .NET runtime to copy dlls in "MyAppDomainBin" folder to a cache location so as not to lock the files in that path. Instead the cached files are locked. For more information refer to article about Shadow Copying Assemblies

Now let's say you have an class you want to use in the assembly you want to unload. In your main app domain you call CreateInstanceAndUnwrap to get an instance of the object

_appDomain.CreateInstanceAndUnwrap("MyAssemblyName", "MyNameSpace.MyClass");

However, and this is very important, "Unwrap" part of CreateInstanceAndUnwrap will cause the assembly to be loaded in your main app domain if your class does not inherit from MarshalByRefObject. So basically you achieved nothing by creating an app domain.

To solve this problem, create a 3rd Assembly containing an Interface that is implemented by your class.

For example:

public interface IMyInterface
{
void DoSomething();
}

Then add reference to the assembly containing the interface in both your main application and your dynamically loaded assembly project. And have your class implement the interface, and inherit from MarshalByRefObject. Example:

public class MyClass : MarshalByRefObject, IMyInterface
{
public void DoSomething()
{
Console.WriteLine("Doing something.");
}
}

And to get a reference to your object:

var myObj = (IMyInterface)_appDomain.CreateInstanceAndUnwrap("MyAssemblyName", "MyNameSpace.MyClass");

Now you can call methods on your object, and .NET Runtime will use Remoting to forward the call to the other domain. It will use Serialization to serialize the parameters and return values to and from both domains. So make sure your classes used in parameters and return values are marked with [Serializable] Attribute. Or they can inherit from MarshalByRefObject in which case the you are passing a reference cross domains.

To have your application monitor changes to the folder, you can setup a FileSystemWatcher to monitor changes to the folder "MyAppDomainBin"

var watcher = new FileSystemWatcher(Path.GetFullPath(Path.Combine(".", "MyAppDomainBin")))
{
NotifyFilter = NotifyFilters.LastWrite,
};
watcher.EnableRaisingEvents = true;
watcher.Changed += Folder_Changed;

And in the Folder_Changed handler unload the appdomain and reload it again

private static async void Watcher_Changed(object sender, FileSystemEventArgs e)
{
Console.WriteLine("Folder changed");
AppDomain.Unload(_appDomain);
_appDomain = AppDomain.CreateDomain("MyAppDomain", null, new AppDomainSetup
{
ApplicationName = "MyAppDomain",
ShadowCopyFiles = "true",
PrivateBinPath = "MyAppDomainBin",
});
}

Then when you replace your DLL, in "MyAppDomainBin" folder, your application domain will be unloaded, and a new one will be created. Your old object references will be invalid (since they reference objects in an unloaded app domain), and you will need to create new ones.

A final note: AppDomains and .NET Remoting are not supported in .NET Core or future versions of .NET (.NET 5+). In those version, separation is achieved by creating separate processes instead of app domains. And using some sort of messaging library to communicate between processes.

Loading DLL dynamically into separated app domain then unload

After days of research, I finally got it working. Below is my final working code.

Useful reference links that helped me achieved this

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

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 { }

dynamically loading and unloading of c# assembly into appdomain

I found that Assembly.LoadFile(...) will lock the file, even beyond the point of unloading the AppDomain.

My solution is to first load the assembly bytes into memory, and then load it into the AppDomain:

var assemblyBytes = System.IO.File.ReadAllBytes("myassembly.dll");
var assembly = System.Reflection.Assembly.Load(assemblyBytes);

That way the file is never locked and you can delete it, etc.

There's a better answer to a similar question here.

Loading DLLs into a separate AppDomain

More specifically

AppDomain domain = AppDomain.CreateDomain("New domain name");
//Do other things to the domain like set the security policy

string pathToDll = @"C:\myDll.dll"; //Full path to dll you want to load
Type t = typeof(TypeIWantToLoad);
TypeIWantToLoad myObject = (TypeIWantToLoad)domain.CreateInstanceFromAndUnwrap(pathToDll, t.FullName);

If all that goes properly (no exceptions thrown) you now have an instance of TypeIWantToLoad loaded into your new domain. The instance you have is actually a proxy (since the actual object is in the new domain) but you can use it just like your normal object.

Note: As far as I know TypeIWantToLoad has to inherit from MarshalByRefObject.



Related Topics



Leave a reply



Submit