GetWindowLongPtr返回垃圾

时间:2016-06-11 15:11:21

标签: c++ winapi dialog

我正在开发一个Win32对话框包装器。

.h文件

class dlg {
    static INT_PTR CALLBACK DlgProcTmp(HWND hwnd, 
                                       UINT wm, WPARAM wp, LPARAM lp);
    INT_PTR CALLBACK DlgProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp);

    bool ismodal;
protected:
    HWND hwndDlg;
    int id;
public:
    virtual void oncreate(const widget &w) { }
    virtual void oncmd(const widget &w, int code, int idCntrl, HWND hwnd) { }
    virtual void onclose(const widget &w) { }
    dlg() { }
    dlg(int id);
    INT_PTR domodal(HWND hwndOwner = nullptr);
    widget domodeless(HWND hwndOwner = nullptr, int cmdshow = SW_SHOW);
};

.cpp文件

INT_PTR dlg::DlgProcTmp(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
    dlg *This;

    if (wm == WM_INITDIALOG) {
        This = (dlg *) lp;
        setwinlong(hwnd, DWLP_USER, This);
        This->oncreate(widget(hwnd));

        return TRUE;
    }
    if ((This = getwinlong<dlg *>(hwnd, DWLP_USER)) != nullptr)
        return This->DlgProc(hwnd, wm, wp, lp);
    return FALSE;
}
INT_PTR dlg::DlgProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
    this->hwndDlg = hwnd;
    switch (wm) {
        case WM_COMMAND:
            this->oncmd(widget(hwnd), HIWORD(wp), LOWORD(wp), (HWND) lp);
            if (this->ismodal)
                EndDialog(hwnd, LOWORD(wp));
            else
                DestroyWindow(hwnd);
            return TRUE;
        case WM_DESTROY:
            this->onclose(widget(hwnd));
            return TRUE;
    }
    return FALSE;
}

该对话框在domodal中通过DialogBoxParam来电创建。最后一个参数是this指针,然后我从WM_INITDIALOG消息的lparam中检索它。为了允许在不同消息之间使用this,我将this与HWND一起保存在WM_INITDIALOG中。但是,每当消息没有WM_INITDIALOG到达,并且我得到this指针GetWindowLongPtr时,它返回一个垃圾dlg类,其vtable已损坏。我使用vtable来调用正确的处理函数。结果,我的代码在WM_COMMAND处理程序的第一行崩溃。以下是调试器显示为this的值:

enter image description here

为什么GetWindowLongPtr会返回垃圾?

BTW,getwinlongGetWindowLongPtr的包装,而setwinlongSetWindowLongPtr的包装。以下是他们的实现:

template <class TYPE> static TYPE getwinlong(HWND hwnd, int idx)
{
    return (TYPE) GetWindowLongPtr(hwnd, idx);
}
template <class TYPE> static void setwinlong(HWND hwnd, int idx, TYPE val)
{
    SetWindowLongPtr(hwnd, idx, (LONG_PTR) val);
}

我已经看过很多关于GetWindowLongPtr如何在Win64上失败的帖子,如果你转换为LONG而不是LONG_PTR,如https://blogs.msdn.microsoft.com/oldnewthing/20131226-00/?p=2263。我相信我的问题不同。

编辑:@andlabs想要创建对话框的代码:

INT_PTR dlg::domodal(HWND hwndOwner)
{
    this->ismodal = true;
    return DialogBoxParam(gethinst(hwndOwner), 
        MAKEINTRESOURCE(id), hwndOwner, dlg::DlgProcTmp, 
        (LPARAM) this);
}

1 个答案:

答案 0 :(得分:2)

我拼凑了你向我们展示的代码,并且能够创建一个工作样本:

dlg.h

#pragma once
#include <Windows.h>

class dlg {
    static INT_PTR CALLBACK DlgProcTmp(HWND hwnd, 
                                       UINT wm, WPARAM wp, LPARAM lp);
    INT_PTR CALLBACK DlgProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp);

    bool ismodal;
protected:
    HWND hwndDlg;
    int id;
