GetWindowLongPtr返回垃圾

问题描述:

我正在开发一个Win32对话框包装器。GetWindowLongPtr返回垃圾

.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,我在WM_INITDIALOG中将HWND保存为this。但是,只要消息不是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_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

对话框模板是否真的为用户数据指定了额外的空间?对话本身是否为了自己的目的而使用这些数据?总是有:['SetProp'](https://msdn.microsoft.com/en-us/library/windows/desktop/ms633568%28v=vs.85%29.aspx) – theB

+0

@theB你的意思是? https://msdn.microsoft.com/en-us/library/windows/desktop/ms645398(v=vs.85).aspx – user3144238

+1

您可以发布您用于创建此对话框实例的代码吗? – andlabs

我拼凑起来的,你已经证明我们的代码,并能够创建工作示例:

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,就像我在这里做的一样。

+0

非常感谢,按照你的建议,我开始工作,每次打电话给虚拟功能。我不知道它为什么开始工作,但遵循你的建议。 – user3144238