Load DLL create Instance and Unload - c#

I have a Application which copies a DLL (UserControlLibrary) to its own Debug/Release folder and loads it using this Code:
AppDomain appDomain = AppDomain.CreateDomain("MyDomain");
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "DLLs (*.dll)|*.dll";
if (dialog.ShowDialog().Value)
{
string newLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\" + dialog.SafeFileName;
File.Copy(dialog.FileName, newLocation, true);
Assembly assembly = appDomain.Load(AssemblyName.GetAssemblyName(newLocation));
UserControl userControl = (UserControl) assembly.CreateInstance("WpfControlLibrary1.UserControl1");
}
I now add that UserControl to a Grid using:
grid.Children.Add(userControl);
Works fine. Now I try to unload the DLL using:
AppDomain.Unload(appDomain);
grid.Children.Clear();
If I now try to load the DLL (because it changed meanwhile) again using the above code I get an error telling me the file is in use (File.Copy).
I've read a lot of things and my guess is that I'm not allowed to use the UserControl like I did (because that loads into the Main AppDomain). How do I have to change the Code to make it work?
I've also read a lot of things about using MarshalByRefObject but unfortunately I was unable to implement it in this project. A example or modification of the code above would be nice.
EDIT:
From the comments I've read so far (especially from svick) it looks like I have to use "AppDomain.CreateInstanceAndUnwrap" instead of "AppDomain.Load".
I've seen that approach already while searching for a solution earlier but as svick mentioned that doesn't work because a UserControl can't inherit from MarshalByRefObject.
Does anyone know another way?

Read the documentation for AppDomain.Load(). It specifically says that this method loads the assembly both into the assembly you are calling the method on and into the current assembly. So, even when you unload the app domain, the assembly still stays loaded in the current assembly.
I'm not sure there is a way around this as you can't make UserControl inherit from MarhshalByRefObject.

When you call unload, the AppDomain is not immediately unloaded (see MSDN for the full article):
When a thread calls Unload, the target domain is marked for unloading.
The dedicated thread attempts to unload the domain, and all threads in
the domain are aborted. If a thread does not abort, for example
because it is executing unmanaged code, or because it is executing a
finally block, then after a period of time a
CannotUnloadAppDomainException is thrown...

Perhaps this quote from the MSDN article for AppDomain.Unload could explain things:
In the .NET Framework version 2.0 there is a thread dedicated to unloading application domains. This improves reliability, especially when the .NET Framework is hosted. When a thread calls Unload, the target domain is marked for unloading. The dedicated thread attempts to unload the domain, and all threads in the domain are aborted. If a thread does not abort, for example because it is executing unmanaged code, or because it is executing a finally block, then after a period of time a CannotUnloadAppDomainException is thrown in the thread that originally called Unload. If the thread that could not be aborted eventually ends, the target domain is not unloaded. Thus, in the .NET Framework version 2.0 domain is not guaranteed to unload, because it might not be possible to terminate executing threads.
Maybe there are some rogue threads that are keeping the app domain alive and therefore also keeping your file locked.
As a workaround, you could copy the DLLs to random file names instead.

Related

Executing code in main thread via dll injection

