【问题标题】:Use class member as WNDPROC/DLGPROC with or without global使用类成员作为 WNDPROC/DLGPROC,带或不带全局
【发布时间】:2012-09-16 22:40:14
【问题描述】:

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

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

所以我制作了一个std::map<HWND,Dialog*> DlgProcs 映射来将对话框窗口句柄与其 Dialog 类指针相关联。

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

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

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


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

编辑:这是代码的相关部分

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?

【问题讨论】:

  • 请在此处粘贴您的代码,因为网站无法依赖第三方粘贴箱。
  • 嗯,代码很多。几乎所有这些都是相关的,我认为排除部分代码是不好的做法,因为这很可能是导致它的部分。好吧,我会选择我认为最相关的部分。

标签: c++ winapi callback wrapper wndproc


【解决方案1】:

标准解决方案是将您的this 指针作为最后一个参数传递给Create,DialogParam,将其存储在DWLP_USER 中的WM_INITDIALOG 处理程序中,然后从DWLP_USER 中检索它。基本上你使用DWLP_USER 作为你的地图。

【讨论】:

    【解决方案2】:

    我尝试了您的代码并且它有效:cb_proc 被调用。您将错过在CreateDialogParam 返回之前发送的所有消息(例如WM_INITDIALOG)。

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

    【讨论】:

    • 联合国。可信的。我总共测试了我的代码 7 次。似乎直到我切换到映射 HWND 和 Dialog* 之后我才使用我的小消息框调试方法,然后我调用了 CreateDialogParam,这就是问题所在。我将一个 NULL 指针传递到地图中。谢谢,我想我会将对话框初始化代码放入派生的构造函数中,因为我不知道如何获得 Dialog*,因为 CreateDialogParam 在构造函数中,因此尚未创建对象
    【解决方案3】:

    我只是在这里添加它以防有人发现它有用;使用 C++11 lambda 和模板的魔力,您可以拥有一个简单的包装模板,这意味着您不必不断重写样板代码来保存和加载窗口和对话框过程中的用户数据。

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

    你会这样使用它:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-24
      • 1970-01-01
      • 2020-11-29
      • 1970-01-01
      • 1970-01-01
      • 2012-03-22
      • 1970-01-01
      相关资源
      最近更新 更多