以下代码注册了一个低级鼠标挂钩,以全局监控鼠标事件 这是我能得到的最简单的工作实例 使用VC ++ 2010编译:{{1}}
cl test.cpp /link /entry:mainCRTStartup /subsystem:windows
这是基本的一个线程,一个窗口,一个消息泵示例。除了鼠标钩。
我怀疑这段代码所做的事情与我在SO,MSDN,论坛,博客等中反复阅读的两件事相矛盾。
全局钩子程序必须驻留在DLL中
MSDN documentation for SetWindowsHookEx
通过说:
如果dwThreadId参数为零,则lpfn参数必须指向DLL中的钩子过程
由于#include <windows.h>
HWND label1 ;
//THE HOOK PROCEDURE
LRESULT CALLBACK mouseHookProc(int aCode, WPARAM wParam, LPARAM lParam){
static int msgCount = 0 ;
static char str[20] ;
SetWindowText( label1, itoa(++msgCount, str, 10) ) ;
return CallNextHookEx(NULL, aCode, wParam, lParam) ;
}
int main(){
/**/// STANDARD WINDOW CREATION PART //////////////////////////////////////////////////////
/**/
/**/ WNDCLASSEX classStruct = { sizeof(WNDCLASSEX), 0, DefWindowProc, 0, 0, GetModuleHandle(NULL), NULL,
/**/ LoadCursor(NULL, IDC_ARROW), HBRUSH(COLOR_BTNFACE+1), NULL, "winClass", NULL } ;
/**/ RegisterClassEx(&classStruct) ;
/**/
/**/ HWND mainWin = CreateWindow("winClass", "", 0, 200,200, 100,100, NULL, NULL, NULL, NULL) ;
/**/ ShowWindow(mainWin, SW_SHOWDEFAULT) ;
/**/
/**/ label1 = CreateWindow("static", "0", WS_CHILD, 5,5, 80,20, mainWin, NULL, NULL, NULL) ;
/**/ ShowWindow(label1, SW_SHOWNOACTIVATE) ;
/**/
/**/// END OF WINDOW CREATION PART ////////////////////////////////////////////////////////
//HOOK INSTALATION
HHOOK hookProc = SetWindowsHookEx(WH_MOUSE_LL, mouseHookProc, GetModuleHandle(NULL), 0) ;
//MESSAGE LOOP
MSG msg ;
while( GetMessage(&msg, NULL, 0, 0) ){
TranslateMessage(&msg) ;
DispatchMessage(&msg) ;
}
UnhookWindowsHookEx(hookProc) ;
}
的等待状态不可警,因此无法中断GUI线程(带有消息泵的线程)。这意味着当GetMessase
阻塞等待更多消息时,它无法接收中断其等待状态的信号。
但是,这里没有任何DLL可以看到,并且钩子程序也必须中断线程,否则程序将无法工作,并且它确实(我假设此程序中只有一个线程)。
所以要么我完全误解了这两点,要么这段代码的工作方式与我期望的异步过程调用方法不匹配。
无论哪种方式,我对这里发生的事情一无所知。
请您解释一下这段代码是如何运作的
它是单线程程序吗?
钩子程序是否会中断线程?
上述两点中的任何一点都是真的吗?
答案 0 :(得分:4)
钩子程序必须在DLL中。
WH_MOUSE_LL
但是,WH_MOUSE_LL挂钩不会注入另一个进程。 相反,上下文切换回安装的进程 hook和它在其原始上下文中被调用。然后是上下文 切换回生成事件的应用程序。
所以这里不需要DLL(对于什么??)和钩子程序也可以放在EXE中。
是单线程程序吗?
一般是的。如果不考虑可能的系统工作线程,但是在第一个线程的上下文中调用的所有消息和钩子
钩子程序是否会中断线程?
来自MSDN
在安装它的线程的上下文中调用此钩子。 通过向安装了该安装的线程发送消息来进行调用 钩。因此,安装钩子的线程必须有一个 消息循环。
可以说mouseHookProc
调用了GetMessage
内部。这个函数在内核中等待消息。当系统想要调用挂钩程序时,它通过KiUserCallbackDispatcher
调用来完成。
“打断线程” - 你打断中断的意思?
1。)这不是真的,如本例所示。钩子程序必须在DLL中,只有必须在接收消息的线程的上下文中调用钩子,所以在任意进程上下文中 - 在这种情况下我们需要将代码注入另一个进程 - 因为这和DLL需要。如果我们总是在自我进程上下文中调用 - 不注入,不需要DLL
2。)GetMessage
实际上不等待处于警报状态,因此当代码在GetMessage
中等待时,任何APC都无法传递,但APC绝对不相关。 APC和Windows消息传递的功能不同。当然存在和类似的观点。对于APC和一些Windows消息传递,线程必须在内核中等待。对于处于警报状态的APC,适用于GetMessage
或PeekMessage
中的Windows消息。用于APC传送系统调用KiUserApcDispatcher
,用于Windows消息KiUserCallbackDispatcher
。两者都是来自内核模式的回调,但它在不同条件下调用
正好意义上的中断。
在这种意义上,在Windows用户模式下根本不存在中断。 APC或Windows消息(钩子消息是Windows消息的特殊情况)永远不会在任意位置中断执行,而是使用回调机制。例外是特例。线程必须首先调用一些api才能进入内核空间(对于Windows消息传递,这是GetMessage
或PeekMessage
,对于APC - 等待可警告状态函数或ZwTestAlert
)。然后内核可以使用回调到用户空间来传递APC或Windows消息。此时从内核到用户空间只存在3个回调点(从win2000到win10不变)
KiUserApcDispatcher
- 用于APC交付,
KiUserCallbackDispatcher
- 用于调用窗口过程或钩子过程
KiUserExceptionDispatcher
- 用于异常基础设施 - 这种感觉最接近中断,
答案 1 :(得分:0)
行为明确记录在LowLevelMouseProc:
下在安装它的线程的上下文中调用此钩子。通过向安装了挂钩的线程发送消息来进行调用。
当要将鼠标输入事件放入线程的消息队列中时,系统会向安装了挂钩的线程发送消息,等待它返回,然后继续处理输入事件。
钩子不需要在DLL中就可以工作。所有这些都是在之前将鼠标输入事件放入目标线程的输入队列中完成的,因此不需要中断消息检索功能。一切都按顺序执行:
GetMessage
,PeekMessage
等)来获取输入事件。<小时/> 关于如何在钩子应用程序中实现它的一些注释:
钩子应用程序需要运行消息循环,因为系统向其发送消息,以通知原始输入线程即将被放入目标线程的输入队列的输入事件。 GetMessage
调用(在钩子应用程序中)在钩子消息的情况下充当调度程序,并在返回之前调用钩子过程。尽管GetMessage
是一个阻塞调用,但它可以被唤醒(没有警报 1))。每当有可用于检索的消息时,它可能正在等待发出信号的Event object。另外,您的钩子应用程序中不需要TranslateMessage
和DispatchMessage
。
1) 参考:The alertable wait is the non-GUI analog to pumping messages。