我如何`std :: bind`一个非静态类成员到Win32回调函数`WNDPROC`? [英] How do I `std::bind` a non-static class member to a Win32 callback function `WNDPROC`?
问题描述
我尝试将非静态类成员绑定到标准 WNDPROC
函数。我知道我可以简单地这样做通过使类成员静态。但是,作为一个C ++ 11 STL学习者,我对使用< functional>
头下的工具非常感兴趣。
I'm trying to bind a non-static class member to a standard WNDPROC
function. I know I can simply do this by making the class member static. But, as a C++11 STL learner, I'm very interested in doing it by using the tools under the <functional>
header.
我的代码如下。
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);
}
};
当我按原样运行时,会显示错误信息:
When I run it as is, it gives the error message:
Error: no suitable conversion function from "std::function<LRESULT(HWND, UINT, WPARAM, LPARAM)>" to "WNDPROC".
推荐答案
虽然JohnB已经解释了为什么不可能,这里是一个常见的解决方案,你要解决的问题:授予类实例访问静态类成员。
While JohnB already explained the details why this is not possible, here is a common solution to the problem you are trying to solve: Granting class instance access to a static class member.
解决方案的指导原则是,指针必须以静态类成员可访问的方式存储。当处理窗口时,额外的窗口存储器是存储此信息的好地方。额外窗口存储器的请求空间通过 WNDCLASSEXW :: cbWndExtra
指定,而数据访问通过 SetWindowLongPtr
和 GetWindowLongPtr
。
The guiding principle to the solution is that an instance pointer must be stored in a way that is accessible to the static class member. When dealing with windows the extra window memory is a good place to store this information. The requested space of extra window memory is specified through WNDCLASSEXW::cbWndExtra
while data access is provided through SetWindowLongPtr
and GetWindowLongPtr
.
-
在建构之后,在窗口额外数据区域中存储实例指针:
Store an instance pointer in the window extra data area after construction:
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));
}
从静态窗口过程检索实例指针,窗口过程成员函数:
Retrieve the instance pointer from the static window procedure and call into the window procedure member function:
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
与您提供的代码相同。
The signature of the class member WindowProc
is the same as in the code you provided.
这是实现所需行为的一种方法。 Remy Lebeau建议了一个变体,它有利于通过类成员 WindowProc
路由所有消息:
This is one way to implement the desired behavior. Remy Lebeau suggested a variation to this which has the benefit of getting all messages routed through the class member WindowProc
:
-
在窗口中分配空格额外数据(与上述相同):
Allocate space in the window extra data (same as above):
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
)发送到窗口:
Extract instance pointer and store it in the window extra data area when the first message (WM_NCCREATE
) is sent to the window:
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
将在实例指针不可用时被调用。因此,需要在 StaticWindowProc
内的 -statement,以便创建期间的消息(如
Note 1: The instance pointer is stored into the window extra data area after the window has been created while the lpfnWndProc
is set prior to creation. This means that StaticWindowProc
will be called while the instance pointer is not yet available. As a consequence the if
-statement inside StaticWindowProc
is required so that messages during creation (like WM_CREATE
) do get properly handled.
注意1a:注1中所述的限制不适用于替代实现。实例指针将从第一个消息向前可用,并且类成员 WindowProc
将因此被调用用于所有消息。
Note 1a: The restrictions stated under Note 1 do not apply to the alternative implementation. The instance pointer will be available going forward from the first message and the class member WindowProc
will consequently be called for all messages.
注意2:如果要销毁底层 HWND
时销毁C ++类实例, WM_NCDESTROY
是这样做的地方;它是发送到任何窗口的最后一条消息。
Note 2: If you want to destroy the C++ class instance when the underlying HWND
is destroyed, WM_NCDESTROY
is the place to do so; it is the final message sent to any window.
这篇关于我如何`std :: bind`一个非静态类成员到Win32回调函数`WNDPROC`?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!