I need to inject a dll into the main thread of a process (otherwise my process will crash)
How would I go about doing this? My dll is in C++ obviously, my injection method though uses C# and CreateRemoteThread.
I've tried using std::thread(func) but that didn't work (of course it probably wouldn't since it most likely creates a new thread)
I don't know if this is a problem for my DLL or my injector, so all help and attempts to help is appreciated.
Your DllMain will run in the context of every thread... figure out which is the main thread.
Since you can't do much while the loader lock is held, create a callback with thread affinity (for example using SetTimer) and do all the work from that callback.
You can create the target process suspended and use CreateRemoteThread() for injection, but mind the following limitations:
You should copy the thread main routine for the remote thread to the address space of the target process.
This code cannot contain any external references (e.g. CRTL or direct WinApi calls). I would usually limit this code to loading of the DLL and either executing function from it, or relying on the DllMain to do the job you need. In order to call LoadLibrary and GetProcAddress methods, I obtain their addresses and copy structure containing this information to the target process and pass the address of the remote structure as an argument for the thread main routine in CreateRemoteThread(). You can use VirtualAllocEx() to allocate memory in the remote process,
Remote thread in this situation will be executed before main thread, including process and some Win32/64 initialization. Therefore, not every Win32 API is safe to call in this condition.
You can read more here: http://blogs.microsoft.co.il/pavely/2017/03/14/injecting-a-dll-without-a-remote-thread/

Thread.Abort() method freezes

So I've googled that it freezes because of using unsafe code, and AbortException throws only when control flow returns to managed code. So, in my case I have a native library, called in a thread. So sometimes I can't abort it, because the library is native and the Abort method not just do nothing, but freezes the calling thread.
So, I'd like to solve it.
For example, using a different process should help, but it's very complicated.
So, a less heavy solution is to use ' AppDomains' . But anyway I should create an exe and call it. I tried to generate it in memory like this
var appDomain = AppDomain.CreateDomain("newDomain");
var assemblyBuilder = appDomain.DefineDynamicAssembly(new AssemblyName("myAsm"), AssemblyBuilderAccess.RunAndCollect);
var module = assemblyBuilder.DefineDynamicModule("myDynamicModule");
var type = module.DefineType("myStaticBulder", TypeAttributes.Public);
var methBuilder = type.DefineMethod("exec", MethodAttributes.Static | MethodAttributes.Public);
var ilGenerator = methBuilder.GetILGenerator();
but I found only EMIT-way, it's very very complicated.
Does a superficial solution exist?
This cannot work by design. The CLR has very strict rules about what kind of code can safely be aborted. It is important, beyond the unwise use of Thread.Abort(), plenty of cases where the CLR must abort code, AppDomain unloads being foremost.
The iron-clad rule is that the CLR must be convinced that it is safe to abort the code. It is only convinced of that if the thread is busy executing managed code or is waiting on a managed synchronization object. Your case does not qualify, no way for the CLR to have any idea what that native code is doing. Aborting a thread in such a state almost never not causes problems. Same idea of the danger of Thread.Abort() but multiplied by a thousand. A subsequent deadlock on an internal operating system lock is very likely, utterly undebuggable.
An AppDomain therefore is not a solution either, it cannot be unloaded until the thread stopped running and it won't.
Only thing you can do is isolate that code in a separate process. Write a little helper EXE project that exposes its api through a standard .NET IPC mechanism like a socket, named pipe, memory mapped file, remoting or WCF. When the code hangs, you can safely Process.Kill() it. No damage can be done, the entire process state is thrown away. Recovering tends to be quite tricky however, you still do have to get the process restarted and get it back into the original state. Especially the state restoration is usually very difficult to do reliably.

How to handle blocking synchronous external DLL methods

Recently I worked with an external dll library where I have no influence on it.
Under some special circumstances, a method of this third party dll is blocking and never returning.
I tried to work around this issue by executing this method in a new AppDomain. After a custom timeout, I wanted to Unload the AppDomain and kill all this crap ;)
Unfortunately, it does not work - as someone would expect.
After some time it throws CannotUnloadAppDomainException since the blocking method does not allow aborting the thread gracefully.
I depend on using this library and it does not seem that there will be an update soon.
So can I work around this issue, even if it's not best practice?
Any bad hack appreciated :)
An AppDomain cannot typically solve that problem, it's only good to throw away the state of your program. The real issue is that your thread is stuck. In cases like these, calling Thread.Abort() is unlikely to work, it will just get stuck as well. A thread can only be aborted if it is a "alertable wait state", blocking on a CLR synchronization object. Or executing managed code. In a state that the CLR knows how to safely clean up. Most 3rd party code falls over like this when executing unmanaged code, no way to ever clean that up in a safe way. A decisive hint that this is the case is AppDomain.Unload failing to get the job done, it can only unload the AppDomain when it can abort the threads that are executing code in the domain.
The only good alternative is to run that code in a separate process. Which you can kill with Process.Kill(). Windows do the cleanup. You'd use a .NET interop mechanism to talk to that code. Like named pipes, sockets, remoting or WCF. Plus the considerable hassle of having to write the code that can detect the timeout, kills the process, starts it back up and recovers internal state since you now restart with an uninitialized instance of that 3rd party code.
Do not forget about the real fix. Create a small repro project that reproduces the problem. When it hangs, create a minidump of the process. Send both to the 3rd party support group.
after reading this (scroll down the end to Blocking Issues) I think your only solution is to run the method in a different process - this might involve quite a bit of refactoring and/or a 'host' project (eg Console application) that loads the method in question and makes it easy to call (eg reading args from command line) when launching the new process using the Process class
You can always use background worker, no need to create a new appdomain. This will ensure that you have complete control over the execution of the thread.
However, there is no way to ensure that you can gracefully abort the thread. As the dll is unmanaged, chances are there that it may cause memory leaks. However, spawning a new thread will ensure that your application does not crash when the Dll does not respond.

