消息队列
windows系统是通过消息驱动的,每移动一下鼠标,点击一下屏幕都会产生一个消息。这些消息会先被放在windows的一个系统消息队列(先进先出)中,windows系统会为每一个GUI线程创建一个线程消息队列,然后系统会从系统消息队列中取出一个消息放到对应的线程消息队列中。之后通过消息循环从线程消息队列中取出消息分发派遣到对应的窗口的窗口过程中。
线程消息队列是如何创建的
系统消息队列是由windows操作系统默认创建的,而对于普通的线程而言是没有线程消息队列的。线程刚创建时为普通线程,其KTHREAD.ServiceTable字段指向的是KerServiceDescriptorTable(SSDT)表,这个表包含的是ntoskrnl.exe服务函数,都是一些非GUI函数。如果我们在线程中调用第一个Windows的GUI函数时,线程的KTHREAD.ServiceTable字段会指向KerServiceDescriptorTable(Shadow SSDT)表,这个表不光包含ntoskrnl.exe服务函数还包含win32k.sys服务函数,后者包含了GUI函数。而且会分配空间为KTHREAD.win32Thread指针分配空间,由此指针就可以找到相应的线程消息队列。
利用消息队列进行IPC
因为线程消息队列是在内核中的,所以可以通过消息队列进行进程间通信(IPC)。当然进行进程间通讯的两个进程必须有GUI线程才行,因为只有GUI线程才有线程消息队列。
消息类型
系统保留从0x0000---0x03ff(WM_USER-1)的值用于系统定义的消息,而用户自定义消息的范围为0x0400(WM_USER)---0x7fff。
发送消息
消息队列中的消息对应一个消息结构MSG,包含了消息的各个信息。我们在利用消息进行进程间通讯时是通过WPARAM和LPARAM传递讯息的。
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
DWORD lPrivate;
} MSG, *PMSG, *NPMSG, *LPMSG;
我们可以通过SendMessage或PostMessage发送消息,但是SendMessage直接将消息发送到对应的窗口过程中并不放在消息队列里,所以我们要想通过消息队列实现IPC应该使用PostMessage。
PostMessageA可以将消息发送到窗口句柄hWnd对应的线程消息队列中,PostThreadMessageA可以将消息发送到线程句柄hThread对应的消息队列中。(这里注意hWnd窗口句柄是属于用户对象的句柄一般通过FindWindow函数获取,而hThread是内核对象的句柄一般通过跨进程共享内核对象完成)
BOOL PostMessageA(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
获取消息
获取消息有一般使用PeekMessage或GetMessage。
PeekMessage可以检索当前调用线程的线程消息队列中指定的消息,并不指定检索后如何处理线程队列中的消息(删除还是保留)。
BOOL PeekMessageA(
LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg
);
GetMessage和PeekMessage一样,只不过其会在检索到消息后直接将消息从对应的线程消息队列中删除。
BOOL GetMessage(
LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax
);