如何添加一个系统“Windows钩子”,以便通知窗口被创建/激活?

时间:2009-06-08 12:01:18

标签: c# winapi hook

尝试了很多东西,但在我的任务栏被我的桌面用户界面上的任务栏和其他超自然效果中,我无法让它始终如一地工作。

首先尝试使用开放式库http://mwinapi.sourceforge.net/。虽然它作为枚举窗口和东西的OO层很好地工作。它无法正常挂钩

下一站是Dino E.'s post on Windows Hooks in the .Net framework。我最终写了自己的类型,因为我理解文本并试图让它发挥作用。

我的目的是让这个应用程序运行,并让它能够在运行时记录所有创建的窗口。打电话给所有的眼球...

更新:因为显然是can't write global windows hooks in .Net /托管代码(某些低级鼠标或键盘挂钩除外)已经剪断了

所以我转而使用C ++。仍然所有WinAPI调用都返回有效句柄,但我没有看到我的过滤器函数被调用 - 似乎没有收到任何通知。仍然行不通......有人能发现错误。

void CWinHookFacade::Hook()
{
    HMODULE hCurrentDll = LoadLibrary(_T("[Path to my hook dll]"));
    m_HookHandle = SetWindowsHookEx(WH_CBT, 
        FilterFunctionForHook, 
        hCurrentDll, 
        0);
    if (m_HookHandle == NULL)
    {
        throw new std::exception("Unable to hook");
    }

}
void CWinHookFacade::Unhook()
{
    if (!UnhookWindowsHookEx(m_HookHandle))
    {
        throw new std::exception("Unhook failed!");
    }
    m_HookHandle = NULL;
}

LRESULT CWinHookFacade::FilterFunctionForHook(int code, WPARAM wParam, LPARAM lParam)
{
    if (code >= 0)
    {
        switch(code)
        {
        case HCBT_CREATEWND:
            wprintf(_T("Created Window"));
            break;
        case HCBT_ACTIVATE:
            wprintf(_T("Activated Window"));
            break;
        case HCBT_DESTROYWND:
            wprintf(_T("Destroy Window"));
            break;
        }
    }

    return CallNextHookEx(m_HookHandle, code, wParam, lParam);
}

客户端exe会像这样调用Hook_DLL

int _tmain(int argc, _TCHAR* argv[])
{
    CWinHookFacade::Hook();
    getchar();
    CWinHookFacade::Unhook();
}

2 个答案:

答案 0 :(得分:5)

我认为你遇到的问题是因为你试图在C#中实现一个钩子函数。根据{{​​1}}上的pinvoke.net's documentation,它表示您无法执行此操作 - 挂钩过程必须位于非托管DLL中。否则,这将使用消息循环将DLL加载到所有正在运行的进程中,这反过来会导致在每个进程中加载​​和启动CLR。这不仅需要很长时间,而且将CLR注入所有进程可能不是最好的主意。另外,如果一个进程已经运行的CLR与你构建的DLL不同,会发生什么?

最好的方法是将此代码移动到非托管C ++ DLL,并使用某种进程间通信将钩子过程截获的数据发送回应用程序。

<强>更新

首先(这可能不会导致您的问题),为什么要调用SetWindowsHookEx()来获取DLL的LoadLibrary()

至于为什么你的钩子程序从未被调用过 - 你怎么验证这个?由于您正在挂钩系统中的所有GUI线程,这意味着您的DLL需要加载到所有GUI进程中。您可能无法看到调用HINSTANCE的结果,因为其他进程没有控制台窗口来显示输出。

要验证您的DLL是否已正确加载,请使用列出进程加载的DLL的程序(我喜欢GetModuleHandle())。您可以使用Find |查找Handle或DLL菜单项以搜索DLL的名称 - 它应该显示在带有消息循环的所有进程中。

确认您的DLL已加载后,要查看是否已调用您的挂钩,您可以将调试器附加到另一个进程(如记事本),然后在挂钩函数中设置断点。只要将消息发送到CBT挂钩,这应该会消失。如果您不想使用调试器,则可以将对wprintf()的调用更改为Process Explorer并运行OutputDebugString()之类的实用程序来监控结果。

最后,由于您的钩子函数是在另一个进程的上下文中调用的,因此您的wprintf()变量在那里无效。您应该将其存储在DebugView中,以便DLL的所有已加载实例具有相同的值。

答案 1 :(得分:3)

最后钉了这一个。把它写成blog post以防万一将来有人需要这个。