Unload a Dll Loaded Using Dllimport

Unload a DLL loaded using DllImport

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.

How to unload native DLL (loaded using [DllImport()] attribute) from SQL Server process?

It sounds like the "reference" is being held by the App Domain. You can try unloading the App Domain which should clear that out (best guess as I have no way to test this). You can do that by making any security changes to the database. The following works:

ALTER DATABASE {db_name} SET TRUSTWORTHY ON (or OFF if already ON);
GO
ALTER DATABASE {db_name} SET TRUSTWORTHY OFF (or ON if already OFF);
GO

Keep in mind that this will unload all AppDomains in that particular Database. This is usually not an issue since people rarely have multiple App Domains in a single Database (that would require Assemblies being owned by different Users, and most people just use dbo).


To see what App Domains exist and which assemblies are loaded into them, run the following:

SELECT DB_NAME(dca.[db_id]) AS [DatabaseName], dca.*, '---' AS [---], dcla.*
FROM sys.dm_clr_appdomains dca
INNER JOIN sys.dm_clr_loaded_assemblies dcla
ON dca.appdomain_address = dcla.appdomain_address
WHERE dca.[db_id] <> 32767;

If nothing is returned by that query and you still can't replace that external DLL, try the following (which seems like a bit much, but we need to know if it works before trying anything else):

sp_configure 'clr enabled', 0;
RECONFIGURE;
GO
sp_configure 'clr enabled', 1;
RECONFIGURE;

Two other options to try are:

  • Create a wrapper DLL that you would call via DLLImport. And it would call your Library64.dll

  • The last resort would be to forcefully remove the unmanaged DLL with yet another unmanaged call. You did not create the DLL using LoadLibrary(), but you should be able to get a reference to it using GetModuleHandleExA() and then using that handle in a call to FreeLibrary(). This is described in the following blog post: PInvoke Library Load/Unload Behavior – Freeing Unmanaged Libraries. It seems this was the only method to be successful. Please see @John's answer for the specific code.

C# Pinvoke and reloading a DLL

When you unload and reload a module it might load at a different address. Or it might load at the same address. It's up to the system to decide where to load the module. When you reload you will need to call GetProcAddress again for each function that you import, because the addresses of the functions may have changed, if the module has loaded at a different address.

Of course that part is missing from your code altogether. The part where you call GetProcAddress for every function that you import. You have to do it that way if you want to be able to reload. Otherwise the module might load at a different address and then everything falls over, as explained at the topic you linked to.

It looks as though you have been unlucky and the module is re-loaded at the same address. I say unlucky because you have not been able to observe the failure mode, and so are erroneously tempted into believing that your code is correct.

So you have to stop p/invoking directly to the module and obtain all function pointers by calling GetProcAddress. Convert these to delegates with Marshal.GetDelegateForFunctionPointer.

Reload a DLL which has been imported with DllImport

You can write a wrapper around the library that manages the access to it. Then you can use native methods to call the library. Take a look at this blog post.

How to dynamically load and unload a native DLL file?

Try this

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadLibrary(string libname);

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool FreeLibrary(IntPtr hModule);

//Load
IntPtr Handle = LoadLibrary(fileName);
if (Handle == IntPtr.Zero)
{
int errorCode = Marshal.GetLastWin32Error();
throw new Exception(string.Format("Failed to load library (ErrorCode: {0})",errorCode));
}

//Free
if(Handle != IntPtr.Zero)
FreeLibrary(Handle);

If you want to call functions first you must create delegate that matches this function and then use WinApi GetProcAddress

[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

IntPtr funcaddr = GetProcAddress(Handle,functionName);
YourFunctionDelegate function = Marshal.GetDelegateForFunctionPointer(funcaddr,typeof(YourFunctionDelegate )) as YourFunctionDelegate ;
function.Invoke(pass here your parameters);

DLL unloading procedure

DLL loading can be automatic (if listed in the import table) or manual (using LoadLibrary). Some managed frameworks, like .NET, will silently call LoadLibrary for you when they see a DLL import declaration in their metadata, but C++ is not one of these. The closest thing C++ has is delay-loading, where the function that calls LoadLibrary is (by default, you can substitute your own) provided by the compiler.

On the other hand, DLL unloading is always manual. Deleting an object never implicitly unloads a DLL. You have to call FreeLibrary (or FreeLibraryAndExitThread). And you had better not call FreeLibrary while objects defined in that library are still in use.

Now, the COM system in Windows is a bit more complicated, because it manages DLL lifetime for you. But DLL lifetime is still not controlled by object deletion, rather by calling a DllCanUnloadNow function in the DLL. Usually you need to maintain a count of active objects in order to write that function correctly. But it still requires your manual implementation, and you can simply always return false to never unload (this is a bit of a pain during development because you have to close the entire application to try a new plugin version, etc... but actually freeing every usage of a DLL and successfully unloading it is rare anyway)

And certainly there is nothing that will automatically unload a DLL listed in the import table, those get loaded during process startup and never unloaded, no matter how many object instances are created or destroyed.

Have to do FreeLibrary 2 times although I have done LoadLibrary only 1 time. Also, after unloading DLL, when trying to load it again, error happens

The update to your question provides enough information to explain the behaviour.

  1. You call LoadLibrary to load your DLL which accounts for one of the references.
  2. You call DllImport p/invoke functions which in turn lead to a call to LoadLibrary. That's the other reference to the library.
  3. Then when you call FreeLibrary, this succeeds twice since there were two calls to LoadLibrary.

But now you are in trouble because you've gone behind the back of the p/invoke system and it still believes that it owns one of the references to the DLL. A reference that you stole from it with your second call to FreeLibrary.

I guess that the information you are missing is how the DllImport p/invokes bind to the function. You are hoping that they will obtain a module handle by calling GetModuleHandle. They don't. They call LoadLibrary. They do this the first time they are called and the module they load stays loaded until the assembly itself unloads.

What you have to do, above all else, is follow the rules. The rules state that each call to LoadLibrary is to be matched by a call to FreeLibrary. You call LoadLibrary once. So you must call FreeLibrary exactly once also. Stop calling it twice and all is well.

I suspect that you are actually trying to arrange a system whereby you can load and unload DLLs. This prevents you from using p/invoke via DllImport. You'll have to do it all with LoadLibrary and GetProcAddress.

Your usage of SetDllDirectory looks somewhat messed up. What do you expect ".\\DLL_Dependencies\\" to be relative to? Supply a full path.

Force unloading of DLL from assembly

I would recommend to implement a separate process (EXE) which your application launches and which in turn loads the DLL.

This allows you to kill the process whenever need be...

I see several options on how to communicate - for example:

  • you could use COM (if you implement it as an out-of-process COM server)
  • you could use shared memory (very high performance, see this for a walkthrough and this for a .NET 2 wrapper)

Since you write that the method must be compatible with several Windows versions and some come with a desktop firewall I would refrain from using anything "networky" for the IPC.



Related Topics



Leave a reply



Submit