1

I've been trying to write DLLs for the first time, and have a project I'm attempting to work on. I write my DLL, which is the simplest DLL I'm able to think of, and then compile it. I go to my executable, call LoadLibrary, which loads it just fine, and I'm able to call functions from the DLL like normal. FreeLibrary, however, returns 1 every single time. I have yet to be able to successfully unload a DLL (I'm playing around with hot-reloading DLLs and this is my hacked together solution just for practice).

Here's my code:

Executable's program.cpp:

#include <iostream>
#include <windows.h>

typedef void (*_TestFunction)();

int main()
{
    HMODULE hinstDLL = LoadLibrary(L"Test.dll");

    _TestFunction TestFunction = (_TestFunction)GetProcAddress(hinstDLL, "TestFunction");

    TestFunction();

    BOOL result = FreeLibrary(hinstDLL);
    std::cout << result << std::endl;

    if (hinstDLL != NULL)
    {
        std::cout << "Still loaded" << std::endl;
    }

    std::cin.ignore();
    return 0;
}

My DLL's dllmain.cpp:

#include "pch.h"

#define DLL_EXPORT

#include "dllmain.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        std::cout << "DLL Load Process" << std::endl;
        break;
    case DLL_THREAD_ATTACH:
        std::cout << "DLL Load Threaded Process" << std::endl;
        break;
    case DLL_THREAD_DETACH:
        std::cout << "DLL Unload Threaded Process" << std::endl;
        break;
    case DLL_PROCESS_DETACH:
        std::cout << "DLL Unload Process" << std::endl;
        break;
    }
    return TRUE;
}

DLL_FUNCTION void TestFunction()
{
    std::cout << "Hello World" << std::endl;
}

dllmain.h:

#pragma once

#ifdef DLL_EXPORT
#define DLL_FUNCTION extern "C" __declspec(dllexport)
#else
#define DLL_FUNCTION extern "C" __declspec(dllimport)
#endif

My precompiled header only contains iostream for the DLL to log information, and for the TestFunction().

I have no clue why it isn't working, and I've been stuck on this problem for almost a full day now. Any help is appreciated.

  • 1
    "FreeLibrary, however, returns 1 every single time." - When [FreeLibrary](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-freelibrary) returns nonzero, it means the function was successful. What makes you think it is failing? – Andreas Wenzel May 10 '20 at 20:01
  • 1
    You should read [this](https://stackoverflow.com/q/14208569/10871073) (especially second answer) about using `cout` in `DllMain`. – Adrian Mole May 10 '20 at 20:05
  • See [this link](https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices) for a more detailed list of restrictions inside DllMain. – Andreas Wenzel May 10 '20 at 20:09
  • @AdrianMole: Your description "second answer" is ambiguous. What are you sorting the answers by? Votes? Oldest answer? Activity? – Andreas Wenzel May 10 '20 at 20:11
  • @AndreasWenzel Sorry - the answer provided by MSalters, [here](https://stackoverflow.com/a/14211487/10871073). – Adrian Mole May 10 '20 at 20:13
  • I'm voting to close this question as *Typo or nonreproducable*, as it's simply a misunderstanding about what constitutes a true `BOOL` return value. FreeLibrary is not failing, and there is no error in the code related to the use of FreeLibrary (except for the confusion in testing hInst after the call to determine whether the DLL unloaded or not - FreeLibrary does not zero out `hInst` on success. It just returns a non-zero value). – Ken White May 10 '20 at 20:14
  • Perhaps I've been confused, my apologies. Thanks for all the help I've gotten so far, I'm just hitting one more snag. I posted this in a comment to another answer, the DLL, while it is freeing, it isn't being unloaded fully, I.E. the code in the DLL_PROCESS_DETATCH case isn't running. Is there a way to force this? – Sloan Phillippi May 10 '20 at 20:36

1 Answers1

1

According to Microsoft Documentation, FreeLibrary returns non-zero value if it succeeds. I've just verified it on the simplest example ever:

#include <iostream>
#include <Windows.h>

int main()
{
    const HMODULE handle = LoadLibraryW(L"kernel32.dll");

    std::cout << FreeLibrary(handle);

    return 0;
}

It prints 1, as expected.

If it calls DllMain with DLL_PROCESS_DETACH when you call FreeLibrary, then everything is fine and your DLL is being entirely unloaded from the memory.

Tihran
  • 106
  • 4
  • Alright. My mistake regarding the exit code. However, followup question, I'm calling FreeLibrary twice now to check if this is working, and while FreeLibrary succeeds, it isn't unloading the DLL from memory, i.e. it doesn't call anything in DLL_PROCESS_DETATCH. Is there a way I can force it to unload? – Sloan Phillippi May 10 '20 at 20:28
  • @SloanPhillippi are you sure DLL PROCESS DETACH is not called? I've just checked it on my pc with your example and it worked for me, I've got output `DLL Load Process` and then `DLL Unload Process` – Tihran May 10 '20 at 20:43
  • For me, while it does call it at program exit, it doesn't call it before printing "Still loaded", which means FreeLibrary() isn't triggering it. It's just the program exiting that's triggering it for me. Are you managing to get it to print before "Still loaded" shows up? If so, maybe I've made an incredibly dumb mistake somewhere. – Sloan Phillippi May 10 '20 at 20:50
  • @SloanPhillippi I've just noticed this 'still loaded' moment. Why do you expect this pointer to become NULL there? It will not - it will contain the address that, likely, will be invalid. FreeLibrary cannot change this pointer, at least because you pass it there by the copy. – Tihran May 10 '20 at 21:04
  • I'm aware that it won't change. I learned that from a comment on my original post. Nevertheless, the code in DLL_PROCESS_DETACH should be called before then, since FreeLibrary() is done before then, right? I know that Still loaded doesn't mean it's still loaded, but the fact that PROCESS_DETACH hasn't been called is an indication that it's still in there right? That and the fact that even once FreeLibrary is called, if I try to modify the DLL after FreeLibrary() has been called but before the program exits, it says it can't open it for writing because the executable is holding onto it. – Sloan Phillippi May 10 '20 at 21:08
  • Right, this is strange if you don't have any output from DllMain when FreeLibrary is being called. Probably there is a mistake somewhere. There is how I checked this: here is your [DllMain example](https://godbolt.org/z/YgjTWT). I put it in the main.cpp, compiled it with `cl /c main.cpp`, and then linked into DLL with `link /DLL main.obj`. Then I compiled other file that loads/unloads it and it was OK. – Tihran May 10 '20 at 21:14
  • Found the issue. Thanks to an outdated link from 2015 from someone having a similar issue, a flag was set in my registry to ignore `FreeLibrary` calls. The calls were, as far as my program was concerned, working as intended, but the library was sticking around in memory. Apparently, those flags are set by a compatibility wizard when your program crashes, most likely triggered by an error thrown in my testing process. For anyone else having this issue, search for "IgnoreFreeLibrary" in your registry and you'll find a registry flag set that has to do with your DLL. Delete it and it will work. – Sloan Phillippi May 11 '20 at 00:20