public:
    //virtual void oncreate(const widget &w) { }
    //virtual void oncmd(const widget &w, int code, int idCntrl, HWND hwnd) { }
    //virtual void onclose(const widget &w) { }
    dlg() { }
    dlg(int id) : id(id) { }
    INT_PTR domodal(HWND hwndOwner = nullptr);
    //widget domodeless(HWND hwndOwner = nullptr, int cmdshow = SW_SHOW);
};

dlg.cpp

#include "dlg.h"


template <class TYPE> static TYPE getwinlong(HWND hwnd, int idx)
{
    return (TYPE) GetWindowLongPtr(hwnd, idx);
}
template <class TYPE> static void setwinlong(HWND hwnd, int idx, TYPE val)
{
    SetWindowLongPtr(hwnd, idx, (LONG_PTR) val);
}


INT_PTR dlg::DlgProcTmp(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
    dlg *This;

    if (wm == WM_INITDIALOG) {
        This = (dlg *) lp;
        setwinlong(hwnd, DWLP_USER, This);
        //This->oncreate(widget(hwnd));

        return TRUE;
    }
    if ((This = getwinlong<dlg *>(hwnd, DWLP_USER)) != nullptr)
        return This->DlgProc(hwnd, wm, wp, lp);
    return FALSE;
}
INT_PTR dlg::DlgProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
    this->hwndDlg = hwnd;
    switch (wm) {
        case WM_COMMAND:
            //this->oncmd(widget(hwnd), HIWORD(wp), LOWORD(wp), (HWND) lp);
            if (this->ismodal)
                EndDialog(hwnd, LOWORD(wp));
            else
                DestroyWindow(hwnd);
            return TRUE;
        case WM_DESTROY:
            return TRUE;
    }
    return FALSE;
}

INT_PTR dlg::domodal(HWND hwndOwner)
{
    this->ismodal = true;
    return DialogBoxParam(GetModuleHandle(NULL), 
        MAKEINTRESOURCE(id), hwndOwner, dlg::DlgProcTmp, 
        (LPARAM) this);
}

的main.cpp

int APIENTRY _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
{
    dlg myDialog(IDD_DIALOG);
    myDialog.domodal();

    return 0;
}

您会注意到已经注释掉了相当多的代码,特别是省略了对虚拟消息处理函数(onXXX)的声明和调用。我这样做是因为我没有widget类型的定义,我认为它与你的实际问题没有密切关系。

原则上必须如此,因为只要不调用那些虚拟消息处理函数,代码就能正常工作。

然后我在widget的存根类中添加如下:

class widget
{
public:
   widget(HWND hWnd)
      : m_hWnd(hWnd)
   { }

   HWND m_hWnd;
};

并取消注释oncmd函数以及WM_COMMAND处理程序中对它的调用。同样,代码编译并正常工作。所以我不知道你遇到了什么问题。它必须在widget类或您未向我们展示的其他代码中。

我会对代码进行至少一次调整。在hwndDlg函数内部分配DlgProcTemp成员变量,而不是等到DlgProc函数。所以,这样做:

INT_PTR dlg::DlgProcTmp(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
    dlg *This;

    if (wm == WM_INITDIALOG) {
        This = (dlg *) lp;
        setwinlong(hwnd, DWLP_USER, This);
        This->hwndDlg = hwnd;
        This->oncreate(widget(hwnd));

        return TRUE;
    }
    if ((This = getwinlong<dlg *>(hwnd, DWLP_USER)) != nullptr)
        return This->DlgProc(hwnd, wm, wp, lp);
    return FALSE;
}
INT_PTR dlg::DlgProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
    switch (wm) {
        case WM_COMMAND:
            this->oncmd(widget(hwnd), HIWORD(wp), LOWORD(wp), (HWND) lp);
            if (this->ismodal)
                EndDialog(hwnd, LOWORD(wp));
            else
                DestroyWindow(hwnd);
            return TRUE;
        case WM_DESTROY:
            return TRUE;
    }
    return FALSE;
}

还强烈考虑using C++-style casts in preference to C-style casts

如果您仍然无法使代码生效,则必须编辑问题Minimal, Complete, and Verifiable example,就像我在此处所做的那样。