简体   繁体   English

使用类成员作为WNDPROC / DLGPROC(带有或不带有全局变量)

[英]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: 我正在创建一个窗口包装器类,但是CreateDialogParam需要一个全局对话框过程,因此我尝试了以下解决方法:

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. 我正在创建一个Dialog类,将其子类化以创建CMainWnd ,然后实例化该类。 In the Dialog class I have a member function defined as INT_PTR CALLBACK Dialog::cb_proc(HWND,UINT,WPARAM,LPARAM) . Dialog类中,我具有一个定义为INT_PTR CALLBACK Dialog::cb_proc(HWND,UINT,WPARAM,LPARAM)的成员函数。 Now, I know that windows must have a global function as a callback procedure. 现在,我知道Windows必须具有全局函数作为回调过程。

So I made a std::map<HWND,Dialog*> DlgProcs map to associate the dialogs window handle with its Dialog class pointer. 因此,我制作了一个std::map<HWND,Dialog*> DlgProcs映射,以将对话框窗口句柄与其Dialog类指针相关联。

And a INT_PTR CALLBACK DlgMainProc(HWND,UINT,WPARAM,LPARAM) so I could pass that to CreateDialogParam() . 还有一个INT_PTR CALLBACK DlgMainProc(HWND,UINT,WPARAM,LPARAM)所以我可以将其传递给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. DlgMainProc(...)的主体中,我搜索地图以使用hWnd参数查找Dialog*并返回其cb_proc(..)成员。

My problem is that none of the messages get processed, this is because the member procedure in my Dialog class never gets called. 我的问题是没有消息得到处理,这是因为Dialog类中的成员过程从未被调用过。 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. 即使当我在if (DlgProcs.find(hWnd) != DlgProcs.end()) {语句中的DlgMainProc放置一个MessageBox()DlgMainProc显示该消息框,直到我不得不中止Visual程序Studio2008。这告诉我它正在我的地图中找到hWnd 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. 奇怪的是,如果我将其放在else语句之后,它也会执行此操作,这矛盾地告诉我它没有在地图中找到hWnd

If I put a messagebox in the cb_proc member function it does not get displayed at all. 如果我在cb_proc成员函数中放置一个消息框,则它根本不会显示。 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. 当我从中删除消息框时(为了不必中止程序,这只是出于调试目的),程序运行但没有消息被处理,X按钮不会关闭程序,单击按钮不会执行任何操作。


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. 这是PasteBin代码: http ://pastebin.com/GsGUBpZU顺便说一句,我对此没有子类化,我的窗口被很好地创建了,只是没有消息被处理, cb_proc永远不会被调用。

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? 还是只是指导我使用成员函数作为DLGPROC的另一种方式?

The standard solution is to pass your this pointer as the last parameter to Create,DialogParam , stash it in DWLP_USER in your WM_INITDIALOG handler, and retrieve it from DWLP_USER thereafter. 标准的解决方案是你的传递this指针作为最后一个参数来Create,DialogParam ,藏匿它DWLP_USER在你的WM_INITDIALOG处理程序,从检索DWLP_USER其后。 Basically you use DWLP_USER as your map. 基本上,您将DWLP_USER用作地图。

I tried your code and it worked: cb_proc gets called. 我尝试了您的代码,它起作用了: cb_proc被调用。 You will miss any messages (eg WM_INITDIALOG ) that get sent before CreateDialogParam returns. 您将错过在CreateDialogParam返回之前发送的所有消息(例如WM_INITDIALOG )。

You can fix the latter problem by adding the window handle and the object to the map in g_MainDlgProc . 您可以通过在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. 将对象放置在全局对象中,然后可以将手柄/对象添加到地图中。

I'm just adding this here in case someone finds it useful; 我只是在这里添加,以防有人觉得有用。 using the magic of C++11 lambdas and templates you can have a simple wrapper template that means you don't have to continually rewrite the boiler-plate code for saving and loading userdata in window and dialog box procedures. 使用C ++ 11 lambda和模板的魔力,您可以拥有一个简单的包装器模板,这意味着您不必连续重写样板代码即可在窗口和对话框过程中保存和加载用户数据。

Here's an example for the DialogBoxParam function, but the same technique can be applied to CreateDialogParam and CreateWindowEx as well. 这是DialogBoxParam函数的示例,但是相同的技术也可以应用于CreateDialogParamCreateWindowEx

template <typename T, INT_PTR (T::*P)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)>
INT_PTR DialogBoxThis(T* pThis, HINSTANCE hInstance, LPCWSTR lpTemplateName, HWND hWndParent)
{
    return ::DialogBoxParam(hInstance, lpTemplateName, hWndParent, [](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -> INT_PTR {
        if (uMsg == WM_INITDIALOG) SetWindowLongPtr(hWnd, DWLP_USER, lParam);
        T* pThis = reinterpret_cast<T*>(GetWindowLongPtr(hWnd, DWLP_USER));
        return pThis ? (pThis->*P)(hWnd, uMsg, wParam, lParam) : FALSE;
    }, reinterpret_cast<LPARAM>(pThis));
}

You would use it like this: 您可以这样使用它:

class MyClass
{
    INT_PTR MyDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};

// from inside MyClass, we can show a dialog that uses member function MyDlgProc as the dialog procedure
// note it is NOT a static function

DialogBoxThis<MyClass, &MyClass::MyDlgProc>(this, hInstance,
    MAKEINTRESOURCE(IDD_MYDIALOG), hWndParent);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM