前几天公司去桂林旅游了, 每天累死了,晚上回来就想睡, 同时也放松一下。 隔了几天没有写Demo了, 今天我们继续写消息机制的第二部分。
在消息机制1部分中,我们主要讲解了消息循环部分,实际更突出说明了GetMessage 和 PeekMessage之间的差别,本章我们将讲解入队消息和非入队消息,入队消息就是指消息在产生后直接放入到应用程序消息队列中,由消息循环获取并通过DispatchMessage分发给窗体过程函数。 非入队消息那自然就是消息产生后不被放入到应用程序消息队列,而是由windows直接调用窗体过程函数处理。 我们先看一段代码:
{
static HWND hWnd;
MSG Msg;
while (true)
{
if (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
{
if (WM_CREATE == Msg.message)
{
hWnd = Msg.hwnd;
}
if (WM_QUIT == Msg.message)
{
break;
}
TranslateMessage(&Msg);
DispatchMessage(&Msg);
} else {
if (NULL != hWnd)
{
OnIdle(hWnd);
}
}
}
return;
}
这个代码有什么问题呢? 这是我在写Demo0003时写的一段代码,我调试了一下才发现OnIdle参数hWnd一直为空, 我在WM_CREATE时不是给hWnd赋值了吗? 分析了一下才知道,WM_CREATE 根本没有进入消息队列, Windows在创建窗体时而去直接调用窗体过程函数的。 由此看来,消息循环并不能获取所有的消息,但消息一定会被窗体过程函数捕获。 那除了
WM_CREATE消息不入队列还有什么消息不入队呢, 有什么规律的吗? 个人认为没有严格的规定一般输入式的消息被放入消息队列如: WM_KEYDOWN, WM_KEYUP, WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_LBUTTONUP等, 我也发现还会包括WM_TIMER, WM_PAINT, WM_QUIT也会被队列化。但被SendMessage的消息一定不进入消息列队,而PostMessage消息会进入消息队列。 我们重点看看这两个函数:
1. LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
MSDN Description: Sends a message to the window and does not return until the window procedure has processed the message.
=> 发送消息给窗体并由窗体过程处理完后才返回.
2. BOOL PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
MSDN Description: Places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for
the thread to process the message.
=> 将一个消息存放创建窗体线程的消息队列中并不等线程处理消息就返回.
比较一个两个函数:
a. 参数部分,它们有着相同的参数
hWnd - 接受消息的窗体,当此参数为HWND_BROADCAST时,发送的消息将会被所有的(其他应用)顶层窗体所接受.
Msg - 消息ID, 当hWnd参数为HWND_BROADCAST时,此消息必须由RegisterWindowsMessage函数生成,才可被接受.
wParam – 消息所属加的参数
lParam – 消息所属加的参数
b. 队列化方面,SendMessage发出的消息是不进消息队列的, PostMessage发出的消息都会进入消息队列。
c. 编程应注意,SendMessage调用时相当于把对应的窗体过徎函数中此消息段的代码搬到过来运行即与调用函数是同步的。而PostMessage调用时仅仅是放到消息队列中,不等其消息处
理 代码结果的即与调用函数是异步的.
接下来我们来验证一下我们以上的理论,SendMessage 发送的消息不入队列, PostMessage 发送的消息会入队列的
验证的方法:
1. 使用timer调用SendMessage和PostMessage发送消息
{
switch (wParam)
{
case IDT_SENDMESSAGE:
{
SendMessage(hWnd, 0XFFFC, 0, 0);
break;
}
case IDT_POSTMESSAGE:
{
PostMessage(hWnd, 0XFFFE, 0, 0);
break;
}
}
return 0;
}
2. 在消息循环中将获取的消息记录下来;
{
MSG Msg;
while (true)
{
if (PeekMessage(&Msg, (HWND)NULL, 0, 0, PM_REMOVE))
{
_PrintMessage(_T("PeekMessage"), Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam);
TranslateMessage(&Msg);
_PrintMessage(_T("Translate"), Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam);
DispatchMessage(&Msg);
_PrintMessage(_T("Dispatch"), Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam);
if (Msg.message == WM_QUIT)
{
break;
}
}
}
return;
}
3. 将进入窗体过程函数的消息记录下来;
{
for (int ii = 0; ii < sizeof(MsgMapTable) / sizeof(*MsgMapTable); ii++)
{
if (nMessage == MsgMapTable[ii].nMessage)
{
MsgMapTable[ii].MsgProc(hWnd, wParam, lParam);
break;
}
}
_PrintMessage(_T("WndProcess"), hWnd, nMessage, wParam, lParam);
return DefWindowProc(hWnd, nMessage, wParam, lParam);
}
4. 对比一下两边的消息是否一样;
结果: 进入消息循环的会打印: PeekMessage, Translate, DispatchMessage, WndProcess
不进入消息循环的只打印: WndProcess
MsgID = FFFF 是由PostMessage发出来打印PeekMessage, Translate, DispatchMessage, WndProcess即进入了消息队列。
MsgID = FFFC 是由SendMessage发出去了我们看到进入这个函数WndProcess。
注: 大家看到结果文件中WndProcess 显示在DispatchMessage之后, 这个因为我调用DispatchMessage之后才打印,而DispatchMessage函数被调用时会系统会调用WndProcess,所以
显我们看到WndProcess显示在DispatchMessage 之前.