《深入浅出MFC第2版(PDF格式)》第23章


消息循环中的Dispat chMessage 把消息分配到哪里呢?它透过USER 模块的协助,送到 
switch case
该窗口的窗口函数去了。窗口函数通常利用 / 方式判断消息种类,以决定处置 
方式。由于它是被Windows 系统所调用的(我们并没有在应用程序任何地方调用此函 
数),所以这是一种call back 函数,意思是指「在你的程序中,被Windows 系统调用」 
的函数。这些函数虽然由你设计,但是永远不会也不该被你调用,它们是为Windows 系 
统准备的。 
程序进行过程中,消息由输入装置,经由消息循环的抓取,源源传送给窗口并进而送到 
窗口函数去。窗口函数的体积可能很庞大,也可能很精简,依该窗口感兴趣的消息数量 
多寡而定。至于窗口函数的形式,相当一致,必然是: 
LRESULT CALLBACK WndProc (HWND hWnd; 
UINT message; 
WPARAM wParam; 
LPARAM lParam) 
switch case def ault 
注意,不论什么消息,都必须被处理,所以 / 指令中的 : 处必须调用 
Def WindowProc,这是Windows 内部预设的消息处理函数。 
窗口函数的wParam 和lParam 的意义,因消息之不同而异。wParam 在16 位环境中是 
16 位,在32 位环境中是32 位。因此,参数内容(格式)在不同操作环境中就有 
了变化。 
我想很多人都会问这个问题:为什么Windows Programming Modal 要把窗口函数设计为 
一个call back 函数?为什么不让程序在抓到消息(GetMessage )之后直接调用它就好 
了?原因是,除了你需要调用它,有很多时候操作系统也要调用你的窗口函数(例如当 
19 
…………………………………………………………Page 82……………………………………………………………
某个消息产生或某个事件发生)。窗口函数设计为callback 形式,才能开放出一个接口 
给操作系统叫用。 
消息映射 ( )的雏形 
Message Map 
有没有可能把窗口函数的内容设计得更模块化、更一般化些?下面是一种作法。请注意, 
MFC 
以下作法是 「消息映射表格」(第9章)的雏形,我所采用的结构名称和变量名称, 
都与MFC 相同,藉此让你先有个暖身。 
MSGMAP ENTR Y dim
_ 
首先,定义一个 结构和一个 宏: 
struct MSGMAP_ENTRY { 
UINT nMessage; 
LONG (*pfn)(HWND; UINT; WPARAM; LPARAM); 
}; 
#define dim (x) (sizeof(x) / sizeof(x '0')) 
MSGMAP_ENTR Y pf n
请注意 的第二元素 是一个函数指针,我准备以此指针所指之函 
数处理nMessage 消息。这正是对象导向观念中把「资料」和「处理资料的方法」封装 
起来的一种具体实现,只不过我们用的不是C++ 语言。 
接下来,组织两个数组_messageEntries ' ' 和_mandEntries ' ',把程序中欲处理的消 
息以及消息处理例程的关联性建立起来: 
// 消息与处理例程之对照表格 
struct MSGMAP_ENTRY _messageEntries'' = 
{ 
WM_CREATE; OnCreate; 
WM_PAINT; OnPaint; 
WM_SIZE; OnSize; 
WM_MAND; Onmand; 
WM_SETFOCUS; OnSetFocus; 
WM_CLOSE; OnClose; 
WM_DESTROY; OnDestroy; 
} ; ↑ ↑ 
这是消息 这是消息处理例程 
20 
…………………………………………………………Page 83……………………………………………………………
// mand…ID
与处理例程之对照表格 
struct MSGMAP_ENTRY _mandEntries = 
{ 
IDM_ABOUT; OnAbout; 
IDM_FILEOPEN; OnFileOpen; 
IDM_SAVEAS; OnSaveAs; 
} ; ↑ ↑ 
这是WM_MAND 命令项这是命令处理例程 
于是窗口函数可以这么设计: 
//………………………………………………………………………………………………………………………………………………………………………………………
// 窗口函数 
//………………………………………………………………………………………………………………………………………………………………………………………
LRESULT CALLBACK WndProc(HWND hWnd; UINT message; 
WPARAM wParam; LPARAM lParam) 
{ 
int i; 
for(i=0; i 《 dim(_messageEntries); i++) { //
消息对照表 
if (message == _messageEntries'i'。nMessage) 
return((*_messageEntries'i'。pfn)(hWnd; message; wParam; lParam)); 
} 
return(DefWindowProc(hWnd; message; wParam; lParam)); 
} 
//………………………………………………………………………………………………………………………………………………………………………………………
// Onmand … 
专门处理 WM_MAND 
//………………………………………………………………………………………………………………………………………………………………………………………
LONG Onmand(HWND hWnd; UINT message; 
WPARAM wParam; LPARAM lParam) 
{ 
int i; 
for(i=0; i 《 dim(_mandEntries); i++) { //
命令项目对照表 
if (LOWORD(wParam) == _mandEntries'i'。nMessage) 
return((*_mandEntries'i'。pfn)(hWnd; message; wParam; lParam)); 
} 
return(DefWindowProc(hWnd; message; wParam; lParam)); 
} 
//………………………………………………………………………………………………………………………………………………………………………………………
LONG OnCreate(HWND hWnd; UINT wMsg; UINT wParam; LONG lParam) 
{ 
。。。 
} 
21 
…………………………………………………………Page 84……………………………………………………………
//………………………………………………………………………………………………………………………………………………………………………………………
LONG OnAbout (HWND hWnd; UINT wMsg; UINT wParam; LONG lParam) 
{ 
。。。 
} 
//………………………………………………………………………………………………………………………………………………………………………………………
这么一来,WndProc 和Onmand 永远不必改变,每有新要处理的消息,只要在 
_messageEntries ' ' 和_mandEntries ' ' 两个数组中加上新元素,并针对新消息撰写新 
的处理例程即可。 
这种观念以及作法就是MFC 的Message M
小说推荐
返回首页返回目录