文章目录
- 要点回顾:
 - 为什么拿到句柄非得要回零环?
 - 消息队列(总共有7个小队列)结构
 - GetMessage的声明:
 - GetMessage进入内核:
 - GetMessage的功能总结:
 - DispatchMessage
 - 举例验证(有前提情况,仔细观察)
 - ```SendMessage```发送消息运行截图
 - ```PostMessage```发送消息运行截图
 
要点回顾:
一个GUI线程有一个消息队列:
 普通线程–>GUI线程–>THREAD.W32THREAD -->THREADINFO–>消息队列
一个线程可以有多个窗口,所有窗口共享一个消息队列:
 _WINDOW_OBJECT ---->PTHREADINFO pti //所属线程
 ---->WNDPROC IpfnWndProc //窗口过程(窗口回调函数)
为什么拿到句柄非得要回零环?
GetMessage(&msg, NULL, 0, 0)
TranslateMessage(&msg);
DispatchMessage(&msg);
 
这里消息msg的结构体成员如下:
typedef struct tagMSG {HWND   hwnd;UINT   message;WPARAM wParam;LPARAM lParam;DWORD  time;POINT  pt;DWORD  lPrivate;
} MSG, *PMSG, *NPMSG, *LPMSG;
 
这个消息里面存放着窗口的句柄,句柄是什么?句柄它仅仅是一个窗口对象的索引而已,并非当前对象地址,通过句柄找不到相应的窗口回调,它仅仅就是一个窗口对象索引值。窗口回调是存储在窗口对象里面的,如果需要找到窗口回调,那么我们就需要先找到窗口对象,而窗口对象在哪呢?(窗口与线程的关系)窗口都是由API进入零环去画出来,一切信息都在零环,这个知识点我们在前面已经说过。。
所以GetMessage,TranslateMessage,DispatchMessage拿着取出来的消息的句柄,进入零环,通过句柄找到相应的窗口对象,通过窗口对象找到相对于的窗口回调函数,然后内核进行调用回调函数。
这也就是为什么非得进入零环的原因。
消息队列(总共有7个小队列)结构
1.SentMessagesListHead //接到SendMessage发来的消息
 2.PostedMessagesListHead //接到PostMessage发来的消息
 3.HardwareMessagesListHead //接到鼠标,键盘的消息
 …………
 …………
 根据前面的介绍,消息队列放在THREADINFO(THREADINFO在KTHREAD结构体中,KTHREAD又在ETHREAD中)中:
 
 USER_MESSAGE_QUEUE又分为七个小队列
 
 
GetMessage的声明:
GetMessage(LPMSG	IpMsg,			//返回从队列中摘下来的消息HWND	  hWnd,				//过滤条件一:(要取的是哪个窗口的消息,如果要专门取哪个窗口的消息,直接把句柄放在此处就行)发个这个窗口的消息UINT	wMsgFilterMin,		//过滤条件UINT    wMsgFilterMax		//过滤条件
);
 
GetMessage进入内核:
GetMessage会调用内核层函数w32k!NtUserGetMessage,伪代码如下:
do{
//先判断SentMessageListHead
do{
……
KeUserModeCallBack(USER32_CALLBACK_WINDOWPROC,Arguments,ArgumentLength,&ResultPointer,&ResultLength);
…………
}while(SentMessageListHead!=NULL)
}while(其他队列!=NULL)
 

进入后可以查看到这里有处理SentMessagesListHead 消息队列的函数:
 
 然后进入后这是从0环回到3环的函数:
 
 GetMessage只处理SendMessage发来的消息,原因可以看上图,而由PostMessage发来的消息,只是取出,并不会进行近一步处理操作
GetMessage的功能总结:
GetMessage(只处理第一个消息队列的消息,至于其它消息队列的消息,GetMessage只负责取出, 然后不管,继续向下传递)的主要功能:
- (第一个循环)首先会判断
SentMessagesListHead这个队列里面有没有消息,如果有的话,首先会把这个消息给处理掉(如何处理呢?也就是从0环回到3环,再来调用注册的窗口过程函数) - (第二个循环:依次判断其他的6个队列,里面如果有消息,就返回,没有就继续取消息)循环判断是否有该窗口的消息,如果有,将消息存储到MSG指定的结构,并将消息从列表中删除(依次判断其他的6个队列,里面如果有消息 返回,没有 继续)
它会首先看SentMessagesListHead这个队列,如果有的话,会就地处理 
DispatchMessage
DispatchMessage(&msg)//消息的分发,根据窗口句柄调用相关的窗口过程,通过不同的句柄,进入零环找到不同的窗口对象,然后根据窗口对象找到回调函数,并且调用回调函数。
即其他6个消息队列的处理流程:
 User32!DispatchMessage调用w32k!NtUserDispatchMessage
- 根据窗口句柄找到窗口对象
 - 根据窗口对象找到窗口过程处理函数,由0环发起调用

 
举例验证(有前提情况,仔细观察)
把TranslateMessage(&msg);和DispatchMessage(&msg);注释掉后,只剩GetMessage(&msg, NULL, 0, 0),然后利用其它程序PostMessage(hwnd, 0x0401, NULL, NULL);和SendMessage(hwnd, 0x0401, NULL, NULL);分别发送消息
 前提情况:(特别注意)
 
SendMessage发送消息运行截图
 
SendMessage(hwnd, 0x0401, NULL, NULL);
 

 下图中我们可以看到
 
 当我们未点击确定时,发送消息的程序未退出,需要点击确定后,发送消息的程序收到返回消息,它才会自行退出。这也就是SendMessage的同步问题
当点击确定后,发送消息程序的运行截图:
 
PostMessage发送消息运行截图
 
PostMessage(hwnd, 0x0401, NULL, NULL);
 
发送消息的运行截图:
 
 接收消息的运行截图:
 
 这里充分说明了上述情况,GetMessage并不会处理PostMessage发送的消息。
注意:
 PostMessage发送完消息后,程序即刻退出,并不会等待处理结果,这也就是PostMessage发送消息异步问题



















