使用类成员作为 WNDPROC/DLGPROC 有或没有全局

2022-01-03 00:00:00 wrapper callback winapi c++ wndproc

我将继续对此进行总结,如何使用属于类成员的对话过程?我正在创建一个窗口包装类,但是 CreateDialogParam 需要一个全局对话框过程,所以我尝试了这个解决方法:

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:

我对这个话题做了一些搜索.我正在创建一个 Dialog 类,我将其子类化以创建一个 CMainWnd 然后实例化它.在 Dialog 类中,我有一个成员函数定义为 INT_PTR CALLBACK Dialog::cb_proc(HWND,UINT,WPARAM,LPARAM).现在,我知道 windows 必须有一个全局函数作为回调过程.

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.

所以我做了一个 std::mapDlgProcs 映射以将对话框窗口句柄与其 Dialog 类指针相关联.

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

还有一个 INT_PTR CALLBACK DlgMainProc(HWND,UINT,WPARAM,LPARAM),所以我可以将它传递给 CreateDialogParam().在 DlgMainProc(...) 的主体中,我搜索地图以使用 hWnd 参数来查找 Dialog* 并返回其 cb_proc(..) 成员.

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.

我的问题是没有处理任何消息,这是因为我的 Dialog 类中的成员过程从未被调用.即使我将 MessageBox() 放在 DlgMainProc 中的 if (DlgProcs.find(hWnd) != DlgProcs.end()) {> 语句,消息框会一遍又一遍地显示,直到我不得不从 Visual Studio 2008 中止程序.这告诉我它正在我的地图中找到 hWnd.奇怪的是,如果我在之后将它放在 else 语句中,它也会这样做,这自相矛盾地告诉我它没有在地图中找到 hWnd.

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.

如果我在 cb_proc 成员函数中放置一个消息框,它根本不会显示.但在此期间,我从未遇到任何编译器、链接器或运行时错误.当我从中删除消息框时(为了不必中止程序,它只是出于调试目的)程序运行但没有消息得到处理,X 按钮不会关闭程序,按钮点击什么也不做.

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.

这是 PasteBin 代码:http://pastebin.com/GsGUBpZU顺便说一句,我对此进行子类化没有问题,我的窗口创建得很好,只是没有处理消息,cb_proc 只是从未被调用.

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.

这是代码的相关部分

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);
}

<小时>

有人知道为什么它永远不会被调用吗?或者只是指导我使用另一种方法将成员函数用作 DLGPROC?


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

推荐答案

我试过你的代码,它奏效了:cb_proc 被调用.您将错过在 CreateDialogParam 返回之前发送的任何消息(例如 WM_INITDIALOG).

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.

您可以通过将窗口句柄和对象添加到 g_MainDlgProc 中的地图来解决后一个问题.如果你收到一个未知窗口的消息,你就知道它属于你正在创建的窗口;将对象放在全局中,您可以将句柄/对象添加到地图中.

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.

相关文章