Как отсоединить глобальный хук от другого процесса? - PullRequest
0 голосов
/ 15 октября 2018

Вот моя текущая настройка: у меня есть C ++ DLL, которая подключает одну из своих функций глобально к каждому процессу, запущенному на компьютере.Перехват выполняется в DLLMain с использованием функции SetWindowsHookEx winapi, а я подключаюсь к событиям WH_CBT и WH_SHELL.У меня также есть приложение на C #, которое загружает DLL с помощью p / invoke (LoadLibrary()), которая запускает установку хуков с DLLMain.Обработчики в DLL отправляют информацию о событиях в приложение C # через именованный канал.

Основываясь на том, что я прочитал в документальном фильме Microsoft , эти события будут обрабатываться на целипоток процесса, и должен быть установлен отдельной C ++ DLL (в отличие от WH_MOUSE_LL и WH_KEYBOARD_LL, которые могут быть установлены любым приложением, даже прямо из приложения C # с использованием p / invoke ).

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

Поскольку обработчик не работает в моем приложении, а вместо этогооно внедряется в другие процессы, запущенные на моем компьютере, приложение C # не может просто вызвать UnhookWindowsHookEx или FreeLibrary, потому что указатель обработчика события принадлежит другим процессам.

Вопрос:

Как запустить подпрограмму отцепления из управляемого приложения, которое гарантирует, что DLL больше не используется каким-либо процессом?

Вот что я сделалпробовал:

Единственное решение, которое я хотел бы найти, - это создать событие выхода (с CreateEvent), и каждый раз, когда обработчик получает сообщение WH_CBT или WH_SHELL, онпроверяет, установлено ли событие выхода, и в этом случае оно отсоединяет себя от процесса, которому оно принадлежит, и возвращает его до обработки сообщения.

Проблема этого подхода заключается в том, что послеЯ закрываю свое приложение и выгружаю DLL, мне нужно подождать, пока остальные процессы получат событие WH хотя бы один раз, чтобы принадлежащий им обработчик мог отцепиться сам.

Вот код DLL:

#include <windows.h>
#include <sstream>

HANDLE hTERM;
HHOOK hCBT;
HHOOK hShell;

void __declspec(dllexport) InstallHooks(HMODULE h);
void __declspec(dllexport) RemoveHooks();

int Continue()
{
    return WAIT_TIMEOUT == WaitForSingleObject(hTERM, 0);
}

LRESULT FAR PASCAL _cbtProc(int c, WPARAM w, LPARAM l)
{
    if (!Continue()) { RemoveHooks(); return 0; }   
    // Handling the message ...
    return CallNextHookEx(0, c, w, l);
}

LRESULT FAR PASCAL _shellProc(int c, WPARAM w, LPARAM l)
{
    if (!Continue()) { RemoveHooks(); return 0; }
    // Handling the message ...
    return CallNextHookEx(0, c, w, l);
}

void InstallHooks(HMODULE h)
{
    hTERM = OpenEvent(EVENT_ALL_ACCESS, 0, __TEXT("{0C3ED513-F38C-4996-8130-F9A3C93D890B}"));
    if (!Continue())
        return;
    hCBT = SetWindowsHookEx(WH_CBT, _cbtProc, h, 0);
    hShell = SetWindowsHookEx(WH_SHELL, _shellProc, h, 0);
}

void RemoveHooks()
{
    UnhookWindowsHookEx(hCBT);
    UnhookWindowsHookEx(hShell);
    if (hTERM) CloseHandle(hTERM); hTERM = 0;
}

int FAR PASCAL DllMain(HMODULE h, DWORD r, void* p)
{
    switch (r)
    {
        case DLL_PROCESS_ATTACH: InstallHooks(h); break;
        case DLL_PROCESS_DETACH: RemoveHooks(); break;
        default: break;
    }
    return 1;
}

В исходном коде управляемого приложения C # нет ничего особенного, потому что единственное, что он делает, это вызывает при запуске LoadLibrary, обрабатывает сообщения, поступающие из канала, и, наконец, устанавливаетпри необходимости завершите событие другим вызовом p / invoke.

1 Ответ

0 голосов
/ 16 октября 2018

" Перехват выполняется в DLLMain " - это абсолютно неправильное место для этого.

Каждый раз, когда DLL загружается в новый процесс, она собирается установитьновый набор хуков Shell / CBT, которые вам НЕ нужны / не нужны.Вам нужен только 1 комплект.

Правильное решение состоит в том, чтобы ваша DLL экспортировала свои функции InstallHooks() и RemoveHooks(), а затем ТОЛЬКО ваше приложение C # вызывало их ПОСЛЕ того, как оно загрузилосьDLL в себя.Этот единственный набор хуков будет обрабатывать загрузку DLL во все запущенные процессы по мере необходимости, БЕЗ необходимости каждый раз вызывать SetWindowsHookEx().

Кроме того, НЕ вызывайте UnhookWindowsHookEx() из внутри .перехватить обратные вызовы сами.Перед выходом из приложения C # оно должно вызвать RemoveHooks(), что может сигнализировать о событии hTerm перед вызовом UnhookWindowsHookEx().Обратные вызовы должны просто завершиться, если Continue() вернет false, не более того.Но НЕ Пропускайте вызов CallNextHookEx(), даже если Continue() возвращает false, так как другие приложения могут устанавливать дополнительные хуки, и вы НЕ хотите их нарушать.

Вместо этого попробуйте что-то подобное:

#include <windows.h>

HMODULE hModule = NULL;
HANDLE hTERM = NULL;
HHOOK hCBT = NULL;
HHOOK hShell = NULL;

static bool Continue()
{
    return (WAIT_TIMEOUT == WaitForSingleObject(hTERM, 0));
}

LRESULT CALLBACK _cbtProc(int code, WPARAM wParam, LPARAM lParam)
{
    if (Continue()) {
        // Handle the message ...
    }
    return CallNextHookEx(NULL, code, wParam, lParam);
}

LRESULT CALLBACK _shellProc(int code, WPARAM wParam, LPARAM lParam)
{
    if (Continue()) {
        // Handle the message ...
    }
    return CallNextHookEx(NULL, code, wParam, lParam);
}

__declspec(dllexport) BOOL WINAPI InstallHooks()
{
    if (!Continue())
        return FALSE;

    if (!hCBT)
        hCBT = SetWindowsHookEx(WH_CBT, _cbtProc, hModule, 0);

    if (!hShell)
        hShell = SetWindowsHookEx(WH_SHELL, _shellProc, hModule, 0);

    return ((hCBT) && (hShell)) ? TRUE : FALSE;
}

__declspec(dllexport) void WINAPI RemoveHooks()
{
    if (hTERM)
        SetEvent(hTERM);

    if (hCBT) {
        UnhookWindowsHookEx(hCBT);
        hCBT = NULL;
    }

    if (hShell) {
        UnhookWindowsHookEx(hShell);
        hShell = NULL;
    }
}

BOOL WINAPI DllMain(HMODULE hinstDLL, DWORD fdwReason, void* lpvReserved)
{
    hModule = hinstDLL;

    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            hTERM = CreateEvent(NULL, TRUE, FALSE, TEXT("{0C3ED513-F38C-4996-8130-F9A3C93D890B}"));
            if (!hTERM) return FALSE;
            break;

        case DLL_PROCESS_DETACH:
            if (hTERM) {
                CloseHandle(hTERM);
                hTERM = NULL;
            }
            break;
    }

    return TRUE;
}

Затем ваше приложение на C # может просто загрузить DLL и при необходимости вызвать InstallHooks() и RemoveHooks().Например, используя вызовы PInvoke при запуске и завершении работы приложения, соответственно.

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