Get*_*ree 1 c++ winapi asynchronous message-queue mouse-hook
以下代码注册一个低级鼠标钩子来全局监视鼠标事件。
这是我能得到的最简单的工作示例。
用VC++2010编译:cl test.cpp /link /entry:mainCRTStartup /subsystem:windows
#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) ;
}
Run Code Online (Sandbox Code Playgroud)
这是基本的一线程、一窗口、一消息泵示例。除了鼠标挂钩。
我怀疑这段代码所做的事情与我在 SO、MSDN、论坛、博客等中反复阅读的两件事相矛盾。
全局挂钩过程必须驻留在 DLL
MSDN 文档中,以便SetWindowsHookEx通过以下内容确认这一点:
如果 dwThreadId 参数为零,则 lpfn 参数必须指向 DLL 中的挂钩过程
GUI 线程(带有消息泵的线程)无法被中断,因为GetMessase的等待状态不可警报。这意味着当GetMessage阻塞等待更多消息时,它无法接收中断其等待状态的信号。
然而,这里没有任何地方可以看到DLL,而且钩子过程也必须中断线程,否则程序将无法工作,但它确实工作了(我假设这个程序中只有一个线程)。
因此,要么我完全误解了这两点,要么这段代码的工作方式与我期望的异步过程调用方法不匹配。
不管怎样,我对这里发生的事情一无所知。
您能解释一下这段代码是如何工作的吗?
是单线程程序吗?
钩子过程是否中断了线程?
上述两点是否属实?
仅当需要将钩子注入另一个进程时,钩子过程才必须位于 DLL 中。为了WH_MOUSE_LL
但是,WH_MOUSE_LL 挂钩不会注入到另一个进程中。相反,上下文切换回安装挂钩的进程,并在其原始上下文中调用它。然后上下文切换回生成事件的应用程序。
所以这里不需要DLL(为什么??)并且钩子程序也可以放在EXE中。
是单线程程序吗?
总的来说是的。如果不考虑可能的系统工作线程,但是在第一个线程的上下文中调用的所有消息和挂钩
钩子过程是否中断了线程?
来自 MSDN
该钩子在安装它的线程的上下文中被调用。该调用是通过向安装挂钩的线程发送消息来进行的。因此,安装钩子的线程必须有消息循环。
所以可以说mouseHookProc叫内线GetMessage电话。该函数在内核中等待消息。当系统需要调用钩子过程时,它通过KiUserCallbackDispatcher调用来完成。“中断线程”-中断是什么意思?
1.) 这不是真的,如本示例所示。钩子过程必须在 DLL 中,只有当钩子必须在接收消息的线程上下文中调用时,所以在任意进程上下文中 - 在这种情况下我们需要将代码注入到另一个进程 - 因为这和 DLL 需要。如果我们总是在自进程上下文中调用 - 无需注入,不需要 DLL
2.)GetMessage确实等待不处于可警报状态,因此当代码等待时任何APC都无法传递GetMessage,但这里的APC绝对不相关。APC 和 windows 消息传递不同的功能。当然存在相似点。对于 APC 和某些 Windows 消息传递,线程必须在内核中等待。对于处于可警报状态的 APC,对于GetMessage或中的 Windows 消息PeekMessage。用于 APC 传递系统调用KiUserApcDispatcher,用于 Windows 消息KiUserCallbackDispatcher。两者都是内核模式的回调,但它在不同的条件下调用
当代码执行可以在任意位置中断并且中断例程开始执行时,中断就是确切意义上的中断。从这个意义上说,中断在 Windows 用户模式中根本不存在。APC或Windows消息(钩子消息是Windows消息的特例)永远不会在任意位置中断执行,而是使用回调机制。例外是特殊情况。线程必须首先调用一些 api 来进入内核空间(对于 Windows 消息传递,这是GetMessage或PeekMessage,对于 APC - 在可警报状态函数中等待或ZwTestAlert)。然后内核可以使用回调到用户空间来传递 APC 或 Windows 消息。此时从内核到用户空间仅存在3个回调点(从win2000到win10都没有变化)
KiUserApcDispatcher- 用于APC传递,
KiUserCallbackDispatcher- 用于调用窗口过程或钩子过程
KiUserExceptionDispatcher- 用于异常基础设施 - 这最接近于意义上的中断,