如何从另一个进程解开全局钩子?
How do I unhook a global hook from another process?
这是我当前的设置:我有一个C++DLL,它将其一个函数全局挂接到计算机上运行的每个进程。挂钩是使用SetWindowsHookEx
winapi 函数在DLLMain
中完成的,我正在挂钩到WH_CBT
和WH_SHELL
事件。我还有一个 C# 应用程序,它使用 p/invoke (LoadLibrary()
( 加载 DLL,这会触发从DLLMain
安装钩子。DLL 中的处理程序通过命名管道将事件信息发送到 C# 应用。
根据我在 Microsoft 纪录片中读到的内容,这些事件将在目标进程的线程上处理,并且必须由独立的C++ DLL 安装(与 WH_MOUSE_LL 和 WH_KEYBOARD_LL 不同,它可以由任何应用程序安装,甚至可以直接从使用 p/invoke 的 C# 应用程序安装(。
到目前为止,一切正常;托管应用正在接收应有的数据。当我关闭应用程序时出现问题,因为处理程序仍然挂接,因此 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 调用设置 exit 事件。
钩子是在DLLMain中完成的" - 这是处理这个问题的完全错误的地方。
每次将DLL加载到新进程中时,它都会安装一组新的Shell/CBT钩子,这是你不希望/不需要发生的。 您只需要 1 套。
正确的解决方案是让 DLL 导出其InstallHooks()
和RemoveHooks()
函数,然后在将 DLL 加载到自身后仅让 C# 应用调用它们。 这组钩子将根据需要处理将 DLL 加载到所有正在运行的进程中,而无需每次都调用SetWindowsHookEx()
。
另外,不要从钩子回调本身内部调用UnhookWindowsHookEx()
。 在 C# 应用退出之前,它应该调用RemoveHooks()
,然后它可以在调用UnhookWindowsHookEx()
之前发出hTerm
事件的信号。 如果返回 false,则回调应该简单地退出Continue()
仅此而已。 但是,即使Continue()
返回 false,也不要跳过调用CallNextHookEx()
,因为其他应用程序可能安装了其他钩子,并且您不想破坏它们。
尝试更多类似的东西:
#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 调用。
- 如何从另一个进程解开全局钩子?
- C++ 给出编译器错误,将一个(静态)常量全局变量设置为另一个静态常量变量
- 访问另一个.cpp文件中的.cpp文件中定义的全局变量
- C 和 C++ 标头:在另一个结构内定义全局结构
- 怎么可能有两个同名的变量——一个是全局变量,另一个是局部变量
- 通过其他文件中的另一个全局变量使用类的全局实例
- 为什么 Clang++ 不在另一个静态库中运行全局对象构造函数?
- 从另一个命名空间中C++全局命名空间访问
- 全局静态常量shared_ptr被另一个shared_ptr的析构函数奇怪地窃取和删除,为什么?
- (威纳皮C++)如何在没有全局变量的情况下将数据从一个窗口传递到另一个窗口
- 如何定义来自另一个命名空间(而不是全局命名空间)的函数和数据
- 如何在另一个类中使用来自主类的全局const int变量
- 为什么全局变量由另一个源文件中的类初始化
- 如何将一个文件中存在的全局常量变量访问到另一个文件
- 如何创建一个具有全局变量的 lib 文件,这些全局变量应该将其信息共享给另一个项目
- 一个有两个线程的程序,一个线程增加全局变量,另一个线程减少相同的变量,变量是否总是正确的
- 在另一个函数中初始化后无法访问 main 中的全局数组
- 如何从另一个.cpp文件访问全局结构
- 命名空间作用域中的操作符在全局作用域中隐藏另一个操作符
- 一个翻译单元如何访问另一个翻译单元的全局作用域?