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

Gis*_*shu 5 c# hook winapi

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

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

下一站是Dino E.在.Net框架中关于Windows Hooks的帖子.我最终写了自己的类型,因为我理解文本并试图让它工作.

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

更新: Snipped因为显然您无法在.Net /托管代码中编写全局Windows挂钩(除了一些低级鼠标或键盘挂钩)

所以我切换到了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);
}
Run Code Online (Sandbox Code Playgroud)

客户端exe会像这样调用Hook_DLL

int _tmain(int argc, _TCHAR* argv[])
{
    CWinHookFacade::Hook();
    getchar();
    CWinHookFacade::Unhook();
}
Run Code Online (Sandbox Code Playgroud)

And*_*ndy 5

我认为你遇到的问题是因为你试图在C#中实现一个钩子函数.基于pinvoke.net的文档上SetWindowsHookEx(),它说,你不能做到这一点-钩子程序必须是在非托管的DLL.否则,这将使用消息循环将DLL加载到所有正在运行的进程中,这反过来会导致在每个进程中加载​​和启动CLR.这不仅需要很长时间,而且将CLR注入所有进程可能不是最好的主意.另外,如果进程已经运行的CLR与您构建的DLL不同,会发生什么?

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

更新

首先(这可能不会导致你的问题),你为什么要打电话LoadLibrary()来获取HINSTANCE你的DLL?由于您的DLL已经加载,因此调用GetModuleHandle()可能会更好.

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

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

一旦验证了DLL的加载,看看你的钩子是否被调用,你可以将调试器附加到另一个进程(如记事本),然后在钩子函数中设置一个断点.只要将消息发送到CBT挂钩,这应该会消失.如果您不想使用调试器,则可以将调用更改wprintf()为OutputDebugString()并运行DebugView之类的实用程序来监视结果.

最后,由于您的钩子函数是在另一个进程的上下文中调用的,因此您的m_HookHandle变量在那里不会有效.您应该将其存储在共享数据段中,以便DLL的所有已加载实例具有相同的值.


Gis*_*shu 5

终于搞定了这个。把它写成一篇博客文章,以防将来有人需要它。