Unload a DLL loaded using DllImport
Asked Answered
C

5

38

How do I unload a DLL which has been loaded using DllImport in C#?

Cowper answered 15/3, 2010 at 6:57 Comment(1)
If you intend to do so just to save few KBs of memory foot print of a loaded module then it isn't worth. CLR does so on its own whenever appDomain gets unloaded. I would be curious to know if you have some real reason to try unloading the native dll.Chloric
T
30

The most reliable way to unload an unmanaged DLL from a process that got loaded by a [DllImport] pinvoke declaration is to load it yourself, again, by pinvoking LoadLibrary(). That gives you a reliable handle to the DLL and works correctly even if the module name of the DLL is ambiguous. It doesn't have any affect at runtime, other than the Windows loader increasing the internal reference count on the DLL from 1 to 2.

You can then pinvoke FreeLibrary() twice to decrease the reference count to 0, passing it the IntPtr you got from LoadLibrary(). That unloads the DLL, as well as any dependent DLLs that got loaded.

Beware that you'll get very nasty failure when you try to pinvoke any exported function on the DLL again, any time after doing this. The pinvoke marshaller is unaware that the DLL isn't around anymore and will call the function at the address it thinks is still valid. Which bombs your program with an AccessViolation exception if you are lucky. Or runs a completely random bit of code if you are not so lucky and the address space formerly occupied by the DLL got re-used by another DLL. Anything can happen then, none of it good.

Theocracy answered 15/3, 2010 at 7:2 Comment(6)
You still didn't answer subbu's question. He was asking how to unload a DLL that was loaded by DllImport, not by manually loading it via LoadLibrary().Niple
Take a look here codeproject.com/KB/cs/…Oz
@Ants, this WILL unload a DLL loaded by DllImport. Mitch is getting the DLL handle by trying to load the (already loaded) DLL. Then you call FreeLibrary() twice to get rid of both the (otherwise useless) reference you just added AND the reference that DllImport has.Relax
At the time I commented, the original answer didn't say that FreeLibrary() should be called twice.Niple
Why does CLR fails to recognize if we have unloaded a native module using native FreeLibrary calls. Even the FreeLibrary calls go through P/invoke only. Isn't it?Chloric
Because the CLR doesn't know that you are manually calling FreeLibrary() directly, so it doesn't know to clear out any references that it itself had loaded before. And what FreeLibrary() were being called via GetProcAddress() instead of DllImport? So the CLR would have to install a hook into FreeLibrary() itself to intercept all calls.Donela
E
13

This should free a module previously loaded when you called P/Invoke function.

[DllImport("kernel32", SetLastError=true)]
static extern bool FreeLibrary(IntPtr hModule);

public static void UnloadModule(string moduleName)
{
    foreach(ProcessModule mod in Process.GetCurrentProcess().Modules)
    {
        if(mod.ModuleName == moduleName)
        {
            FreeLibrary(mod.BaseAddress);
        }
    }
}
Earthstar answered 11/8, 2012 at 11:38 Comment(5)
That will usually work. But module names can be ambiguous, it might release the wrong DLL.Valentinvalentina
Hans is right. So perhaps better using Path.GetFileName(mod.FileName) instead of mod.ModuleName ?Conceptualism
Or hang onto the pointer you get from LoadLibrary and compare BaseAddress to that.Aquarelle
(Or, if you save the IntPtr handle, just unload the handle directly instead of iterating the modules.)Aquarelle
An improvisation for LINQ lovers - var loadedAssemblyModule = Process.GetCurrentProcess().Modules.OfType<ProcessModule>() .FirstOrDefault(x => x.ModuleName == moduleName); if (loadedAssemblyModule != null) FreeLibrary(loadedAssemblyModule.BaseAddress);Chloric
P
6

Based on Peters recommendation this works for me:

    [DllImport("kernel32", SetLastError = true)]
    private static extern bool FreeLibrary(IntPtr hModule);

    public static void UnloadImportedDll(string DllPath)
    {
        foreach (System.Diagnostics.ProcessModule mod in System.Diagnostics.Process.GetCurrentProcess().Modules)
        {
            if (mod.FileName == DllPath)
            {
                FreeLibrary(mod.BaseAddress);
            }
        }
    }
Pokeweed answered 2/4, 2015 at 9:14 Comment(0)
S
0

Late to the party, but I've made a tool to do this. It's supposed to run in Unity, but I think it can be adopted to other C# solutions. It's available here: https://github.com/mcpiroman/UnityNativeTool.

Please note that it is essentionally a hack (but often used hack, see Harmony), so I don't quite recommend using it at production code.

Starcrossed answered 10/4, 2019 at 20:13 Comment(0)
B
-2

Since I came across the information here while I was looking around for information I figure I will contribute back what I ended up doing to fix an issue with the Sixense SDK on OSX IN UNITY. You'll see in there an implementation of dynamically loading/unloading a dylib on OSX:

https://gist.github.com/amirebrahimi/d7b02c01bcd3ca7da144

Bowden answered 19/8, 2015 at 18:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.