使用std::bind绑定C++类成员函数为WNDPROC回调出现类型转换错误怎么解决?
问题原因分析
- WNDPROC是Windows定义的原生函数指针类型,要求指向的函数必须符合
LRESULT CALLBACK(HWND, UINT, WPARAM, LPARAM)的签名,且不能携带额外的绑定状态。std::bind返回的是带有捕获this的可调用对象,不是原生函数指针,二者类型完全不兼容,因此编译报错。 - 类的非静态成员函数会隐含一个this指针作为第一个入参,实际签名和WNDPROC要求的4个参数的签名不匹配,即使你用
std::bind绑定了this参数,Windows窗口管理器调用窗口过程时也无法识别bind生成的对象结构,无法正确传参执行。
正确实现方案
实现思路是用静态成员函数做窗口过程的入口,再把类实例指针和窗口绑定,转发消息到非静态成员函数处理:
- 把窗口过程声明为类的静态成员函数,消除隐式this指针,让它的签名和WNDPROC匹配
- 创建窗口时将类实例的this指针作为最后一个参数传入
CreateWindowEx - 在静态窗口过程中处理
WM_NCCREATE消息,取出传入的this指针,用SetWindowLongPtrW存到窗口的GWLP_USERDATA属性中 - 后续所有消息都可以从GWLP_USERDATA取出类实例指针,转发到非静态的成员函数处理
示例代码
#include <windows.h> class A { public: A(); // 静态窗口过程,作为WNDPROC注册 static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // 实际处理消息的非静态成员函数,可以访问类内所有成员 LRESULT WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); HWND m_hWnd = nullptr; // 存储创建的窗口句柄 }; A::A() { WNDCLASSEXW wc = { sizeof(WNDCLASSEXW), CS_CLASSDC, &A::StaticWndProc, // 直接传静态成员函数的指针 0L, 0L, GetModuleHandleW(NULL), NULL, NULL, NULL, NULL, L"Example", NULL }; RegisterClassExW(&wc); // 创建窗口时将this作为最后一个参数传入 m_hWnd = CreateWindowExW( 0, L"Example", L"TestWindow", WS_OVERLAPPEDWINDOW, 100, 100, 400, 300, NULL, NULL, GetModuleHandleW(NULL), this // 传入当前类实例指针 ); } LRESULT CALLBACK A::StaticWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { A* pThis = nullptr; if (msg == WM_NCCREATE) { // 从CreateWindowEx的最后一个参数取出this指针 CREATESTRUCTW* pCreate = reinterpret_cast<CREATESTRUCTW*>(lParam); pThis = reinterpret_cast<A*>(pCreate->lpCreateParams); // 把this存到窗口的用户数据区 SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis)); } else { // 从用户数据区取出this指针 pThis = reinterpret_cast<A*>(GetWindowLongPtrW(hWnd, GWLP_USERDATA)); } if (pThis) { // 转发到非静态成员函数处理消息 return pThis->WndProc(hWnd, msg, wParam, lParam); } // 没有拿到实例指针的时候走默认窗口过程 return DefWindowProcW(hWnd, msg, wParam, lParam); } LRESULT A::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { // 这里可以正常访问类的所有成员变量和成员函数 switch (msg) { case WM_DESTROY: PostQuitMessage(0); return 0; default: return DefWindowProcW(hWnd, msg, wParam, lParam); } }
内容的提问来源于stack exchange,提问作者Il'ya Zhenin
相关产品推荐
相关产品推荐

