Search code examples
c++winapicallbackwrapperwndproc

Use class member as WNDPROC/DLGPROC with or without global


I'll go ahead and give a summary to this, how can I use a dialog procedure that is a member of a class? I am creating a window wrapper class, but CreateDialogParam needs a global dialog procedure, so I tried this workaround:

I have done a bit of searching on this topic. I am making a Dialog class which I am subclassing to make a CMainWnd and then instantiating that. In the Dialog class I have a member function defined as INT_PTR CALLBACK Dialog::cb_proc(HWND,UINT,WPARAM,LPARAM). Now, I know that windows must have a global function as a callback procedure.

So I made a std::map<HWND,Dialog*> DlgProcs map to associate the dialogs window handle with its Dialog class pointer.

And a INT_PTR CALLBACK DlgMainProc(HWND,UINT,WPARAM,LPARAM) so I could pass that to CreateDialogParam(). In the body of DlgMainProc(...) I search the map for using the hWnd parameter to find the Dialog* and return its cb_proc(..) member.

My problem is that none of the messages get processed, this is because the member procedure in my Dialog class never gets called. Even though when I put a MessageBox() in DlgMainProc inside a if (DlgProcs.find(hWnd) != DlgProcs.end()) { statement, the messagebox is displayed, over and over again until I have to abort the program from Visual Studio 2008. Which tells me that it is finding the hWnd in my map. The weird thing is it also does this if I put it in the else statement after that, which contradictingly tells me it is NOT finding the hWnd in the map.

If I put a messagebox in the cb_proc member function it does not get displayed at all. But during this I never get any compiler, linker, or runtime errors. When I remove the messagebox from it (as to not have to abort the program, it was just for debugging purposes) the program runs but no messages get processed, the X button does not close the program, button clicks do nothing.


Here is the PasteBin code: http://pastebin.com/GsGUBpZU Btw, I have no problem subclassing this, my window is created fine, just no messages are processed, cb_proc just never gets called.

EDIT: Here is the relevant parts of the code

map<HWND,Dialog*> g_DlgProcs;

INT_PTR CALLBACK g_MainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
        if (g_DlgProcs.find(hWnd) != g_DlgProcs.end()) {
                Alert("blah"); // Gets executed repeatedly
                return g_DlgProcs[hWnd]->cb_proc(hWnd, msg, wParam, lParam);
        } else {
                Alert("blah"); // Removing the above alert, this gets
                               // executed repeatedly, erm, as well.. O.o strange
                return FALSE;
        }
}

Dialog::Dialog(int id, HWND parent /* = HWND_DESKTOP */) {
        _id = id;
        _parent = parent;

        // Tried this before CreateDialogParam
        g_DlgProcs.insert(make_pair(_handle, this));

        _handle = CreateDialogParam(
                (HINSTANCE)GetModuleHandle(NULL),
                MAKEINTRESOURCE(id), _parent,
                (DLGPROC)g_MainDlgProc, NULL
        );

        // Then tried it after CreateDialogParam
        g_DlgProcs.insert(make_pair(_handle, this));
}

INT_PTR CALLBACK Dialog::cb_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
        Alert("blah"); // Never gets executed

        bool handled = true;

        switch (msg)
        {
        case WM_INITDIALOG:
                OnInitialize();
                break;
        case WM_COMMAND:
                if (HIWORD(wParam) == 0 || HIWORD(wParam) == 1) {
                        OnMenuCommand((HIWORD(wParam) == 1), (int)LOWORD(wParam));
                } else {
                        OnCtrlCommand((int)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam);
                }
                break;
        case WM_NOTIFY:
                {
                        LPNMHDR head = (LPNMHDR)lParam;
                        OnNotification(head->code, head->idFrom, head->hwndFrom);
                }
                break;
        case WM_CLOSE:
                OnClose(); // DestroyWindow(_handle)
                break;
        case WM_DESTROY:
                OnDestroy(); // PostQuitMessage(0)
        default:
                handled = ProcessMsg(msg, wParam, lParam);
        }

        // Convert bool to Windows BOOL enum
        return ((handled == true) ? TRUE : FALSE);
}

Does anybody know why it never gets called? Or maybe just guide me to another way to use a member function as a DLGPROC?


Solution

  • I tried your code and it worked: cb_proc gets called. You will miss any messages (e.g. WM_INITDIALOG) that get sent before CreateDialogParam returns.

    You can fix the latter problem by adding the window handle and the object to the map in g_MainDlgProc. If you get a message for an unknown window, you know it belongs to the window you're creating; put the object in a global and you can add the handle/object to the map.