8

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

首先尝试使用开放库http://mwinapi.sourceforge.net/。虽然它作为一个 OO 层可以很好地用于枚举窗口和其他东西。它不能正确地做钩子

下一站是Dino E. 在 .Net 框架中的 Windows Hooks 上的帖子。当我理解文本并试图让它发挥作用时,我最终编写了自己的类型。

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

更新:由于显然您不能在 .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();
}
4

2 回答 2

7

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

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

更新

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

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

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

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

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

于 2009-06-08T12:21:43.073 回答
6

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

于 2009-06-16T06:38:44.653 回答