繁体   English   中英

如何从另一个进程中解开全局钩子?

[英]How do I unhook a global hook from another process?

这是我当前的设置:我有一个C ++ DLL,该DLL全局地将其功能之一挂接到计算机上运行的每个进程。 该挂钩是使用SetWindowsHookEx winapi函数在DLLMain完成的,我已经挂钩到WH_CBTWH_SHELL事件。 我也有一个C#应用程序,该应用程序通过p / invoke( LoadLibrary() )加载DLL,从而触发从DLLMain安装钩子。 DLL中的处理程序通过命名管道将事件信息发送到C#应用程序。

根据我在microsoft纪录片上所读的内容 ,这些事件将在目标进程的线程上处理,并且必须由独立的C ++ DLL安装(与WH_MOUSE_LL和WH_KEYBOARD_LL可以由任何应用程序安装,甚至可以直接从使用p / invoke的C#应用 )。

到目前为止,一切正常。 托管应用正在接收数据。 当我关闭应用程序时,会出现问题,因为处理程序仍处于挂接状态,因此DLL文件正在使用中,无法删除。

由于该处理程序不在我的应用程序中运行,而是注入了我的计算机上运行的其他进程中,因此C#应用程序不能只是简单地调用UnhookWindowsHookExFreeLibrary ,因为事件处理程序的指针属于其他进程。

问题:

我该如何从托管应用程序中触发一个解除挂钩例程,以确保该DLL不再被任何进程使用?

这是我尝试过的:

我想提出的唯一解决方案是创建一个退出事件(使用CreateEvent ),并且每次处理程序收到WH_CBTWH_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事件信号。 如果Continue()返回false,则仅应退出回调。 但是,即使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调用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM