Безопасно удалить подклассы окна? - PullRequest
4 голосов
/ 15 марта 2010

Я пытаюсь создать подкласс текущего окна в системе Windows, используя глобальный хук CBT. Это связано с тем, что происходит в этом вопросе , но ошибка другая.

Что происходит, когда действует это подклассирование, так это то, что главное окно 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

Может ли это быть действительно так? Есть ли другие объяснения?

1 Ответ

8 голосов
/ 15 марта 2010

Это может произойти, как Рэймонд Чен пишет :

Подумайте, что произойдет, если кто-то еще вложил в окно подкласс во время раздела "... делать вещи ...". Когда мы отменили блокировку окна, мы удалили два подкласса: тот, который мы установили, и тот, который был установлен после нас. Если другой подкласс выделил память (что очень распространено), то эта память утекла, в дополнение к тому, что подкласс не смог сделать то, что он пытался сделать.

Он продолжает решение:

Это довольно громоздкий процесс, поэтому команда разработчиков написала несколько вспомогательных функций, чтобы сделать все это за вас. Функция SetWindowSubclass выполняет всю основную работу по установке процедуры подкласса, запоминанию предыдущей и передаче справочных данных в предоставленную вами процедуру подкласса. Вы используете функцию DefSubclassProc для пересылки сообщения в предыдущую процедуру подкласса, а когда вы закончите, вы используете функцию RemoveWindowSubclass, чтобы удалить себя из цепочки. RemoveWindowSubclass выполняет всю работу, чтобы делать правильные вещи, если вы не занимаетесь оконным процессором в верхней части цепочки.

...