【问题标题】:GetWindowLongPtr returns garbage [closed]GetWindowLongPtr 返回垃圾[关闭]
【发布时间】:2016-06-11 15:11:21
【问题描述】:

我正在开发一个 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 与WM_INITDIALOG 中的HWND 一起保存。但是,只要不是 WM_INITDIALOG 的消息到达,并且我得到带有 GetWindowLongPtrthis 指针,它就会返回一个垃圾 dlg 类,其 vtable 已损坏。我使用 vtable 调用正确的处理函数。结果,我的代码在 WM_COMMAND 处理程序的第一行崩溃。以下是调试器显示为this 的值:

为什么 GetWindowLongPtr 返回垃圾?

顺便说一句,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);
}

【问题讨论】:

  • 对话框模板是否真的为用户数据指定了额外的空间?对话本身是否出于自身目的使用该数据?总有:SetProp
  • 你能贴出你用来创建这个对话框实例的代码吗?
  • @andlabs 将其添加为编辑
  • DLGPROC(dlg::DlgProcTmp) 是干什么用的?如果在没有强制转换/转换的情况下无法编译,则您可能传递了带有错误签名的函数指针。

标签: c++ winapi dialog


【解决方案1】:

我将您展示给我们的代码拼凑在一起,并能够创建一个工作示例:

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 类或您没有向我们展示的其他代码中。

我至少会对代码进行一项调整。在DlgProcTemp 函数内部分配hwndDlg 成员变量,而不是等到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,就像我在这里所做的那样。

【讨论】:

    猜你喜欢
    • 2017-10-19
    • 1970-01-01
    • 2015-06-10
    • 2013-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-04
    • 2015-06-27
    相关资源
    最近更新 更多