unload AppDomain

A DLL process is running in an isolated App. Domain which was runned from a 3rd application.
I would like to unload AppDomain
If I use Enviroment.Exit(), it also kills the father application (app which lauches the DLL process)
edit
Doing as it follows neither:
AppDomain domainObj = AppDomain.CurrentDomain;
AppDomain.Unload(domainObj);
If a function from the DLL is running as a thread in a separate AppDomain in your process, then you can try calling AppDomain.Unload. According to the documentation,
When a thread calls Unload, the target domain is marked for unloading. The dedicated thread attempts to unload the domain, and all threads in the domain are aborted. If a thread does not abort, for example because it is executing unmanaged code, or because it is executing a finally block, then after a period of time a CannotUnloadAppDomainException is thrown in the thread that originally called Unload. If the thread that could not be aborted eventually ends, the target domain is not unloaded. Thus, in the .NET Framework version 2.0 domain is not guaranteed to unload, because it might not be possible to terminate executing threads.
So if you are unable to successfully unload this other appdomain - you may have to find other alternatives.

How to properly unload an AppDomain using C#?

I have an application that loads external assemblies which I have no control over (similar to a plugin model where other people create and develop assemblies that are used by the main application). It loads them by creating new AppDomains for these assemblies and then when the assemblies are done being used, the main AppDomain unloads them.
Currently, it simplistically unloads these assemblies by
try
{
AppDomain.Unload(otherAssemblyDomain);
}
catch(Exception exception)
{
// log exception
}
However, on occasion, exceptions are thrown during the unloading process specifically CannotUnloadAppDomainException. From what I understand, this can be expected since a thread in the children AppDomains cannot be forcibly aborted due to situations where unmanaged code is still being executed or the thread is in a finally block:
When a thread calls Unload, the target
domain is marked for unloading. The
dedicated thread attempts to unload
the domain, and all threads in the
domain are aborted. If a thread does
not abort, for example because it is
executing unmanaged code, or because
it is executing a finally block, then
after a period of time a
CannotUnloadAppDomainException is
thrown in the thread that originally
called Unload. If the thread that
could not be aborted eventually ends,
the target domain is not unloaded.
Thus, in the .NET Framework version
2.0 domain is not guaranteed to unload, because it might not be
possible to terminate executing
threads.
My concern is that if the assembly is not loaded, then it could cause a memory leak. A potential solution would be to kill the main application process itself if the above exception occurs but I rather avoid this drastic action.
I was also considering repeating the unloading call for a few additional attempts. Perhaps a constrained loop like this:
try
{
AppDomain.Unload(otherAssemblyDomain);
}
catch (CannotUnloadAppDomainException exception)
{
// log exception
var i = 0;
while (i < 3) // quit after three tries
{
Thread.Sleep(3000); // wait a few secs before trying again...
try
{
AppDomain.Unload(otherAssemblyDomain);
}
catch (Exception)
{
// log exception
i++;
continue;
}
break;
}
}
Does this make sense? Should I even bother with trying to unload again? Should I just try it once and move on? Is there something else I should do? Also, is there anything that can be done from the main AppDomain to control the external assembly if threads are still running (keep in mind others are writing and running this external code)?
I'm trying understand what are best practices when managing multiple AppDomains.
I've dealt with a similar problem in my app. Basically, you can't do anything more to force the AppDomain to go down than Unload does.
It basically calls abort of all threads that are executing code in the AppDomain, and if that code is stuck in a finalizer or unmanaged code, there isn't much that can be done.
If, based on the program in question, it's likely that the finalizer/unmanaged code will finish some later time, you can absolutely call Unload again. If not, you can either leak the domain on purpose or cycle the process.
Try to make GC.Collect() if you do not unload the domain.
try
{
AppDomain.Unload(otherAssemblyDomain);
}
catch (CannotUnloadAppDomainException)
{
GC.Collect();
AppDomain.Unload(otherAssemblyDomain);
}
I had similar issues with random behavior for months now, (with some app.Unload even BLOCKING forever ! on some machines)
finally decided to take a big breath and made process isolation.
you can spawn child console process and redirect output
if you need to cancel this is finger in the nose to kill child process and all dependencies / handles.
To an extreme i had to run dedicated cleanup code, and came to solution to create additional process with dedicated cmd line waiting input extracted from console output of initial runner process.
yes this app domain is a real joke and i think this is not a coincidence that it is not anymore in net core.

Categories