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
值:
为什么GetWindowLongPtr返回垃圾?
BTW,getwinlong
是GetWindowLongPtr
的包装,而setwinlong
是SetWindowLongPtr
的包装。以下是它们的实现:
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);
}
我拼凑起来的,你已经证明我们的代码,并能够创建工作示例:
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,就像我在这里做的一样。
非常感谢,按照你的建议,我开始工作,每次打电话给虚拟功能。我不知道它为什么开始工作,但遵循你的建议。 – user3144238
对话框模板是否真的为用户数据指定了额外的空间?对话本身是否为了自己的目的而使用这些数据?总是有:['SetProp'](https://msdn.microsoft.com/en-us/library/windows/desktop/ms633568%28v=vs.85%29.aspx) – theB
@theB你的意思是? https://msdn.microsoft.com/en-us/library/windows/desktop/ms645398(v=vs.85).aspx – user3144238
您可以发布您用于创建此对话框实例的代码吗? – andlabs