36

I have an application that loads a DLL to execute a specific part of processing

Example : "Application.dll" loading "Process.dll" 

Process.dll is dynamically loaded at Runtime, using reflection, and not referenced in the application.

After processing is finished, the DLL needs to be recompiled on server and loaded back again later.
In order to do so, I need to free it, otherwise I get the following message : "Unable to copy file "Process.dll" to "Process.dll". The process cannot access the file 'Process.dll' because it is being used by another process."

So the question is : How to programmatically free/release/unload the Process.dll from my application before loading it back again. Of course,the whole point is to do this WITHOUT stopping the Application.

EDIT 1 :

A proposed solution goes like this :

AppDomain newDomain4Process = AppDomain.CreateDomain("newDomain4Process");
Assembly processLibrary = newDomain4Process.Load("Process.dll");
AppDomain.Unload(newDomain4Process);

The problem I am still having is that, though I am giving the proper full path, I get a FileNotFound Exception. The answer to this post did not have the expected effect either.

EDIT 2 :

This post saved my life, here is the code :

class ProxyDomain : MarshalByRefObject
    {
        public Assembly GetAssembly(string AssemblyPath)
        {
            try
            {
                return Assembly.LoadFrom(AssemblyPath);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }

   ProxyDomain pd = new ProxyDomain();
   Assembly a = pd.GetAssembly(FullDLLPath);

EDIT 3 :
I didn't get access to the AppDomain and unload it with the previous solution though. When I used the classic method of AppDomain Creation, I felt into Alexei's warning : AppDomain.Unload "seemed" to work, but the assembly was still loaded (Module View). So I still have my problem in some way, since I can't really unload the DLL efficiently.

Community
  • 1
  • 1
Mehdi LAMRANI
  • 11,289
  • 14
  • 88
  • 130
  • Regarding your Edit 3. This rings a bell and I remember now that when I tried it I gave up due to similar issues and instead went for creating a very simple wrapper executable that would load up the DLL and my original executable communicated with the DLL via a COM interface. Then I just closed down the wrapper executable whenever I needed to load the new version of the DLL and so I didn't need to worry about AppDomains. This worked ok for me since performance was not that important, so having the dll out of process didn't affect me :) – Hans Olsson Feb 04 '11 at 13:01
  • Dude LOL. I am about to give up (at least for the moment) since it's been two day I have the impression of drawning into the Abyss. My life has been a curse of misery since I've been trying to mess with all that I had tons of errors/issues that seemed very difficult to solve in a reasonable amount of time and without digging really deep into concepts to really understand what I'm doing. For the time being, I'm gonna take a break for some time and go back to good old regular coding as I have a project to deliver :-) – Mehdi LAMRANI Feb 04 '11 at 13:50
  • 1
    Beware the tradeoffs, marshalling (serialization) will happen between AppDomains. http://stackoverflow.com/questions/5600761/what-is-marshalling-what-is-happening-when-something-is-marshalled – gerleim Apr 29 '14 at 11:42
  • 1
    One thing you can also do to help decrease the downtime from the process of: Wait for old AppDomain to finish any tasks, unload the old AppDomain, copying the new DLL, then load a new AppDomain is using [Shadow Copy Assembiles](http://msdn.microsoft.com/en-us/library/ms404279.aspx). This removes that "Unable to copy file..." error you where getting and lets you change the order of your steps to be: Copy new dll, load new AppDomain and start processing new requests, wait for old AppDomain to finish old requests, unload old AppDomain. So now you have a 0 downtime situation. – Scott Chamberlain Jun 01 '14 at 18:38
  • Been working on this in C# under Unity, since Unity's editor never unloads a DLL (since there is no difference between the editor itself and your game, from Windows's point of view). My trick is to have my C# call a "shell" DLL with signatures in it that match all the functions in my "real" DLL. The shell DLL explicitly calls `LoadLibrary` and `GetProcAddress` so it can forward the shell calls to the real functions. It can also call `FreeLibrary`, allowing me to edit and recompile the real library while the editor is running. – Stevens Miller Jul 17 '18 at 01:16
  • 1
    @StevensMiller I've dabbled with that the same, except i don't bother unload the DLL I just create a new one with new pdb/mdb files. I ran into issues the debugger randomly won't break at breakpoints etc. Your idea sounds interesting, could you elaborate a bit more on it? Are you saying that this 'Shell' DLL is C++ and your real DLL is C#? So you're using LoadLibrary on a managed DLL? Did you have any issues debugging/stepping into your real DLL code? – vexe Feb 13 '19 at 05:03
  • @vexe, my "shell" DLL is written in C#. Unity loads it on first call. The shell's functions can be used by other code to load and unload a second DLL, and to delegate calls made to the shell's functions on to matching calls in the second DLL. The second DLL could be written in any language. The problem is that Unity loads a DLL on first use of any of the DLL's functions, and never unloads it. My approach solves that problem by having the shell do the loading and unloading of the second DLL, so I am free to recompile the second DLL without stopping/restarting Unity. Hope that helps! – Stevens Miller Feb 14 '19 at 14:13
  • @StevensMiller Thanks for the reply! I'm still trying to fill in some gaps. Where does that 'shell' live? Is it inside the Unity project under Assets visible from Unity? or is it outside the project and you load the shell via Assembly.Load*? You mentioned 'LoadLibrary' and 'FreeLibrary', afaik those functions don't work on managed DLLs? are you saying your shell is in C# and your real DLL code is C++? Apologies if I'm missing something obvious in what you're saying. I just don't get how you could recompile your real code while the shell has it loaded/locked since it needs to load it. Cheers! – vexe Feb 15 '19 at 04:00
  • [How to: Load and unload assemblies (MSDoc)](https://learn.microsoft.com/dotnet/standard/assembly/load-unload) & [How to unload a DLL C# (MSDN)](https://social.msdn.microsoft.com/Forums/vstudio/en-US/a2682b53-12ad-4719-b05c-df1476cb39c9/how-to-unload-a-dll?forum=csharpgeneral) –  Dec 11 '20 at 11:07

3 Answers3

20

It's been quite a while since I looked at this but I'm fairly sure that you'd need to create a new AppDomain and then load the DLL inside there.

The reason is that you can't unload an Assembly by it self but you can unload an AppDomain that contains an Assembly.

Hans Olsson
  • 54,199
  • 15
  • 94
  • 116
  • Correct, you cannot "unload" a DLL from an app domain – Gabriel Magana Feb 03 '11 at 15:11
  • That's right, there is no way to unload an assembly from an AppDomain once it's in there. – TToni Feb 03 '11 at 15:11
  • Okay then. Is my edit correct ? Technically speaking, is that enough to make the Trick ? And, BTW, what happens behind the scenes ? AppDomains are independent but still can "communicate" assemblies between each other ? – Mehdi LAMRANI Feb 03 '11 at 15:28
  • @Mika: Sorry, haven't looked at this in a few years so can't comment on your code snippet except to say that it looks plausible. – Hans Olsson Feb 03 '11 at 15:41
  • @MikaJacobi: Behind the scenes the AppDomains need a type defined in an assembly which they both share. When you create a type in a different AppDomain via the AppDomain.CreateInstance methods it gets proxied via .Net Remoting (i'm not sure if the channel uses ipc or a lighter mechanism, tho) – M.Stramm Dec 31 '12 at 12:57
  • You can always call `FreeLibrary()` -- but it's not very safe to do so unless you know for certain there is nothing dependent on the currently loaded image. non windows platforms may or may not have similar p/invokes. so it's not entirely true to write this off as "not possible" -- it is possible. it's also worth noting that a ".NET Assembly" is more than just a DLL, it could be mutiple DLLs (however rare it may be) or they may not be DLLs at all (binary assembly loads, reflection emits, etc.) – Shaun Wilson Nov 08 '16 at 07:24
  • @ShaunWilson, that will only half work. `FreeLibrary` (cooperating with `GetModuleHandle`) will unload the DLL, but you can't safely load it again. The entry points may remain valid, but you can't be sure of that. Having an intermediary DLL explicitly use `LoadLibrary`, `GetProcAddress`, and `FreeLibrary` does work for me, though. – Stevens Miller Jul 17 '18 at 01:19
  • This is not working for me due to the 'file not found' problem. The dll that I'm referencing references other dlls as well so I think there is a problem – ceds Dec 10 '19 at 12:11
  • 1
    @HansOlsson Provided link is not valid – RJN Mar 22 '21 at 13:54
7

Once an assembly has been loaded into an AppDomain, it cannot be unloaded. You have to dispose of the AppDomain. If you need to load and unload assemblies within an application, you'll need to spawn a new AppDomain and load the assembly, then destroy the AppDomain once you're finished with the assembly.

Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
6

While you seem to solve the problem according to your edit here are couple comments:

Watch out for objects from your Process.dll "leaking" into main AppDomain. I.e. something like throwing custom exception defined in Process.dll and caught in main AppDomain will force Process.dll to be loaded into main AppDomain. Check list of loaded assemblies in the main AppDomain after performing operations you are interested in.

Unloading of AppDomain can fail and may take long time.

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • Heeeeeelp ! I fall into the leaking problem ! Dude that's bad news. The Module view shows that the damn assembly is still loaded. Any way i can force it the heck out ? – Mehdi LAMRANI Feb 04 '11 at 12:38
  • 3
    You can't force it out - as soon it is loaded in particular AppDomain it will stay there. You have to avoid types of that assembly ever to cross AppDomain boundary. Make sure you don't return any objects that are defined in that Assembly. I.e. if you are returning object that implements interface from main assembly you may need to write proxy class in main assembly and return that instead. Expect to spend a lot of time getting whole story with loading assemblies to new AppDomain right. – Alexei Levenkov Feb 04 '11 at 17:01