我正在尝试使用全局CBT钩子在Windows系统上子类化当前关注的窗口。这与this question中发生的情况有关,但错误不同。
当这个子类化生效时会发生什么,是Opera的(版本10.50)主窗口被阻止显示。 Opera有一个“启动画面”,您需要在主窗口中单击“开始”以显示Opera未正确关闭后显示的内容。每当弹出此窗口时,Opera的主窗口都不会显示。如果Opera正常关闭,并且没有显示此启动画面,则主窗口将按原样显示。
HHOOK hHook;
HWND hWndSubclass = 0;
void SubclassWindow(HWND hWnd)
{
Unsubclass();
FARPROC lpfnOldWndProc = (FARPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LPARAM)SubClassFunc);
SetProp(hWnd, L"PROP_OLDWNDPROC", lpfnOldWndProc);
hWndSubclass = hWnd;
}
void Unsubclass()
{
if (hWndSubclass != 0 && IsWindow(hWndSubclass))
{
FARPROC lpfnOldWndProc = (FARPROC)GetProp(hWndSubclass, L"PROP_OLDWNDPROC");
RemoveProp(hWndSubclass, L"PROP_OLDWNDPROC");
SetWindowLongPtr(hWndSubclass, GWLP_WNDPROC, (LPARAM)lpfnOldWndProc);
hWndSubclass = 0;
}
}
static LRESULT CALLBACK SubClassFunc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_MOVING)
{
// do something irrelevant
}
else if (message == WM_DESTROY)
{
Unsubclass();
}
FARPROC lpfnOldWndProc = (FARPROC)GetProp(hWndSubclass, L"PROP_OLDWNDPROC");
return CallWindowProc((WNDPROC)lpfnOldWndProc, hWndSubclass, message, wParam, lParam);
}
static LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HCBT_SETFOCUS && hWndServer != NULL)
{
SubclassWindow((HWND)wParam);
}
if (nCode < 0)
{
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
return 0;
}
BOOL APIENTRY DllMain( HINSTANCE hInstance,
DWORD Reason,
LPVOID Reserved
)
{
switch(Reason)
{
case DLL_PROCESS_ATTACH:
hInst = hInstance;
return TRUE;
case DLL_PROCESS_DETACH:
Unsubclass();
return TRUE;
}
return TRUE;
}
我怀疑Opera的主窗口已经以某种方式已经被子类化了。我想以下情况正在发生:
真的可以吗?还有其他解释吗?
答案 0 :(得分:8)
这可能发生在Raymond Chen writes:
考虑一下如果其他人在“......做东西......”部分中将窗口子类化了会发生什么。当我们取消分类窗口时,我们将删除两个子类,即我们安装的子类,以及我们之后安装的子类。如果另一个子类分配了内存(这是非常常见的),那么该内存就会被泄露,除了子类无法做任何尝试。
他继续解决方案:
这是一个非常繁琐的过程,因此shell团队编写了一些帮助函数来为您完成所有这些。
SetWindowSubclass
函数执行安装子类过程的所有繁琐工作,记住前一个过程,并将引用数据传递给您提供的子类过程。您可以使用DefSubclassProc
函数将消息转发到上一个子类过程,完成后,使用RemoveWindowSubclass
函数将自己从链中删除。如果你不是链条顶端的窗口进程,那么RemoveWindowSubclass
会做所有正确的工作。