Dialog window 过程作为 class 的成员函数

Dialog window procedure as a member function of a class

基于,我正在尝试创建一个简单的 Win32 应用程序,其中对话框 window 的 window procedure/callback 函数是一个成员函数的 class。我的代码如下所示:

H档:

class MyClass
{
    public:
    HINSTANCE hInstance;
    HWND hWnd;

    int APIENTRY WinMainProc(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
    static LRESULT CALLBACK WinProcWraper(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
    LRESULT CALLBACK WinProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
    ATOM MyClassRegisterClass(HINSTANCE hInstance); 
};

CPP 文件:

LRESULT CALLBACK MyClass::WinProcWraper(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    if (WM_NCCREATE == message)
    {
        SetWindowLong (hWnd, GWL_USERDATA,  (long)((CREATESTRUCT*) lParam)->lpCreateParams);
        return TRUE;
    }

    return ((MyClass*) GetWindowLong (hWnd, GWL_USERDATA))->WinProc (hWnd, message, wParam, lParam);
}

int APIENTRY MyClass::WinMainProc(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    MSG msg;
    MyClass.hInstance=hInstance;
    MyClass.MyClassRegisterClass(hInstance);

    //MY PROBLEM IS HERE: cannot convert parameter 4 to int
    HWND hWnd = CreateDialog(hInstance,MAKEINTRESOURCE(IDD_MAIN_DIALOG), 0, (DLGPROC)MyClass::WinProc);

    ShowWindow(hWnd,nCmdShow); 
    UpdateWindow(hWnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
    
        if (!IsWindow(hWnd) || !IsDialogMessage(hWnd,&msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return  static_cast<int>(msg.wParam);
}

LRESULT CALLBACK MyClass::WinProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    (...)
}

调用 CreateDialog() 时,出现以下错误:

cannot convert from 'long (__stdcall MyClass::*)(struct HWND__ *,unsigned int,unsigned int,long)'
                 to 'long (__stdcall          *)(struct HWND__ *,unsigned int,unsigned int,long)'

在这种情况下如何进行正确的类型转换?

让我们从错误消息开始。您正在尝试使用 非静态 class 方法 WinProc() 作为您的 CreateDialog() 回调。这是行不通的。您不厌其烦地实现了 static class 方法 WinProcWraper(),它调用了 WinProc(),但实际上您并没有使用它。您需要使用 WinProcWraper() 作为 CreateDialog().

的实际 window 过程

此外,如果它的签名开头是正确的(而你的签名不正确),则在将其传递给 CreateDialog() 时无需进行类型转换 WinProcWraper

修复后,您还有其他问题:

  • 您正在建模的 是为 CreateWindow/Ex() 的 window 程序设计的,但您使用的是 CreateDialog(),它有不同的要求。

  • 由于您使用的是 CreateDialog() 而不是 CreateWindow/Ex(),因此 WinProcWraper()WinProc() 需要 return 一个 BOOL 而不是 LRESULT。如果需要将实际的 LRESULT 值 return 编辑到系统,WinProc() 将需要为此目的使用 SetWindowLong(DWL_MSGRESULT)

  • WinProcWraper() 期望收到一条 WM_NCCREATE 消息以向其传递一个 MyClass* 指针,然后它将分配给 HWND。但是,由于您使用的是 CreateDialog()WinProcWraper() 将不会收到任何 WM_(NC)CREATE 消息,而是会收到一条 WM_INITDIALOG 消息。即使这样,CreateDialog() 也不允许您将任何用户定义的值传递给 window 过程,因此您不能传递 WinProcWraper() 需要的 MyClass* 指针.为此,您需要使用 CreateDialogParam()

  • WM_INITDIALOG 可能不是 window 收到的第一条消息,只是可以让您访问您传递给的 MyClass* 指针的第一条消息CreateDialogParam()WinProcWraper() 需要处理它可以在接收到对 MyClass* 指针的访问权限之前接收消息的可能性。

  • 您正在使用 SetWindowLong()。如果为 64 位编译,您的代码将无法运行。使用 SetWindowLongPtr() 代替,即使对于 32 位代码也是如此。

话虽如此,试试这个:

class MyClass
{
public:
    HINSTANCE hInstance;
    HWND hWnd;

    int APIENTRY WinMainProc(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
    BOOL CALLBACK WinProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
    ATOM MyClassRegisterClass(HINSTANCE hInstance); 

private:
    static BOOL CALLBACK WinProcWraper(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};
BOOL CALLBACK MyClass::WinProcWraper(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    MyClass *cls;

    if (WM_INITDIALOG == uMsg)
    {
        cls = reinterpret_cast<MyClass*>(lParam);
        SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(cls));
    }
    else
        cls = reinterpret_cast<MyClass*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));

    if (cls)
        return cls->WinProc(uMsg, wParam, lParam);

    return FALSE;
}

int APIENTRY MyClass::WinMainProc(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    MSG msg;
    MyClass.hInstance = hInstance;
    MyClass.MyClassRegisterClass(hInstance);

    HWND hWnd = CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_MAIN_DIALOG), NULL, MyClass::WinProcWraper, reinterpret_cast<LPARAM>(this));
    if (!hWnd)
        return -1;

    ShowWindow(hWnd, nCmdShow); 
    UpdateWindow(hWnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!IsWindow(hWnd) || !IsDialogMessage(hWnd, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return static_cast<int>(msg.wParam);
}

BOOL CALLBACK MyClass::WinProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    ...

    if (needed)
        SetWindowLongPtr(hWnd, DWLP_MSGRESULT, ...);

    return ...; // TRUE or FALSE as needed...
}