【发布时间】: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 的消息到达,并且我得到带有 GetWindowLongPtr 的 this 指针,它就会返回一个垃圾 dlg 类,其 vtable 已损坏。我使用 vtable 调用正确的处理函数。结果,我的代码在 WM_COMMAND 处理程序的第一行崩溃。以下是调试器显示为this 的值:
为什么 GetWindowLongPtr 返回垃圾?
顺便说一句,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 而不是 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 -
@theB 你是说这个吗? msdn.microsoft.com/en-us/library/windows/desktop/…
-
你能贴出你用来创建这个对话框实例的代码吗?
-
@andlabs 将其添加为编辑
-
DLGPROC(dlg::DlgProcTmp)是干什么用的?如果在没有强制转换/转换的情况下无法编译,则您可能传递了带有错误签名的函数指针。