Thread: RegisterClass/UnregisterClass in DLL

  1. #1
    Registered User
    Join Date
    May 2014
    Posts
    121

    RegisterClass/UnregisterClass in DLL

    How can I make sure that these two functions get called exactly once when the DLL is loaded/unloaded by Windows regardless of how many processes have used the DLL? I also want to be able to use the code as a static library so that complicates things even further.

  2. #2
    Registered User
    Join Date
    May 2014
    Posts
    121
    I looked at the source code of a few popular open source projects and every single one of them was broken in this regard.

    Everyone seem to be using GetModuleHandle(NULL) to get the HINSTANCE that is then used when registering the window class but that is wrong according to this old blog entry from a Microsoft employee: What is the HINSTANCE passed to CreateWindow and RegisterClass used for? - The Old New Thing - Site Home - MSDN Blogs

    Using GetModuleHandle(NULL) so the window class is registered in the calling process' namespace does solve the problem somewhat since the window class is unregistered automatically when the calling process exits and you can also create init/terminate functions that the user of your DLL must call, but that approach is clearly not the correct one. It should be pretty safe to do if you pick a window class name that is likely to be unique, but it's still not what you're supposed to do.

    Surely this must be a common concern considering how old the WinAPI is but I couldn't find anything useful during my google searches on the subject.

  3. #3
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    Do it in DllMain. It's what the common controls do. For the static library you'll have to have functions for clients to call, no way around it.
    To get the HMODULE of whatever module your static library in is, just GetModuleHandleEx() with a pointer to your function as the module name, and the address and Unchanged_ref_count flags.

    Using GetModuleHandle(NULL) so the window class is registered in the calling process' namespace does solve the problem somewhat since the window class is unregistered automatically when the calling process exits and you can also create init/terminate functions that the user of your DLL must call, but that approach is clearly not the correct one.
    All the private window classes registered in the process are destroyed when it exits. There's nothing special about GetModuleHandle(NULL) when it comes to auto unregistration. UnregisterClass is probably aimed at things like plugins that might be unloaded and then reloaded at a different address (thus different hModule). Of course there's the 'it's the right thing to do' concern, but as you've witnessed pretty much nobody bothers with it, since a) it'll happen anyway and b) at any one time on a typical Windows system you have to create about 15,000 unique class names before you even come close to RegisterClass failing.

  4. #4
    Registered User
    Join Date
    May 2014
    Posts
    121
    The official documention explicitly forbids you from calling code from USER32.dll in DllMain but that is where RegisterClass/UnregisterClass happen to reside. It also doesn't give you a good way to keep track of how many times the DLL has been loaded/unloaded since the variables in DllMain are not shared across processes. I thought about using shared memory to keep track of how many processes are currently using the DLL but it doesn't sound like something I should be doing. Is there no standard way to keep track of when the first process loads your DLL and also when the last process unloads it?

    The special thing about calling GetModuleHandle(NULL) inside the DLL code is that it doesn't give you the HINSTANCE of the DLL but rather that of calling process. That means your DLL is injecting its window class name into the namespace of the calling process when you call RegisterClass with that HINSTANCE. Normally that doesn't lead to any issues provided that the class name isn't a common one but it's not something a DLL is supposed to do. I've seen a lot of different open souce code do this and it works well because Windows automatically cleans up window classes upon process exit that don't belong to DLLs so you don't need to worry about calling UnregisterClass. Unfortunately it's not the correct way to do things.

    This is the code I'm using to get the instance of the DLL (it works regardless of the how the code is linked):
    Code:
    bool get_current_module(HMODULE *result) {
        const DWORD flags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
        if (GetModuleHandleEx(flags, (LPCWSTR)get_current_module, result))
            return true;
        else
            return false;
    }

  5. #5
    Registered User
    Join Date
    May 2014
    Posts
    121
    I still haven't found a solution to this problem. This is the documentation for the RegisterClass function:

    https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx
    No window classes registered by a DLL are unregistered when the DLL is unloaded.
    This makes sense this there might be other processes that are still using the DLL so unregistering the window class every time you get a process detach notification would break the other processes.



    A DLL must explicitly unregister its classes when it is unloaded.
    What the hell Microsoft? Your own documentation specifically says that you're not allowed to call functions in User32.dll in DLLMain so how exaxtly are you supposed to unregister the class then (UnregisterClass resides in User32.dll)?



    https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx
    You should never perform the following tasks from within DllMain:
    Call functions in User32.dll or Gdi32.dll. Some functions load another DLL, which may not be initialized.


    Even if you were allowed to do it then you still wouldn't be able to do it safely without making sure that no other processes are still using the DLL and that seems like a nightmare to figure out.

  6. #6
    Registered User
    Join Date
    May 2014
    Posts
    121
    It seems that I had the wrong idea about how window class registration in Windows works. The window class you register in a DLL is not shared by all processes that use the DLL so you only need to concern yourself about what the current process is doing.

    There could still be issues if the process that is using your DLL is manually loading and unloading your DLL so you're basically forced to implement an init and corresponding uninit function and specificy that they must be called after (before) you call LoadLibrary (FreeLibrary).

  7. #7
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    Class names are per desktop. Classes themselves (except CS_GLOBALCLASS) are per process.
    If two programs independently register a class named "MainWindow", the ATOM returned by RegisterClass(Ex) would be the same but the functions which take class names or ATOMs would resolve it to the individual class each process created.

    You probably won't need it, but since it's not intuitive or seemingly widely known, you can get the class name from the ATOM using GetClipboardFormatName. The reason being that window class names, registered window messages and custom clipboard formats all share the same ATOM pool.

  8. #8
    Registered User
    Join Date
    May 2014
    Posts
    121
    What do you mean "per desktop"? I read somewhere that each process has it's own lookup table for classes where the HINSTANCE of the module is one field in the table. Something like this:

    Code:
    Module      | Window Class Name
    -------------------------------
    module1.dll | "Hello"
    module2.dll | "Hello"
    NULL        | "Hello"
    There's no conflict here because each module has its own namespace. Global classes are in their own namespace but I don't care about them.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. RegisterClass() and specific modules?
    By Alpo in forum Windows Programming
    Replies: 1
    Last Post: 11-24-2014, 09:09 AM
  2. failing RegisterClass
    By Bajanine in forum Windows Programming
    Replies: 6
    Last Post: 02-02-2008, 09:25 AM