Почему происходит сбой кода подклассов этого окна? - PullRequest
1 голос
/ 28 августа 2009

Я пытаюсь создать подкласс для окна, которое в данный момент имеет фокус. Я делаю это, отслеживая события HCBT_ACTIVATE с помощью ловушки CBT, а также устанавливаю и сбрасываю WndProc сфокусированных и ранее сфокусированных окон.

Проблема в том, что он работает только тогда, когда у меня установлена ​​точка останова где-то в коде .

Если точки останова не существует, то после выхода из моего приложения все окна, которые я вложил в подклассы, вылетают по порядку, даже если я удалил подкласс и восстановил исходный WndProc.

Я подтвердил, что Unsubclass() вызывается всякий раз, когда мое приложение закрывается.

// code extracts
HINSTANCE hInst;
HHOOK hHook;

#pragma data_seg(".shared")
HWND hWndSubclass = 0;
FARPROC lpfnOldWndProc = NULL;
#pragma data_seg()
#pragma comment(linker, "/section:.shared,rws")

void Unsubclass()
{
    // if the window still exists
    if (hWndSubclass != 0 && IsWindow(hWndSubclass))
    {
        SetWindowLongPtr(hWndSubclass, GWLP_WNDPROC, (LPARAM)lpfnOldWndProc);
        hWndSubclass = 0;
    }
}

static LRESULT CALLBACK SubClassFunc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    if (message == WM_MOVING)
    {
        // this is just test code so I can see it works (it does)
        RECT* r = (RECT*)lParam;
        r->right = r->left + 500;
        r->bottom = r->top + 500;
        return TRUE;
    }
    else if (message == WM_DESTROY)
    {
        Unsubclass();
    }
    return CallWindowProc((WNDPROC)lpfnOldWndProc, hWndSubclass, message, wParam, lParam);
}

void SubclassWindow(HWND hWnd)
{
    // remove the subclassing for the old window
    Unsubclass();
    // subclass the new window
    lpfnOldWndProc = (FARPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LPARAM)SubClassFunc);
    hWndSubclass = hWnd;
}

static LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HCBT_ACTIVATE)
    {
        SubclassWindow((HWND)wParam);
    }
    return 0;
}

// ... code that initializes the CBT proc
__declspec(dllexport) BOOL Setup()
{
    hHook = SetWindowsHookEx(WH_CBT, CBTProc, hInst, 0);
}

__declspec(dllexport) BOOL Teardown()
{
    UnhookWindowsHookEx(hHook);
    Unsubclass();
}

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;
}

Ответы [ 4 ]

3 голосов
/ 03 сентября 2009

Ваши проблемы зависят от нескольких направлений:

  • UnHookWindowsHook не выгружает внедренные dll, все, что он делает, это удаляет процедуру ловушки. Если библиотеки должны быть выгружены, они должны изобретать какой-то механизм разгрузки.
  • SetWindowLongPtr обычно завершается ошибкой при вызове из процесса, отличного от процесса, которому принадлежит окно.

В результате нетрудно безопасно удалить хуки окон. Во-первых, указатель OldWindowProc не должен храниться в общей области данных. Затем, чтобы удалить подкласс, необходимо иметь возможность принудительно (в настоящее время) обрабатывать подклассы для выполнения не подклассов.

Что вы можете сделать, это сначала зарегистрировать новый уникальный идентификатор сообщения и поместить его в общую область с помощью RegisterWindowMessage. WM_REMOVE_HOOK.

UINT idWM_REMOVE_HOOK = RegisterWindowMessage("WM_REMOVE_HOOK");

Теперь, когда вам нужно удалить крючок,

SendMessage(hWndSubClass,idWM_REMOVE_HOOK,0,0);

В вашем подклассе proc:

if(uMsg == WM_DESTROY || uMsg == idWM_REMOVE_HOOK)
{
  Unsubclass(hwnd);
}

Удалить вызов UnSubClass в DLL_PROCESS_DETATCH. Это опасное состояние гонки, которое может привести к тому, что ваша dll будет выгружена в каком-то случайном процессе, чтобы уничтожить данные потенциально допустимого хука в другом процессе.

0 голосов
/ 05 сентября 2009

Если отладчик вызовет успешное завершение процесса, добавив точку останова, то, скорее всего, это проблема синхронизации. Возможно, ваше главное приложение закрывается и освобождает ресурсы непосредственно перед тем, как окна подкласса получают сообщения, необходимые для повторного удаления подкласса. Возможно, вы захотите дать им несколько циклов обработки, чтобы обрабатывать их собственные сообщения между отцеплением и откатом. (В Delphi вы могли бы сделать это, вызвав Application.ProcessMessages, но в вашей версии C ++? Не знаю ответа на этот вопрос.

0 голосов
/ 29 августа 2009

Вы создаете глобальный общесистемный хук в DLL. Вам нужно хранить дескриптор HHOOK и информацию о ваших подклассах в блоке разделяемой памяти, чтобы все экземпляры вашей DLL во всех запущенных процессах могли иметь к ним доступ. Ваши переменные объявлены глобальными в коде, но каждый отдельный экземпляр DLL будет иметь свою собственную локальную копию, и, таким образом, они не будут инициализированы не во всех, кроме 1 ваших экземплярах DLL (тот, который вызывает Setup ()). Вместо этого они должны использоваться глобально во всей системе.

Вы также не должны вызывать TearDown () в DLL_PROCESS_DETACH. Каждый экземпляр DLL будет вызывать TearDown (), когда завершаются их соответствующие процессы, но только тот экземпляр, который фактически вызвал Setup (), должен быть тем, который вызывает Teardown ().

0 голосов
/ 28 августа 2009

lpfnOldWndProc и hWndSubclass являются глобальными указателями. Похоже, у вас есть только один на процесс. Что если процесс создает более одного окна?

Тогда вы отмените подкласс только на последнем.

РЕДАКТИРОВАТЬ: Кроме того, почему вы разрушаете в Process DETACH?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...