HHOOK SetWindowsHookExW(
[in] int idHook,
[in] HOOKPROC lpfn,
[in] HINSTANCE hmod,
[in] DWORD dwThreadId
[in] idHook
类型:int
要安装的挂钩过程的类型。 此参数可以是下列值之一。
[in] lpfn
类型:HOOKPROC
指向挂钩过程的指针。 如果 dwThreadId 参数为零或指定由其他进程创建的线程的标识符,则 lpfn 参数必须指向 DLL 中的挂钩过程。 否则,lpfn 可以指向与当前进程关联的代码中的挂钩过程。
[in] hmod
类型:HINSTANCE
DLL 的句柄,其中包含由 lpfn 参数指向的挂钩过程。 如果 dwThreadId 参数指定由当前进程创建的线程,并且挂钩过程位于与当前进程关联的代码中,则必须将 hMod 参数设置为 NULL。
[in] dwThreadId
类型:DWORD
要与之关联的挂钩过程的线程的标识符。 对于桌面应用,如果此参数为零,则挂钩过程与调用线程在同一桌面上运行的所有现有线程相关联。 有关 Windows 应用商店应用,请参阅“备注”部分。
类型:HHOOK
如果函数成功,则返回值为挂钩过程的句柄。
如果函数失败,则返回值 NULL。 若要获取扩展的错误信息,请调用 GetLastError。
SetWindowsHookEx 可用于将 DLL 注入另一个进程。 不能将 32 位 DLL 注入 64 位进程,64 位 DLL 无法注入到 32 位进程中。 如果应用程序需要在其他进程中使用挂钩,则需要将 32 位应用程序调用 SetWindowsHookEx 将 32 位 DLL 注入 32 位进程,而 64 位应用程序调用 SetWindowsHookEx 将 64 位 DLL 注入 64 位进程。 32 位和 64 位 DLL 必须具有不同的名称。
由于挂钩在应用程序的上下文中运行,因此它们必须与应用程序的“位性”匹配。 如果 32 位应用程序在 64 位 Windows 上安装全局挂钩,则会将 32 位挂钩注入到每个 32 位进程(通常的安全边界适用)。 在 64 位进程中,线程仍标记为“挂钩”。但是,由于 32 位应用程序必须运行挂钩代码,因此系统会在挂钩应用的上下文中执行挂钩;具体而言,在调用 SetWindowsHookEx的线程上。 这意味着挂钩应用程序必须继续泵送消息,否则可能会阻止 64 位进程的正常运行。
如果 64 位应用程序在 64 位 Windows 上安装全局挂钩,则会将 64 位挂钩注入到每个 64 位进程中,而所有 32 位进程都使用挂钩应用程序的回调。
若要挂钩 64 位 Windows 安装桌面上的所有应用程序,请安装一个 32 位全局挂钩和一个 64 位全局挂钩,每个挂钩进程都来自相应的进程,并确保在挂钩应用程序中保持泵送消息以避免阻止正常运行。 如果已有 32 位全局挂钩应用程序,并且它不需要在每个应用程序的上下文中运行,则可能不需要创建 64 位版本。
如果 hMod 参数 NULL,dwThreadId 参数为零或指定由另一个进程创建的线程的标识符,则可能会出现错误。
调用 CallNextHookEx 函数 函数链接到下一个挂钩过程是可选的,但强烈建议这样做:否则,安装了挂钩的其他应用程序不会收到挂钩通知,因此行为可能不正确。 应调用 CallNextHookEx,除非绝对需要阻止其他应用程序看到通知。
在 .NET 应用中,必须确保垃圾回收器不会移动回调(否则你的应用会因 ExecutionEngineException 而崩溃)。 执行此操作的一种方法是将回调设置为类的静态方法。
在终止之前,应用程序必须调用 UnhookWindowsHookEx 函数 函数来释放与挂钩关联的系统资源。
挂钩的范围取决于挂钩类型。 某些挂钩只能与全局范围一起设置;也可以仅为特定线程设置其他线程,如下表所示。
WH_CALLWNDPROC
线程或全局
WH_CALLWNDPROCRET
线程或全局
WH_CBT
线程或全局
WH_DEBUG
线程或全局
WH_FOREGROUNDIDLE
线程或全局
WH_GETMESSAGE
线程或全局
WH_JOURNALPLAYBACK
WH_JOURNALRECORD
WH_KEYBOARD
线程或全局
WH_KEYBOARD_LL
WH_MOUSE
线程或全局
WH_MOUSE_LL
WH_MSGFILTER
线程或全局
WH_SHELL
线程或全局
WH_SYSMSGFILTER
对于指定的挂钩类型,首先调用线程挂钩,然后调用全局挂钩。 请注意,可以在安装挂钩的线程上调用WH_MOUSE、WH_KEYBOARD、WH_JOURNAL*、WH_SHELL和低级别挂钩,而不是线程处理挂钩。 对于这些挂钩,如果 32 位挂钩领先于挂钩链中的 64 位挂钩,则调用 32 位和 64 位挂钩。
全局挂钩是共享资源,安装一个挂钩会影响与调用线程相同的桌面中的所有应用程序。 所有全局挂钩函数都必须位于库中。 全局挂钩应限制为专用应用程序或在应用程序调试期间用作开发助手。 不再需要挂钩的库应删除其挂钩过程。
Windows 应用商店应用: 如果 dwThreadId 为零,则 Windows 应用商店应用进程和 Windows 运行时中转站进程不会加载窗口挂钩 DLL,除非它们由 UIAccess 进程(辅助功能工具)安装。 通知在安装程序的线程上传送这些挂钩:
WH_JOURNALPLAYBACK
WH_JOURNALRECORD
WH_KEYBOARD
WH_KEYBOARD_LL
WH_MOUSE
WH_MOUSE_LL
此行为类似于挂钩 DLL 与目标应用程序进程之间存在体系结构不匹配的情况,例如,挂钩 DLL 为 32 位,应用程序进程为 64 位。
有关示例,请参阅 安装和释放挂钩过程。
winuser.h 标头将 SetWindowsHookEx 定义为一个别名,该别名根据 UNICODE 预处理器常量的定义自动选择此函数的 ANSI 或 Unicode 版本。 将中性编码别名与不中性编码的代码混合使用可能会导致编译或运行时错误不匹配。 有关详细信息,请参阅函数原型的 约定。