PInvoke Library Load/Unload Behavior – Freeing Unmanaged Libraries

What’s the load behavior of dlls that we PInvoke into anyway? The libraries that are pinvoked are loaded when the 1st call to that function is made. The module will stay loaded in memory until the AppDomain shuts down. Typically this means that the dll will be loaded into your applications memory until the process goes away (unless you are creating/destroying app domains in your application).

Well, what can we do about that? Funny you asked…. J

You can actually free some resources by unloading the PInvoked dll by calling FreeLibrary on the dll. There are some caveats though. If you call FreeLibrary on a dll that you’ve pinvoked and then you try to call into the same library you’ll get a System.NullReferenceException (Access Violiation under the covers). The reason this happens is because the call to FreeLibrary is unmanaged and you’re circumventing the CLR. So, when you attempt to pinvoke again the CLR has no concept that the dll was unloaded from memory and tries to make a call to some invalid memory location.

You can actually get this scenario to work by calling LoadLibrary prior to making the pinvoke call and then when you want to free the library you have to account for the additional ref count and call FreeLibrary multiple times.

I really don’t recommend doing this in a production scenario because of other unknown side effects. If you need to do it to replace a dll or some such without taking your .NET app offline this would certainly help. This'll also help your memory footprint if you're infrequently calling into massive libraries and have limited memory resources.

Below is some simple code (from a winforms app with a form that has two buttons on it) for a simple app that illustrates this concept. You can use ProcessExplorer from https://www.sysinternals.com to view the loaded modules and you can see that the calls to freelibrary actually remove powrprof.dll from the process space.

Junfeng Zhang also has a post on dynamic pinvoke that talks about this and upcoming changes for Whidbey.

    Declare Function IsPwrHibernateAllowed Lib "powrprof.dll" () As Boolean

    Declare Function FreeLibrary Lib "kernel32.dll" (ByVal hModule As IntPtr) As Boolean

    Declare Function LoadLibraryA Lib "kernel32.dll" (ByVal hModule As String) As Boolean

    Declare Function GetModuleHandleExA Lib "kernel32.dll" (ByVal dwFlags As Integer, ByVal ModuleName As String, ByRef phModule As IntPtr) As Boolean

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        LoadLibraryA("powrprof.dll")

        MessageBox.Show(IsPwrHibernateAllowed)

    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

        Dim hMod As IntPtr

        If GetModuleHandleExA(0, "powrprof", hMod) Then

            Do While FreeLibrary(hMod)

            Loop

        End If

    End Sub