如何使用WndProc作为类函数

时间:2014-01-26 21:11:02

标签: c++ c class winapi

我正在尝试创建一个包含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);

我该怎么办?我真的很困惑......

谢谢!

2 个答案:

答案 0 :(得分:16)

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

在WndProc回调的特定情况下,您可以将对象指针存储在HWND本身中(使用SetWindowLong/Ptr(GWL_USERDATA)SetProp()),然后您的静态方法可以从{检索该对象指针{1}}参数(使用hWndGetWindowLong/Ptr(GWL_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, GWL_USERDATA, reinterpret_cast<LONG_PTR>(pThis)))
        {
            if (GetLastError() != 0)
                return FALSE;
        }
    }
    else
    {
        pThis = reinterpret_cast<Client*>(GetWindowLongPtr(hwnd, GWL_USERDATA));
    }

    if (pThis)
    {
        // use pThis->member as needed...
    }

    return DefWindowProc(hwnd, msg, wParam, lParam);
}

答案 1 :(得分:2)

不幸的是,您不能将类函数用作wndproc,因为编译器试图告诉您调用约定有所不同,即使这两个函数具有相同的签名,类函数也希望将该指针传递给它。在64位版本中,它将期望它在RCX / ECX注册表中,而在32位版本中,它将期望该指针是在堆栈上推送的最后一个参数。调用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);
}

我没有对此进行过测试,因此可能会有一些错误,但如果您遇到任何问题请告诉我,如果需要,我会对其进行改进。