hkB*_*sai 7 c++ winapi wndproc stdbind std-function
我正在尝试将非静态类成员绑定到标准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);
}
};
Run Code Online (Sandbox Code Playgroud)
当我按原样运行它时,它会给出错误消息:
Error: no suitable conversion function from "std::function<LRESULT(HWND, UINT, WPARAM, LPARAM)>" to "WNDPROC".
Run Code Online (Sandbox Code Playgroud)
虽然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));
}
Run Code Online (Sandbox Code Playgroud)从静态窗口过程中检索实例指针并调用窗口过程成员函数:
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);
}
Run Code Online (Sandbox Code Playgroud)
类成员的签名与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*);
// ...
Run Code Online (Sandbox Code Playgroud)将实例指针传递给CreateWindowExW:
m_hWnd = CreateWindowEx( /* ... */,
static_cast<LPVOID>(this) );
// SetWindowLongPtrW is called from the message handler
}
Run Code Online (Sandbox Code Playgroud)当第一个message(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);
}
Run Code Online (Sandbox Code Playgroud)注1:在创建窗口之后,实例指针存储在窗口额外数据区域中,而在创建lpfnWndProc之前设置该窗口.这意味着StaticWindowProc将在实例指针尚不可用时调用.因此,if需要在内部使用-statement,StaticWindowProc以便在创建期间(如WM_CREATE)正确处理消息.
注1a:注1中所述的限制不适用于替代实施.实例指针将从第一个消息开始前进,WindowProc因此将为所有消息调用类成员.
注意2:如果你想在底层HWND被破坏时销毁C++类实例,那么就是WM_NCDESTROY这样做的地方; 它是发送到任何窗口的最终消息.