Вот моя текущая настройка: у меня есть 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.