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.
My code is as follows.
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".
Guess you cannot do that, since WNDPROC stands for a function pointer. Every function pointer can be converted to a std::function, yet not every std::function represents a function pointer.
Proof of impossibility of your plan: Technically, WNDPROC represents only the address of the function in memory which is to be called. Hence a variable of type WNDPROC does not contain "space" to store information about bound parameters.
Its the same problem as in the following example:
Yet in order to call
h
from aCallingObject
with some parameter value determined at runtime, you must store the parameter value in a static variable and then write a wrapper function callingh
with this value as argument.That's the reason callback functions usually take an argument of type
void *
, where you can pass arbitrary data needed for the calculation.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.
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 throughSetWindowLongPtr
andGetWindowLongPtr
.Store an instance pointer in the window extra data area after construction:
Retrieve the instance pointer from the static window procedure and call into the window procedure member function:
The signature of the class member
WindowProc
is the same as in the code you provided.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):
Pass instance pointer to
CreateWindowExW
:Extract instance pointer and store it in the window extra data area when the first message (
WM_NCCREATE
) is sent to the window: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 thatStaticWindowProc
will be called while the instance pointer is not yet available. As a consequence theif
-statement insideStaticWindowProc
is required so that messages during creation (likeWM_CREATE
) do get properly handled.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.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.