Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call and use UnregisterClass?

Using Visual Studio 2013

I have an application that could potentially use up to about 20 window classes, but not all at the same time. In order to save on space I decided to Unregister those not needed any more before starting another batch of window classes, but I could not make the UnregisterClass function work.

I called Unregister at WM_DESTROY and/or WM_NCDESTROY but it always returned error message 1412 'Class still has open window'. Perhaps the Unregister call in WM_DESTROY failed because the window had not been destroyed yet, but I did not expect the call in WM_NCDESTROY to fail since this message is sent after destruction of the window.

The only way I could make UnregisterClass work was to call PostQuitMessage at either WM_DESTROY or WM_NCDESTROY. Then UnregisterClass would work after the message loop just before the whole application exits, but I want to start another batch of classes from inside the application, not to have to start it all over.

I am submitting a test program that shows the problem. It is Win32Project7, a program provided by Visual Studio 2013 with two tiny additions by myself - wrapped Messagebox (mbox) and a procedure to call unregister (tryunreg).

One extreme would be to register 20 window classes just to have them ready when needed, another would be to use a single windowclass and multiplex on HWND. Not too keen on any of these.

Questions:

  1. Have I made mistakes or wrong assumptions in this program?
  2. Is there a way to make unregisterclass work without having to close the program?
  3. how much space would a typical windowclass register need? Is it likely to be KB or MB? Any way to experiment to find out?

Have Googled on this. Did not find anything that is not already in the documentation, e.g. like unregister is automatic on exit from application. Stackoverflow has two posts similar to this, but with no answers.

The code:

I placed the code like this:

<pre>
 program fragments
</pre>

enclosed between html pre tags but the post was not sent. Error message said the text was formatted like a program but the indentation was not 4 spaces. Originally it wasn't but I changed it, but it still was not sent. Have never sent questions on this forum, so I am doing something wrong. What?


Here is the code I did not know how to send in my original post. Better late than never.

// Win32Project7.cpp : Defines the entry point for the application.
#include "stdafx.h"
#include "Win32Project7.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst;    
TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];

// Forward declarations of functions included in this code module:
ATOM    MyRegisterClass(HINSTANCE hInstance);
BOOL    InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);


static void mbox(const wchar_t * msg)  // added
    {
    int errcode;
    const wchar_t * caption = L"Info";
    int res = MessageBox(NULL, msg, caption, 0);
    if (res == 0)
       {
       errcode = GetLastError();
       return;  // was setting breakpoint, but never got here 
             //  but mbox does not give any output after postquit
       }
    }

static void tryunreg(const wchar_t * where)   // added
    {
    int errcode;
    wchar_t outmsg[100];
    BOOL b = UnregisterClass(szWindowClass, hInst);
    if (!b)
       {
       errcode = GetLastError();
       wsprintf(outmsg, L"%s: Unreg failed for classname %s errcode %d",
                where, szWindowClass, errcode);
       }
    else
       {
       wsprintf(outmsg, L"%s: Unreg worked", where);
       }
    mbox(outmsg);
    }





int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                 _In_opt_ HINSTANCE hPrevInstance,
                 _In_ LPTSTR    lpCmdLine,
                 _In_ int       nCmdShow)
    {
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);
    // TODO: Place code here.
    MSG msg;
    HACCEL hAccelTable;
    // Initialize global strings
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_WIN32PROJECT7, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);
    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
       {
       return FALSE;
       }
    hAccelTable = LoadAccelerators(hInstance,
                       MAKEINTRESOURCE(IDC_WIN32PROJECT7));
    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0))
        {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
            {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            }
        }
    tryunreg(L"After message loop" ); // added this
    return (int) msg.wParam;
    }



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
    {
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style      = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon      = LoadIcon(hInstance,
                                    MAKEINTRESOURCE(IDI_WIN32PROJECT7));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_WIN32PROJECT7);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, 
                                    MAKEINTRESOURCE(IDI_SMALL));
    return RegisterClassEx(&wcex);
    }

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global 
//        variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    {
    HWND hWnd;
    hInst = hInstance; // Store instance handle in our global variable
    hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
               CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
    if (!hWnd)
       {
       return FALSE;
       }
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return TRUE;
    }

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, 
                         LPARAM lParam)
    {
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;
    BOOL b;
    int errcode;
    wchar_t msg[100];
    switch (message)
        {
        case WM_COMMAND:
            wmId    = LOWORD(wParam);
            wmEvent = HIWORD(wParam);
            // Parse the menu selections
            switch (wmId)
                {
                case IDM_ABOUT:
                    DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), 
                              hWnd, About);
                    break;
                case IDM_EXIT:
                    DestroyWindow(hWnd);
                    break;
                default:
                    return DefWindowProc(hWnd, message, wParam, lParam);
                }
            break;
        case WM_PAINT:
            hdc = BeginPaint(hWnd, &ps);    
            // TODO: Add any drawing code here...
            EndPaint(hWnd, &ps);
            break;
        case WM_CLOSE:  // added
            // mbox(L"@wm_close before destroywindow");
            DestroyWindow(hWnd);
            break;
        case WM_DESTROY:
            tryunreg(L"@wm_destroy before postquit");  // added
            PostQuitMessage(0);  // in original MS code
            tryunreg(L"@wm_destroy after postquit");   // added
            break;
        case WM_NCDESTROY: // added
            tryunreg(L"@wm_NCdestroy before postquit");  // added
            //PostQuitMessage(0);
            tryunreg(L"@wm_NCdestroy after postquit");  // added
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    return 0;
    }



// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
    {
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
        {
        case WM_INITDIALOG:
            return (INT_PTR)TRUE;
        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
               {
               EndDialog(hDlg, LOWORD(wParam));
               return (INT_PTR)TRUE;
               }
            break;
        }
    return (INT_PTR)FALSE;
    }
like image 921
resander Avatar asked Apr 15 '15 15:04

resander


1 Answers

The time that UnregisterClass is needed is a dynamically loaded DLL that registers a window class. Such a library needs to ensure that the class is unregistered before it unloads, otherwise a CreateWindow for that class would make a call to code that is no longer present.

If you do choose to unregister window classes a delay can be introduced by using QueueUserAPC, however that does require changing the message loop (to one based around MsgWaitForMultipleObjectsEx and an embedded PeekMessage loop). Or you could use a thread message.

I prefer the APC because it allows for decoupling the code to be invoked from the rest of the program. For example in MFC using a thread message would require changing the message map for the thread class (the CWinApp in most cases).

like image 58
SoronelHaetir Avatar answered Oct 11 '22 00:10

SoronelHaetir