Search code examples
c++windowsunique-ptrcustom-deleter

Calling ::GetModuleHandleEx with unique_ptr having custom deleter to handle freeing resources automatically


Do I need to free the HMODULE in the below code?

DWORD flags =
  GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS;
HMODULE hm = 0;
  ::GetModuleHandleExW(flags, L"Test.dll", &hm);
HRSRC hResource =
  ::FindResourceW(hm, MAKEINTRESOURCEW(TEST1), L"ICON");

if (hResource) {
  HGLOBAL hLoaded = LoadResource(hm, hResource);
  if (hLoaded) {
    size = (int)SizeofResource(hm, hResource);
    auto lpAddress = LockResource(hLoaded);
    //Read resource here
    UnlockResource(hLoaded);
  } else {
    //Error logging
  }
  FreeResource(hLoaded);
} else {
  //Error logging
}

Here I am freeing most of the resource at right place except probably HMODULE! Do I need to free hm? My understanding is that we are only getting the HMODULE and not loading library here, so we may not need to free it?

If the answer is yes, then I wanted to try to something like what we have here:

Using std::unique_ptr for Windows HANDLEs

struct Deleter {
    void operator()(HMODULE hModule) const
    {
        if (hModule != nullptr)
        {
            FreeLibrary(hModule);
        }
    }
};

using unique_module = std::unique_ptr<std::remove_pointer<HMODULE>::type, Deleter>;

But I got bit confused like how to use this unique_module.

Meanwhile I was writing this:

HMODULE hm = 0;
unique_module um {hm};

::GetModuleHandleExW(flags, L"Test.dll", &um.get());

Though I have not compiled and tested, this seems wrong to me, and I need input on how to correctly write this custom deleter and use it with HMODULE and how to call it with ::GetModuleHandleEx().


Solution

  • First off, your use of GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS is wrong, as you are not passing GetModuleHandleExW() a memory address that is inside the module you are looking for. If you want to find a module by its name, then DON'T use GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS. In which case, you may as well just use GetModuleHandleW() instead, and not worry about FreeLibrary() at all.

    Now, that being said...

    To answer your actual question, the GetModuleHandleExW() documentation says:

    https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandleexw

    Retrieves a module handle for the specified module and increments the module's reference count unless GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT is specified. The module must have been loaded by the calling process.

    ...

    [in] dwFlags

    This parameter can be zero or one or more of the following values. If the module's reference count is incremented, the caller must use the FreeLibrary function to decrement the reference count when the module handle is no longer needed.

    So yes, you need to call FreeLibrary() if you are not using GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, eg:

    HMODULE hm = NULL;
    ::GetModuleHandleExW(0, L"Test.dll", &hm);
    ...
    FreeLibrary(hm);
    

    If you want to use unique_ptr with a custom Deleter to call FreeLibrary(), that is certainly doable, however you cannot obtain the address of the pointer that the unique_ptr holds, so you must retrieve the HMODULE before you then construct the unique_ptr from it, eg:

    HMODULE hm = NULL;
    ::GetModuleHandleExW(0, L"Test.dll", &hm);
    unique_module um {hm};
    

    Otherwise, you could simply forget about the Deleter and just use GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT instead if you must use GetModuleHandleExW(), or just use GetModuleHandleW() instead.