有开发者问了这样一个问题:既然 SetWindowsHookEx 的第一个参数总是会被转换为一个文件名,那为什么它的传参类型是 HINSTANCE 呢?这岂不是多此一举?
原因是这样的:在 16 位 Windows 系统上,它不是这样工作的。16 位 Windows 上根本就没有 “钩子注入” 的概念。
所有 16 位 Windows 应用程序都是运行在同一个地址空间,所以就没有必要将代码注入到其他程序中。所以,就没有必要将实例句柄转换为一个文件名来注入代码。
实际的工作原理是这样的:实例句柄将会被用来增加引用计数,从而钩子函数就不会被意外释放掉。当钩子被卸载的时候,模块的引用计数会自动减一。
即使到了 32 位 Windows,窗口管理器需要实例句柄才能将函数指针转换回 RVA,以便在将模块加载到另一个进程时可以找到函数。
如果你传递了带有模块路径的 LPCWSTR,则窗口管理器无论如何都必须执行 GetModuleHandle 来恢复实例句柄。
由于大多数程序的实例句柄都随时可用,因此这是更自然的选择。(更不用说它将保持与 16 位 Windows 的源代码兼容性,这是试图让人们有兴趣将他们的代码移植到 Win32 时的一个重要标准。)
总结
做 Windows 世界的良好公民,不要随意使用钩子。除非迫不得已。