【问题标题】:How to add a system "windows hook" so as to be notified of windows being created/activated?如何添加系统“windows hook”以便在创建/激活窗口时收到通知?
【发布时间】:2010-11-01 03:54:44
【问题描述】:

尝试了很多东西,但在我的任务栏被核弹和桌面 UI 上的其他超自然效果的情况下,我无法让它始终如一地工作。

首先尝试使用开放库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();
}

【问题讨论】:

  • 澄清一下 - SetWindowsHookEx 每次都返回一个有效的句柄。此外,如果有人想尝试这个,上面的代码没有任何其他依赖项。粘贴到空白项目中的 .cs 文件中并运行。

标签: c# winapi hook


【解决方案1】:

我认为您遇到的问题是因为您尝试在 C# 中实现挂钩函数。基于SetWindowsHookEx() 上的pinvoke.net's documentation,它说您不能这样做 - 挂钩过程必须在非托管 DLL 中。否则,这会将您的 DLL 加载到带有消息循环的所有正在运行的进程中,这反过来会导致 CLR 在每个进程中被加载和启动。这不仅需要很长时间,而且将 CLR 注入所有进程可能也不是最好的主意。另外,如果一个进程已经运行了一个不同于构建 DLL 的 CLR 会怎样?

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

更新

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

至于为什么你的钩子程序永远不会被调用——你是如何验证的?由于您要挂钩系统中的所有 GUI 线程,这意味着您的 DLL 需要加载到所有 GUI 进程中。您可能看不到调用wprintf() 的结果,因为其他进程没有控制台窗口来显示输出。

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

一旦您确认您的 DLL 已加载,要查看您的钩子是否被调用,您可以将调试器附加到另一个进程(例如记事本),然后在钩子函数中设置断点。每当将消息发送到 CBT 挂钩时,它就会消失。如果您不想使用调试器,则可以将对 wprintf() 的调用更改为 OutputDebugString() 并运行像 DebugView 这样的实用程序来监控结果。

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

【讨论】:

  • 我知道注入部分 - 将我的代码放在 DLL 中。但是不知道在C#中是不可能的。明天将在 CPP 中尝试这个并发布它是如何进行的......我错过了阅读 pinvoke 页面上的注释部分,这让我很危险。 (我发现 ManagedThreadID 也不是很辛苦)
  • 让我失望的另一件事是来自mwinapi.sourceforge.net 库的 Hook 类有一个 ctor 参数 - bGlobal 并且是完全托管的。奇怪...
  • 也切换到非托管 - 似乎仍然不起作用。更新了原始问题...
  • 我知道我的钩子不会被调用,因为所有东西都习惯了 crash-n-burn :) 是的,我很快发现记录到文件是比控制台更好的选择。共享数据段的事情在路上找到了我。
【解决方案2】:

终于搞定了这个。写成blog post,以防将来有人需要。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-24
    • 1970-01-01
    • 2021-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多