如何将WndProc用作类函数

How to use WndProc as a class function

本文关键字:类函数 WndProc      更新时间:2023-10-16

我正试图创建一个包含WndProc的类,但我遇到了一个错误:

Error 2 error C2440: '=' : cannot convert from 'LRESULT (__stdcall Client::* )(HWND,UINT,WPARAM,LPARAM)' to 'WNDPROC'

我在网上搜索了一下,发现你需要使WndProc静态,但后来,它编译了,一切都很好,尽管如果我想更改什么,它不允许我:

Error 3 error C2352: 'Client::CreateMen' : illegal call of non-static member function

(CreateMen是使用HMENU等创建菜单的类中的一个函数)。

这是我的功能标题:

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

我能做什么?我真的很困惑。。。

谢谢!

非静态类方法有一个隐藏的this参数。这就是阻止该方法用作WndProc(或任何其他API回调)的原因。必须将类方法声明为static才能删除该this参数。但是,正如您已经注意到的,您无法从静态方法访问非静态成员。您需要一个指向对象的指针才能访问它们。

在WndProc回调的特定情况下,您可以将对象指针存储在HWND本身中(使用SetWindowLongPtr(GWLP_USERDATA)SetProp()),然后您的静态方法可以从hWnd参数中检索该对象指针(使用GetWindowLongPtr(GWLP_USERDATA)GetProp()),并根据需要使用该对象指针访问非静态成员。

例如:

private:
HWND m_Wnd;
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK Client::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
Client *pThis;
if (msg == WM_NCCREATE)
{
pThis = static_cast<Client*>(reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams);
SetLastError(0);
if (!SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis)))
{
if (GetLastError() != 0)
return FALSE;
}
}
else
{
pThis = reinterpret_cast<Client*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
}
if (pThis)
{
// use pThis->member as needed...
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
m_Wnd = CreateWindowEx(..., this);

不幸的是,您不能将类函数用作wndproc,因为当编译器试图告诉您调用约定不同时,即使两个函数具有相同的签名,类函数也希望this指针传递给它。在64位构建中,它希望它在RCX/ECX注册表中,而在32位构建中它希望this指针是推送到堆栈上的最后一个参数。当调用WndProc时,窗口代码不会这样做——本质上将其转换为对垃圾指针的函数调用。

可以做的是创建一个静态方法,该方法可以执行以下操作:

LRESULT Client::CreateMen(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
// The OS makes sure GWLP_USERDATA is always 0 before being initialized by the application
Client* client = (Client*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(msg == WM_INIT)
{
client = new Client();
SetWindowLongPtr(hwnd, GWLP_USERDATA, client);
}
if(msg == WM_DESTROY)
{
client = (Client*)GetWindowLongPtr(hwnd, GWLP_USERDATA, client);
SetWindowLongPtr(hwnd, GWLP_USERDATA, NULL);
delete client;
client = NULL;
}
if(client)
{
// Do stuff with the client instance
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}

我还没有测试过这个,所以它可能有一些错误,但如果你有任何问题,请告诉我,如果需要,我会改进它。