安全删除窗口子类?

时间:2010-03-15 10:07:33

标签: c++ winapi subclassing

我正在尝试使用全局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的主窗口已经以某种方式已经被子类化了。我想以下情况正在发生:

  1. 窗口是使用自己的基本WndProc创建的,并且具有焦点
  2. 我的应用程序对窗口进行子类化,存储原始的WndProc
  3. Opera子类化自己的窗口
  4. 当窗口失去焦点时,我恢复原来的WndProc,从而忽略第二个WndProc
  5. 真的可以吗?还有其他解释吗?

1 个答案:

答案 0 :(得分:8)

这可能发生在Raymond Chen writes

  

考虑一下如果其他人在“......做东西......”部分中将窗口子类化了会发生什么。当我们取消分类窗口时,我们将删除两个子类,即我们安装的子类,以及我们之后安装的子类。如果另一个子类分配了内存(这是非常常见的),那么该内存就会被泄露,除了子类无法做任何尝试。

他继续解决方案:

  

这是一个非常繁琐的过程,因此shell团队编写了一些帮助函数来为您完成所有这些。 SetWindowSubclass函数执行安装子类过程的所有繁琐工作,记住前一个过程,并将引用数据传递给您提供的子类过程。您可以使用DefSubclassProc函数将消息转发到上一个子类过程,完成后,使用RemoveWindowSubclass函数将自己从链中删除。如果你不是链条顶端的窗口进程,那么RemoveWindowSubclass会做所有正确的工作。