不再需要在子类化之后恢复GWLP_USERDATA
Restoring GWLP_USERDATA after subclassing is no longer necessary
我创建了一个Subclass类,并在其中使用SetWindowLongPtr()
将GWLP_USERDATA
设置为包含指向派生类的指针,我使用该派生类来分发消息。
因此,如果我使用SetWindowLongPtr(CurrentWindow, GWLP_USERDATA, (LONG_PTR)Data);
,我该如何恢复当我不再想子类化时,旧的数据?MSDN说初始值为零,所以我应该使用SetWindowLongPtr(CurrentWindow, GWLP_USERDATA, (LONG_PTR)NULL);
函数吗?
编辑:实际上,我使用SetProp、GetProp、RemoveProp找到了解决这个问题的方法,现在我不需要担心其他函数的问题。Bellow是我为那些可能需要它的人准备的代码:
#ifndef WIN32_SUBCLASS_CLASS_H
#define WIN32_SUBCLASS_CLASS_H
#include <Windows.h>
#include <tchar.h>
class SubclassWindow
{
public:
static LRESULT CALLBACK stWinSubclassHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
SubclassWindow* pWnd;
pWnd = (SubclassWindow*)GetProp(hwnd, TEXT("Subclass"));
if (pWnd)
return pWnd->WinSubclassHandler(hwnd, uMsg, wParam, lParam);
else
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
BOOL Subclass(SubclassWindow* Data, HWND hwnd)
{
if (Subclassed == TRUE || CurrentWindow != NULL)
return 0;
CurrentWindow = hwnd;
Subclassed = TRUE;
OriginalProc = (WNDPROC)GetWindowLongPtr(hwnd, GWL_WNDPROC);
SetProp(CurrentWindow, TEXT("Subclass"), (HANDLE)Data);
SetWindowLongPtr(CurrentWindow, GWL_WNDPROC, (LONG_PTR)SubclassWindow::stWinSubclassHandler);
return 1;
}
BOOL RemoveSubclass()
{
if (OriginalProc == NULL || CurrentWindow == NULL)
return 0;
Subclassed = FALSE;
RemoveProp(CurrentWindow, TEXT("Subclass"));
SetWindowLongPtr(CurrentWindow, GWL_WNDPROC, (LONG_PTR)OriginalProc);
return 1;
}
BOOL IsSubclassed() { return Subclassed; }
protected:
HWND CurrentWindow;
WNDPROC OriginalProc;
BOOL Subclassed;
SubclassWindow()
{
CurrentWindow = NULL;
OriginalProc = NULL;
Subclassed = FALSE;
}
virtual LRESULT CALLBACK WinSubclassHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
};
#endif
SetWindowLongPtr()
返回旧值,因此保存它,然后在稍后调用SetWindowLongPtr()
删除子类时恢复它。
话虽如此,将GWLP_USERDATA
替换为不是您自己创建的窗口是危险的。您不知道该窗口是否已将GWLP_USERDATA
用于其自身目的。改为使用SetWindowSubclass()
,它旨在解决这个问题:
class SubclassWindow
{
public:
static LRESULT CALLBACK stWinSubclassHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
SubclassWindow* pWnd = (SubclassWindow*) dwRefData;
if ((pWnd) && (uIdSubclass == 1))
return pWnd->WinSubclassHandler(hwnd, uMsg, wParam, lParam);
else
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
BOOL Subclass(SubclassWindow* Data, HWND hwnd)
{
if (CurrentWindow != NULL)
return FALSE;
if (!SetWindowSubclass(hwnd, &stWinSubclassHandler, 1, (DWORD_PTR)Data))
return FALSE;
CurrentWindow = hwnd;
return TRUE;
}
BOOL RemoveSubclass()
{
if (CurrentWindow == NULL)
return FALSE;
if (!RemoveWindowSubclass(CurrentWindow, &stWinSubclassHandler, 1))
return FALSE;
CurrentWindow = NULL;
return TRUE;
}
BOOL IsSubclassed() { return (CurrentWindow != NULL); }
protected:
HWND CurrentWindow;
SubclassWindow()
{
CurrentWindow = NULL;
}
virtual LRESULT CALLBACK WinSubclassHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
};
如果派生类需要默认处理任何给定的消息,那么您需要做的唯一更改是让它们的WinSubclassHandler()
实现调用DefSubclassProc()
而不是DefWindowProc()
(BTW调用的API错误-您应该调用CallWindowProc(OriginalProc, ...)
,这样子类替换的上一个窗口过程就有机会处理消息)。为此,我建议将该调用封装在SubclassWindow
类内部,以向后代隐藏该细节,例如:
class SubclassWindow
{
...
protected:
...
static LRESULT DefaultHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
...
};
class MySubclass : public SubclassWindow
{
protected:
virtual LRESULT CALLBACK WinSubclassHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
...
return DefaultHandler(hWnd, uMsg, wParam, lParam);
}
};
或者(这将允许后代更好地从后代派生):
class SubclassWindow
{
...
protected:
...
virtual LRESULT CALLBACK WinSubclassHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
...
};
class MySubclass1 : public SubclassWindow
{
protected:
virtual LRESULT CALLBACK WinSubclassHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
...
return SubclassWindow::WinSubclassHandler(hWnd, uMsg, wParam, lParam);
}
};
class MySubclass2 : public MySubclass1
{
protected:
virtual LRESULT CALLBACK WinSubclassHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
...
return MySubclass1::WinSubclassHandler(hWnd, uMsg, wParam, lParam);
}
};
class MySubclass3 : public MySubclass2
{
protected:
virtual LRESULT CALLBACK WinSubclassHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
...
return MySubclass2::WinSubclassHandler(hWnd, uMsg, wParam, lParam);
}
};
正如Hans所提到的,如果有东西需要恢复,你的状态就不好了。这意味着原始的WNDPROC
,它仍然被用于任何你不会处理的东西,将在那里期待另一个值。
也就是说,SetWindowLongPtr返回旧值,所以您总是可以先调用GetWindowLongPtr来获得原始值。这个值可能仍然是零,但至少你会确信。
- 用C++将哈希表写入文件并从文件中恢复
- Opencv 恢复到比我设置的更高的分辨率
- 变量在使用赋值语句赋值后恢复为以前的值
- 在信号处理程序中捕获C++未处理的异常并恢复应用程序
- 删除所有字符串后如何恢复 QStringList?
- 当对套接字 send() 的同步调用由于连接另一端丢失而被阻止时,如何恢复?
- 如何仅使用一次固定<<设置精度(2)?或者至少恢复到默认行为?
- "co_yield"是否可以在恢复协程时从调用方返回值?
- OpenSSL C API:如何在程序exec()之后恢复TLS连接?
- 通过指针恢复对数组的引用.UB与否?
- 从不同进程中的另一个线程挂起/恢复线程或进程
- 如何在 XML 中正确存储原始字节数据并恢复它?
- ExtTextOut 文本的持续闪烁,在一段时间后,文本将恢复为默认字体
- 模板化类中运算符 + 重载的值的恢复
- C++ 线程创建/删除与线程停止/恢复
- ZMQ::send() 抛出异常并终止 QNX 进程.为什么以及如何从中恢复?
- Windows桌面程序保存您的计算机会话 - 基于程序崩溃时的恢复会话
- xSemaphoreTake 在调用 xSemaphoreGive 后不会恢复任务
- 将类型映射到整数值后,如何在给定整数值的情况下恢复类型?
- 恢复ASIO无堆栈Coroutine