Search code examples
c#pinvokedllimportloadlibrarypython.net

FreeLibrary returns true indefinitely


Is there any reason why FreeLibrary would repetitively return true?

I'm trying to unload some native dll from my process, so I'm getting it's handle and then calling FreeLibrary until the ref count goes to zero and so FreeLibrary returns false... but it never does:

IntPtr pDll = DllLoadingImports.LoadLibrary(dllTounLoad);
//throw if pDll == IntPtr.Zero
while(DllLoadingImports.FreeLibrary(pDll));

The code runs and never returns. Also the process explorer shows the dll still loaded.

More background:

I'm trying to unload native library loaded by use of DllImport and I'm using trick described here: https://stackoverflow.com/a/2445558/2308106 It's for prototyping purposes so I don't have to care about possible consequnces... but I'm puzzled why the library won't unload

Edit 1: I found that similar behavior can be achieved by specifying GET_MODULE_HANDLE_EX_FLAG_PIN flag in GetModuleHandleEx function (which can be called from within DllMain on dll load).

The dll I'm trying to unload is python.dll (more precisely python36.dll). But haven't found usage of this flag within python source code. DllMain itself is empty.

Edit 2: I was asked for minimum executable example - so here it goes: It uses pythonnet library (version 2.3.0) - that's the PythonEngine calls.

[TestFixture]
public class PythonUnloadTest
{
    public static class DllImports
    {
        [DllImport("kernel32.dll")]
        public static extern IntPtr LoadLibrary(string dllToLoad);

        [DllImport("kernel32.dll")]
        public static extern bool FreeLibrary(IntPtr hModule);
    }

    [Test]
    public void PythonLoadUnload()
    {
        const string PythonDll = @"PythonEngine\python36";

        PythonEngine.Initialize();
        //locking etc not included for simplicity

        //Replace module with 'sys' (or some others) and dll can be unloaded
        var module = PythonEngine.ImportModule("numpy");
        module.Dispose();

        IntPtr pythonDllHandle = DllImports.LoadLibrary(PythonDll);
        if (pythonDllHandle == IntPtr.Zero)
        {
            throw new Exception("Dll not loaded");
        }

        for (int i = 0; i < 100000; i++)
        {
            if (!DllImports.FreeLibrary(pythonDllHandle))
            {
                return;
            }
        }

        Assert.Fail("Python not unloaded");
    }
}

Regardless of this specific case (python and pythonnet and loading of numpy) - there still needs to be some phenomenon that prevents process from being able to unload dlls by calling FreeLibrary. I'm suspecting instalment of some hooks or calling GetModuleHandleEx with above mentioned flag... I'll try to inspect numpy source. But if there are any specific tips what should I be looking for - I'd appreciate those


Solution

  • Is there any reason why FreeLibrary would repetitively return true?

    As I already put in edits - there can be few reasons: