64位Windows上的全局Keyhook
Global Keyhook on 64-Bit Windows
我目前在Windows 7 64位O/S上获得全局键钩工作有一些麻烦。现在我知道在Stackoverflow上已经有很多关于这个主题的线程,但是他们没有一个给我一个我可以工作的答案,或者我不太明白这个问题是如何在这些线程中解决的。
所以我会试着解释我正在挣扎的是什么,希望任何人都能帮助我,或者给我指出正确的方向。
基本上我的目标是拦截CTRL+C和CTRL+V键盘输入的剪贴板管理器。出于这个原因,我目前的尝试是注册一个系统范围的WH_KEYBOARD
钩子,它可以根据我的需要处理拦截的击键。
我在64位Windows 7 O/S上运行钩子,这就是问题开始的地方。很明显,32位的Hook DLL会给64位进程带来问题,反之亦然。出于这个原因,我已经生成了包含钩子的x86和x64版本的库,以及钩子的调用者(调用SetWindowsHookEx()
的人),两者都具有文档建议的不同文件名。
但是现在呢?如果我将64位DLL设置为系统范围钩子,那么当我按下一个键时,所有32位应用程序就会开始挂起。同样的事情,当我应用32位挂钩,我的Windows实际上是不可用的,因为explorer.exe
是64位。如果我设置了这两个钩子,我的系统就会处于停顿状态,并拥有一个全局的"咬合"战斗。
现在我假设这个问题是由64位挂钩DLL试图注入到32位进程等等引起的,这当然是没有意义的。但是对于这种情况,SetWindowsHookEx()
的文档是这样说的:
因为钩子在应用程序的上下文中运行,所以它们必须匹配应用程序的"咬合度"。如果32位应用程序安装了在64位Windows上的全局钩子,32位钩子被注入到每个32位进程(应用通常的安全边界)。64位进程中,线程仍然被标记为"已挂起"。然而,因为a32位应用程序必须运行钩子代码,系统才执行在钩子应用的上下文中钩子;具体来说,在这个线程上叫SetWindowsHookEx。这意味着钩子应用程序必须继续发送消息,否则可能会阻塞的正常功能
我不完全理解文本的粗体部分,但我将其解释为,如果钩子目标的"位"与钩子的"位"不同,它将在实际设置钩子的线程上执行,因此它可以执行。此外,这意味着该线程必须仍然处于活动状态,并且可能运行某种消息循环。这是正确的吗?还是说我完全错了?文档似乎也给出了关于我的场景该怎么做的确切说明:
勾起64位Windows桌面上的所有应用程序安装时,安装一个32位全局钩子和一个64位全局钩子,每个都来自适当的进程,并确保持续发送消息在挂接应用程序中避免阻塞正常功能。
但是,我没有把握在我的实现中必须做什么。最后,为了展示一些代码,让我们以这个尝试设置系统范围键钩的基本示例为例。我猜线程的创建代码应该是不相关的:
volatile static bool runThread = false;
DWORD WINAPI threadStart(LPVOID lpThreadParameter) {
HMODULE hMod = LoadLibraryA(is64Bit() ? "keyhook.x64.dll" : "keyhook.x86.dll");
HHOOK hHook = SetWindowsHookExA(WH_KEYBOARD, (HOOKPROC)GetProcAddress(hMod, "hookProc"), hMod, 0)));
runThread = true;
while(runThread) {
// Message pump? Yes? No? How?
Sleep(10);
}
UnhookWindowsHookEx(hHook);
FreeLibrary(hMod);
return 0;
}
钩子本身保持得相当琐碎——它足以在越过位时引起挂起问题:
extern "C" LRESULT hookProc(int code, WPARAM wParam, LPARAM lParam) {
if(code == HC_ACTION) {
}
return CallNextHookEx(nullptr, code, wParam, lParam);
}
我想有些人现在可能会把他们的手放在头上,你可以看出我很少使用钩子;)
但这正是我问的原因:)
长话短说:如果有人能告诉我如何改变上面的例子,让一个系统范围的键钩在64位Windows上工作,我将不胜感激。我的问题是,某些具有其他"位"而不是钩子的应用程序开始挂起,我不知道如何解决这个问题。
任何帮助都非常感谢!
Thanks and regards
PuerNoctis
好了,我知道问题出在哪里了。也许我的解决方案可以帮助遇到同样问题的其他人:如上所述,文档明确地声明
[…系统在(32位)钩子应用的(32位)钩子中执行(32位)钩子上下文;具体来说,在调用SetWindowsHookEx的线程上。这意味着挂钩应用程序必须继续泵送消息或者它可能会阻塞64位进程的正常运行。
我所经历的是提到的阻塞行为,应该用消息泵来克服。在我上面的代码示例中,正是缺少了这部分机制,因为我不知道这应该是一个简单的Windows消息循环(我之前不知道术语"泵")。我的初始代码的最终代码片段必须看起来像这样:
volatile static bool runThread = false;
DWORD WINAPI threadStart(LPVOID lpThreadParameter) {
HMODULE hMod = LoadLibraryA(is64Bit() ? "keyhook.x64.dll" : "keyhook.x86.dll");
HHOOK hHook = SetWindowsHookExA(WH_KEYBOARD, (HOOKPROC)GetProcAddress(hMod, "hookProc"), hMod, 0)));
MSG msg;
runThread = true;
while(runThread) {
// Keep pumping...
PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE);
TranslateMessage(&msg);
DispatchMessage(&msg);
Sleep(10);
}
UnhookWindowsHookEx(hHook);
FreeLibrary(hMod);
return 0;
}
在这个例子中,我使用非阻塞PeekMessage()
而不是GetMessage()
,因为我希望我的线程不断检查它的终止标志。
有了这个实现,我的钩子在所有本机64位或WOW64进程中都能像预期的那样工作,没有应用程序一旦被钩子挂起。消息泵是唯一缺少的部分。
经过所有这些实验,我得出以下结论-如果我错了,请在评论中纠正我:
当一个系统范围的钩子被安装时,它试图将给定的钩子dll注入到每个正在运行的进程中。如果进程的"位元"与Hook-DLL中的位元匹配,则钩子子程将作为目标进程中的远程线程执行(就像普通注入器的工作一样)。在"位"不同的情况下,Windows采取回退迂回回到最初调用SetWindowsHookEx()
的进程和线程(在我的例子中是代码片段中的线程),并作为与"位"不匹配的进程的执行代理。由于这个原因,这个线程需要不断地处理传入的消息,否则原始目标进程将不会处理任何事件,而原始目标进程将开始挂起。
同样,如果有不完全正确,不完整,或者有任何进一步的注释要添加,请在下面发表评论。谢谢!:)
我的解决方案是编译钩子应用程序(其中SetWindowsHookEx()
被调用)和DLL(其中回调钩子函数位于)到x86和x64版本。因此,我们有两个exe (xxx-x86.exe &xxx-x64.exe)和两个dll (xxx-x86.dll &xxx-x64.dll)。
然后实现一个复杂的IPC协议来同步x86应用程序和x64应用程序之间的数据。它看起来工作,不会阻塞另一个进程,"位"是不匹配的。但是很难处理精确的事件序列,它只能作为事件的粗略指示器。
这个解决方案很难看,但我没有更好的办法…
- 为什么在全局范围内使用"extern int a"似乎不行?
- 在全局变量中保存类的实例以重新创建类(创建"backup")
- 当vector是tje全局变量时,c++中vector的内存管理
- std::threads可以从Windows DLL中的全局变量创建/销毁吗?
- 如何创建一个空的全局类并在启动时实例化它
- 内联函数中具有内部链接的全局变量
- 为什么虚函数不能是静态的和全局的?
- 如何在信号处理程序和普通函数中对全局变量进行互斥读写操作
- 全局作用域中函数指针的赋值
- g++用户定义的动态链接库上的全局new和delete运算符
- 在命名空间中使用全局命名空间中的函数
- 全局变量 多读取器 一个写入器多线程安全?
- 类的全局对象和静态成员
- 如何声明一个可以在整个程序中使用的全局 2d 3d 4d .. 数组(堆版本)变量?
- 如果全局变量默认是外部变量,为什么要添加"extern"关键字?
- 不同作用域中的静态变量和全局变量
- 如何使用 llvm-10 库在C++定义 LLVM 全局值变量?
- 使用全局声明的向量时,C++双重释放错误/损坏
- 将线程中的数据存储到全局容器的最佳方法?
- 64位Windows上的全局Keyhook