[英]How do I `std::bind` a non-static class member to a Win32 callback function `WNDPROC`?
我正在尝试将非静态类成员绑定到标准WNDPROC
函数。 我知道我可以通过使类成员静态来做到这一点。 但是,作为一名C ++ 11 STL学习者,我对使用<functional>
标题下的工具非常感兴趣。
我的代码如下。
class MainWindow
{
public:
void Create()
{
WNDCLASSEXW WindowClass;
WindowClass.cbSize = sizeof(WNDCLASSEX);
WindowClass.style = m_ClassStyles;
WindowClass.lpfnWndProc = std::function<LRESULT(HWND, UINT, WPARAM, LPARAM)>
( std::bind(&MainWindow::WindowProc,
*this,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3,
std::placeholders::_4));
WindowClass.cbClsExtra = 0;
WindowClass.cbWndExtra = 0;
WindowClass.hInstance = m_hInstance;
WindowClass.hIcon = LoadIconW(m_hInstance, MAKEINTRESOURCEW(IDI_WINDOW));
WindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WindowClass.hbrBackground = (HBRUSH) COLOR_WINDOW;
WindowClass.lpszMenuName = MAKEINTRESOURCEW(IDR_MENU);
WindowClass.lpszClassName = m_ClassName.c_str();
WindowClass.hIconSm = LoadIconW(m_hInstance, MAKEINTRESOURCEW(IDI_WINDOW_SMALL));
RegisterClassExW(&WindowClass);
m_hWnd = CreateWindowEx(/*_In_ DWORD*/ ExtendedStyles,
/*_In_opt_ LPCTSTR*/ m_ClassName.c_str(),
/*_In_opt_ LPCTSTR*/ m_WindowTitle.c_str(),
/*_In_ DWORD*/ m_Styles,
/*_In_ int*/ m_x,
/*_In_ int*/ m_y,
/*_In_ int*/ m_Width,
/*_In_ int*/ m_Height,
/*_In_opt_ HWND*/ HWND_DESKTOP,
/*_In_opt_ HMENU*/ NULL,
/*_In_opt_ HINSTANCE*/ WindowClass.hInstance,
/*_In_opt_ LPVOID*/ NULL);
}
private:
LRESULT CALLBACK WindowProc(_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
};
当我按原样运行它时,它会给出错误消息:
Error: no suitable conversion function from "std::function<LRESULT(HWND, UINT, WPARAM, LPARAM)>" to "WNDPROC".
虽然JohnB已经解释了为什么这是不可能的详细信息,但这是您要解决的问题的常见解决方案:授予对静态类成员的类实例访问权限。
解决方案的指导原则是实例指针必须以静态类成员可访问的方式存储。 处理Windows时,额外的窗口内存是存储此信息的好地方。 通过WNDCLASSEXW::cbWndExtra
指定额外窗口内存的请求空间,同时通过SetWindowLongPtr
和GetWindowLongPtr
提供数据访问。
在构造之后将实例指针存储在窗口额外数据区域中:
void Create() { WNDCLASSEXW WindowClass; // ... // Assign the static WindowProc WindowClass.lpfnWndProc = &MainWindow::StaticWindowProc; // Reserve space to store the instance pointer WindowClass.cbWndExtra = sizeof(MainWindow*); // ... RegisterClassExW(&WindowClass); m_hWnd = CreateWindowEx( /* ... */ ); // Store instance pointer SetWindowLongPtrW(m_hWnd, 0, reinterpret_cast<LONG_PTR>(this)); }
从静态窗口过程中检索实例指针并调用窗口过程成员函数:
static LRESULT CALLBACK StaticWindowProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { // Retrieve instance pointer MainWindow* pWnd = reinterpret_cast<MainWindow*>(GetWindowLongPtrW(hwnd, 0)); if ( pWnd != NULL ) // See Note 1 below // Call member function if instance is available return pWnd->WindowProc(hwnd, uMsg, wParam, lParam); else // Otherwise perform default message handling return DefWindowProc(hwnd, uMsg, wParam, lParam); }
类成员WindowProc
的签名与您提供的代码中的签名相同。
这是实现所需行为的一种方法。 Remy Lebeau建议对此进行修改,这样可以使所有消息通过类成员WindowProc
路由:
在窗口中分配额外数据的空间(与上面相同):
void Create() { WNDCLASSEXW WindowClass; // ... // Assign the static WindowProc WindowClass.lpfnWndProc = &MainWindow::StaticWindowProc; // Reserve space to store the instance pointer WindowClass.cbWndExtra = sizeof(MainWindow*); // ...
将实例指针传递给CreateWindowExW
:
m_hWnd = CreateWindowEx( /* ... */, static_cast<LPVOID>(this) ); // SetWindowLongPtrW is called from the message handler }
当第一条消息( WM_NCCREATE
)发送到窗口时,提取实例指针并将其存储在窗口额外数据区域中:
static LRESULT CALLBACK StaticWindowProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { // Store instance pointer while handling the first message if ( uMsg == WM_NCCREATE ) { CREATESTRUCT* pCS = reinterpret_cast<CREATESTRUCT*>(lParam); LPVOID pThis = pCS->lpCreateParams; SetWindowLongPtrW(hwnd, 0, reinterpret_cast<LONG_PTR>(pThis)); } // At this point the instance pointer will always be available MainWindow* pWnd = reinterpret_cast<MainWindow*>(GetWindowLongPtrW(hwnd, 0)); // see Note 1a below return pWnd->WindowProc(hwnd, uMsg, wParam, lParam); }
注1:在创建窗口之后,实例指针存储在窗口额外数据区域中,而在创建之前设置了lpfnWndProc
。 这意味着在实例指针尚不可用时将调用StaticWindowProc
。 因此, if
在StaticWindowProc
中使用if
-statement,以便在创建过程中(如WM_CREATE
)正确处理消息。
注1a:注1中所述的限制不适用于替代实施。 实例指针将从第一个消息开始前进,因此将为所有消息调用类成员WindowProc
。
注意2:如果要在销毁基础HWND
时销毁C ++类实例, WM_NCDESTROY
就是这样做的地方; 它是发送到任何窗口的最终消息。
猜猜你做不到,因为WNDPROC代表一个函数指针。 每个函数指针都可以转换为std :: function,但并不是每个std :: function都代表一个函数指针。
您的计划不可能的证明:从技术上讲,WNDPROC仅代表要调用的内存中函数的地址。 因此,WNDPROC类型的变量不包含“空格”来存储有关绑定参数的信息。
它与以下示例中的问题相同:
typedef void (* callbackFn) ();
struct CallingObject {
callbackFn _callback;
CallingObject (callbackFn theCallback) : _callback (theCallback) {
}
void Do () {
_callback ();
}
};
void f () { std::cout << "f called"; }
void g () { std::cout << "g called"; }
void h (int i) { std::cout << "h called with parameter " << i; }
int main () {
CallingObject objF (f); objF.Do (); // ok
CallingObject objG (g); objG.Do (); // ok
}
然而,为了从具有在运行时确定的某个参数值的CallingObject
调用h
,您必须将参数值存储在静态变量中,然后编写一个以此值作为参数调用h
的包装函数。
这就是回调函数通常采用void *
类型的参数的原因,您可以在其中传递计算所需的任意数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.