下面我们举一个常见的小例子来说明这个消息泵的运用:
if (::PeekMessage(&msg, m_hWnd, WM_KEYFIRST,WM_KEYLAST, PM_REMOVE)){if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE)...}
这里我们接受所有的键盘消息,所以就用WM_KEYFIRST 和 WM_KEYLAST作为参数。最后一个参数可以是PM_NOREMOVE 或者 PM_REMOVE,表示消息信息是否应该从消息队列中删除。
所以这段小代码就是判断是否按下了Esc键,如果是就进行处理。
窗口过程窗口过程是一个用于处理所有发送到这个窗口的消息的函数。任何一个窗口类都有一个窗口过程。同一个类的窗口使用同样的窗口过程来响应消息。 系统发送消息给窗口过程将消息数据作为参数传递给他,消息到来之后,按照消息类型排序进行处理,其中的参数则用来区分不同的消息,窗口过程使用参数产生合适行为。
一个窗口过程不经常忽略消息,如果他不处理,它会将消息传回到执行默认的处理。窗口过程通过调用DefWindowProc来做这个处理。窗口过程必须return一个值作为它的消息处理结果。大多数窗口只处理小部分消息和将其他的通过DefWindowProc传递给系统做默认的处理。窗口过程被所有属于同一个类的窗口共享,能为不同的窗口处理消息。下面我们来看一下具体的实例:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){int wmId, wmEvent;PAINTSTRUCT ps;HDC hdc;TCHAR szHello[MAX_LOADSTRING];LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);switch (message){case WM_COMMAND:
wmId = LOWORD(wParam);wmEvent = HIWORD(wParam);// Parse the menu selections:
switch (wmId){case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)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...
RECT rt;GetClientRect(hWnd, &rt);DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);EndPaint(hWnd, &ps);break;case WM_DESTROY:
PostQuitMessage(0);break;default:
return DefWindowProc(hWnd, message, wParam, lParam);}
return 0;}
消息分流器通常的窗口过程是通过一个switch语句来实现的,这个事情很烦,有没有更简便的方法呢?有,那就是消息分流器,利用消息分流器,我们可以把switch语句分成更小的函数,每一个消息都对应一个小函数,这样做的好处就是对消息更容易管理。
之所以被称为消息分流器,就是因为它可以对任何消息进行分流。下面我们做一个函数就很清楚了:
void MsgCracker(HWND hWnd,int id,HWND hWndCtl,UINT codeNotify){switch(id){case ID_A:
if(codeNotify==EN_CHANGE)...
break;case ID_B:
if(codeNotify==BN_CLICKED)...
break;....
}
}
然后我们修改一下窗口过程:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){switch(message){HANDLE_MSG(hWnd,WM_COMMAND,MsgCracker);HANDLE_MSG(hWnd,WM_DESTROY,MsgCracker);default:
return DefWindowProc(hWnd, message, wParam, lParam);}
return 0;}
在WindowsX.h中定义了如下的HANDLE_MSG宏:
#define HANDLE_MSG(hwnd,msg,fn) \switch(msg): return HANDLE_##msg((hwnd),(wParam),(lParam),(fn));实际上,HANDLE_WM_XXXX都是宏,例如:HANDLE_MSG(hWnd,WM_COMMAND,MsgCracker);将被转换成如下定义:
#define HANDLE_WM_COMMAND(hwnd,wParam,lParam,fn)\((fn)((hwnd),(int)(LOWORD(wParam)),(HWND)(lParam),(UINT)HIWORD(wParam)),0L);好了,事情到了这一步,应该一切都明朗了。
不过,我们发现在windowsx.h里面还有一个宏:FORWARD_WM_XXXX,我们还是那WM_COMMAND为例,进行分析:
#define FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, fn) \(void)(fn)((hwnd), WM_COMMAND, MAKEWPARAM((UINT)(id),(UINT)(codeNotify)), (LPARAM)(HWND)(hwndCtl))所以实际上,FORWARD_WM_XXXX将消息参数进行了重新构造,生成了wParam && lParam,然后调用了我们定义的函数。
好了,事情到这里也算是也段落了,下次我们在分析消息在MFC中的处理。
前面,我们分析了消息的基本理论和基本的函数及用法,接下来,我们将进一步讨论消息传递在MFC中的实现。