在Win32模式下,消息传递的主要函数有:PostMessage、SendMessage、GetMessage、PeekMessage和WaitMessage。其中PostMessage和SendMessage是消息的投递和发送函数,而GetMessage、PeekMessage和WaitMessage是消息的接收函数。在VC中进行Win32编程时,要经常遇到到一段代码如下:
// 主消息循环
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
示意代码段1
VC中称之为主消息循环,它的工作模式为:利用while循环,GetMessage按照时间次序,不断地从进程消息队列中读取消息,读取的方式是从消息队列的指定消息中拷贝数据到传入的msg消息变量中,完成第一步。如果进程消息队列为空,则GetMessage一直处于等待状态,直到有消息到来后,GetMessage才会返回,返回值为非零值。如果GetMessage接到的消息为WM_QUIT,则函数返回零值,如果函数返回零值,则while循环就会结束,进而应用程序也会终结。
根据代码流程,被GetMessage所取得msg消息变量被传入到函数TranslateAccelerator中,而TranslateAcclerator要判断消息是否为菜单命令消息或者加速键消息(什么叫做加速键消息?用过Word的朋友都知道,当你选中文档中的一段文字后,选择菜单编辑—>复制,跟同时按下CTRL和C键,所起作用是一样的,所以称Ctrl + C就是复制的加速键,也叫快捷键)。
如果判断是,则该函数将把WM_KEYDOWN或者WM_SYSKEYDOWN转换成一个WM_COMMAND消息,根据msg.hWnd的参数值,将其发送给指定窗口的事件发布函数,直到窗口事件发布函数处理完该WM_COMMAND消息之后,TranslateAccelerator才返回。处理了加速键之后,系统继续while循环,执行新的一轮GetMessage。
如果判断不是,则执行TranslateMessage函数,该函数将虚拟键消息转换为字符消息,字符消息被寄送到调用线程的消息队列里,当下一次线程调用函数GetMessage或PeekMessage时被读出(TranslateMessage 只能用于转换该while循环所接收的消息)。经过TranslateMessage处理之后(其实消息并没有发生变化),消息还要经过DispatchMessage的处理,该函数把消息发送给窗口的WndProc()函数。而所谓的窗口的WndProc函数就是事件发布函数,如示意代码段2所示。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND: // 命令消息
wmId = LOWORD(wParam); wmEvent = HIWORD(wParam);
// 解析具体命令
switch (wmId)
{
case IDM_ABOUT: // 打开关于对话框
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT: // 发出退出命令
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT: // 窗口消息
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY: // 通知消息(也包含了窗口消息成分)
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
示意代码段2
GetMessage函数的工作状态还跟其参数有关,该函数用来获取与hWnd参数所指定的窗口相关的且wMsgFilterMin和wMsgFilterMax参数所限范围内的消息。如果hWnd为空,则GetMessage获取所属进程内的消息,如果wMsgFilterMin和wMsgFilterMax都是0(按照字面意思就是不经过消息过滤),则GetMessage可获取进程内所有的消息。例如在示意代码段2中,GetMessage就是为获取整个进程内所有的消息的。GetMessage函数获取消息之后,将会使该消息被消息队列所删除(WM_PAINT消息除外,至于WM_PAINT则只有在其被合并处理之后才被删除)。GetMessage只检索呼叫进程的消息,不检索属于其它进程的消息。