1

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

denfromufa
  • 5,610
  • 13
  • 81
  • 138
Jan
  • 1,905
  • 17
  • 41
  • Probably your code is wrong but you didn't show it – David Heffernan Feb 05 '18 at 21:17
  • @DavidHeffernan The code is realy as simple as that - that's why I'm so puzzled. It should either fail on LoadLibrary, or eventually return false on FreeLibrary.... well - none of that happens – Jan Feb 05 '18 at 21:21
  • We can't see your code. You didn't show it. Perhaps the function you call just returns true. You didnt show it. [mcve] – David Heffernan Feb 05 '18 at 21:32
  • Still not complete. Why can't you provide a [mcve]? Have you followed the link yet? Having said all of this, unloading and reloading Python isn't attainable in my experience. You need process segregation. – David Heffernan Feb 05 '18 at 22:17
  • @DavidHeffernan Can you please elaborate more on "unloading and reloading Python isn't attainable in my experience"? That's exactly the information I'm looking for. I'm able to unload and reload it, even after executing some very simple scripts.. however I loose this option after loading numpy module – Jan Feb 05 '18 at 22:27
  • My experience is that I'm not able to unload and reload. I've not investigated why. Certainly I have numpy loaded. But I think other modules will result in such behaviour. I think you won't succeed. For sure screwing with FreeLibrary is bound to fail at some point. – David Heffernan Feb 05 '18 at 22:34
  • @Jan did you try finalizing Python engine before unloading python36.dll? does this work without numpy loaded at all? – denfromufa Feb 06 '18 at 19:36
  • @denfromufa It works without numpy - loading numpy is culprit. And yes I was trying to unload the dll after calling Py_Finalize. I didn't find call to GetModuleHandleEx in python engine nor numpy source code - by I guess one of those is doing something similar that is causing the dll to be unloadable (installing hooks? using a diferent winapi that prevents unload?) – Jan Feb 07 '18 at 11:39
  • Maybe numpy loads other symbols from blas/lapack/mkl which prevent unloading? This was possible for numpy in 2007 according to their mailing list – denfromufa Feb 07 '18 at 12:08
  • @denfromufa do you happen to have a link? As finding the exact cause can greatly help in finding solution/workaround – Jan Feb 07 '18 at 12:43
  • @jan here it is https://mail.scipy.org/pipermail/numpy-discussion/2007-April/027000.html – denfromufa Feb 07 '18 at 14:16

1 Answers1

2

Is there any reason why FreeLibrary would repetitively return true?

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

Jan
  • 1,905
  • 17
  • 41