MFC技术系列(二)--窗口消息(1)

本文将以实际的典型场景来解释发生的消息,从而能够将窗口的消息具体化,并能连贯起来。文中的内容绝大部分为VC提供的Spy++工具获得,小部分为通过程序获得。文中显示的片段多数为一个窗口中的消息,对于发往父窗口的消息,将会附带说明。

Windows的标准消息定义位于winuser.h(%Program Files%/Microsoft Visual Studio .NET 2003/Vc7/PlatformSDK/include/winuser.h).可是,使用Spy++能够捕获所有窗口消息,经常会看到一些MSDN中未提及的消息.这一少部分消息的定义位于afxpriv.h(%Program Files%/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/include/afxpriv.h),这部分为MFC的内部消息(0x0360 ~ 0x037f),因此在MSDN中通常也找不到它们的踪迹.不过,MSDN中的"TN024: MFC-Defined Messages and Resources"解释了这些消息.

1          Mouse动作

1.1           MouseTitle bar上移动

下面的片段是MouseDialogCaption上移动时产生的片段:

<00001> 00050592 
     
      S WM_NCHITTEST
      xPos:254 yPos:170
     
<00002> 00050592 R WM_NCHITTEST nHittest:HTCAPTION
     
<00003> 00050592 
     
      S WM_SETCURSOR
      hwnd:00050592 nHittest:HTCAPTION wMouseMsg:WM_MOUSEMOVE
     
<00004> 00050592 R WM_SETCURSOR fHaltProcessing:False
     
<00005> 00050592 P WM_NCMOUSEMOVE nHittest:HTCAPTION xPos:254 yPos:170
     
<00006> 00050592 S WM_KICKIDLE
     

其中,

1.       WM_NCHITTEST:当鼠标在窗体移动时,系统会产生该消息。对于窗口的不同的区域,会返回不同的代码,。对于上述场景,为HTCAPTION,即title bar。如果是客户区,则都为HTCLIENT。其它代码都是用来区分非客户区的不同部分。

2.       WM_SETCURSOR可以用来设置光标的形状。其nHittestWM_NCHITTEST的代码,wMouseMsg参数表明了此时鼠标的动作

3.       WM_NCMOUSEMOVEpost消息,而且只有当窗口拥有光标时,系统才会发送该消息。如果一个窗口capture了光标,那么该消息不会被发送。

4.       WM_KICKIDLE为消息队列空闲时,系统发送的消息,以便程序可以在此时处理一些事情,比如:同步状态等。应用可以控制系统是否发送该消息。对对话框而言,就是设置DS_NOIDLEMSG风格。如果是主消息循环,那么CWinApp提供了OnIdle虚方法(实际为CWinThread的方法),其中lIdleCount可以用来度量消息队列空闲的时间长短。对于模态对话框,则需要响应WM_KICKIDLE消息。

1.2           MouseClient区域的移动

在鼠标进入客户区前,通常要经过非客户区,比如:窗口的Border。因此,在该场景中的消息序列中,前面的部分表明了这个过程,它重复了上面的场景,只是由于是从Border进入的,WM_NCHITTEST代码为HTBORDER。值得注意的是,如果是Resize窗口(WS_THICKFRAME或者WS_SIZEBOX),那么Border代码将具化成精确的含义的代码(HTSIZEFIRSTHTSIZELAST)。

接下来的消息序列相当简单,

<00035> 
     
      000C
     0BDC 
     
      S WM_SETCURSOR
      hwnd:
     
      000C
     0BDC nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
     
<00036> 
     
      000C
     0BDC R WM_SETCURSOR fHaltProcessing:False
     
<00037> 
     
      000C
     0BDC P WM_MOUSEMOVE fwKeys:0000 xPos:376 yPos:149
     
注意,WM_MOUSEMOVEpost消息。对于几乎所有的鼠标消息,如果mouse没有被捕获(capture),那么将被发送到包含光标的窗口,否则发送到捕获鼠标的窗口。只有前景窗口可以Capture鼠标。使用SetCapture捕获鼠标,且一个时刻只能有一个窗口Capture鼠标。 

1.3           Mouse的其它行为

鼠标的其它行为包括左、右、中键的按下(ButtonDown)和抬起(ButtonUp)。同时,又分为客户区和非客户区(NC开头)鼠标消息。这里暂且就不详细描述了(有时间,我将用专门的篇幅来解释这些行为)。补充一点,在任何鼠标动作时,都会产生WM_SETCURSOR消息,其参数之一会指明此时的鼠标动作。

2          窗口的状态

2.1           窗口销毁

以对话框为例:鼠标左键在对话框的Close按钮上点击。消息序列如下:

<00115> 001D
     
      0A
     94 
     
      S WM_SETCURSOR
      hwnd:001D
     
      0A
     94 nHittest:HTCLOSE wMouseMsg:WM_LBUTTONDOWN
     
<00116> 001D
     
      0A
     94 R WM_SETCURSOR fHaltProcessing:False
     
<00117> 001D
     
      0A
     94 P WM_NCLBUTTONDOWN nHittest:HTCLOSE xPos:439 yPos:238
     
<00118> 001D
     
      0A
     94 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:396 yPos:-12
     
<00119> 001D
     
      0A
     94 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:396 yPos:-12
     
<00120> 001D
     
      0A
     94 P WM_LBUTTONUP fwKeys:0000 xPos:396 yPos:-12
     
<00121> 001D
     
      0A
     94 
     
      S WM_CAPTURECHANGED
      hwndNewCapture:00000000
     
<00122> 001D
     
      0A
     94 R WM_CAPTURECHANGED
     
<00123> 001D
     
      0A
     94 
     
      S WM_SYSCOMMAND
      uCmdType:SC_CLOSE xPos:439 yPos:238
     
<00124> 001D
     
      0A
     94 
     
      S WM_CLOSE
     
     
<00125> 001D
     
      0A
     94 R WM_CLOSE
     
<00126> 001D
     
      0A
     94 R WM_SYSCOMMAND
     
<00127> 001D
     
      0A
     94 P WM_NCMOUSELEAVE
     
<00128> 001D
     
      0A
     94 P WM_COMMAND wNotifyCode:BN_CLICKED wID:IDCANCEL hwndCtl:00030AA0
     
<00129> 001D
     
      0A
     94 
     
      S WM_CTLCOLORBTN
      hdcButton:
     
      38010C
     99 hwndButton:
     
      00040A
     9E
     
<00130> 001D
     
      0A
     94 R WM_CTLCOLORBTN hBrush:
     
      0110005A
     
     
<00133> 001D
     
      0A
     94 
     
      S WM_SETFOCUS
      hwndLoseFocus:
     
      00040A
     9E
     
<00134> 001D
     
      0A
     94 R WM_SETFOCUS
     
<00135> 001D
     
      0A
     94 
     
      S WM_WINDOWPOSCHANGING
      lpwp:
     
      0012F
     
     
      7F
     0
     
<00136> 001D
     
      0A
     94 R WM_WINDOWPOSCHANGING
     
<00137> 001D
     
      0A
     94 
     
      S WM_WINDOWPOSCHANGED
      lpwp:
     
      0012F
     
     
      7F
     0
     
<00138> 001D
     
      0A
     94 R WM_WINDOWPOSCHANGED
     
<00139> 001D
     
      0A
     94 
     
      S WM_WINDOWPOSCHANGING
      lpwp:0012FC88
     
<00140> 001D
     
      0A
     94 R WM_WINDOWPOSCHANGING
     
<00141> 001D
     
      0A
     94 
     
      S WM_DESTROY
     
     
<00142> 001D
     
      0A
     94 R WM_DESTROY
     
<00143> 001D
     
      0A
     94 
     
      S WM_NCDESTROY
     
     
<00144> 001D
     
      0A
     94 R WM_NCDESTROY
     

其中,我们看到在按下TitleBar右上角的Close按钮后,系统还产生了WM_MOUSEMOVEWM_LBUTTONUP两个原本在客户区的鼠标消息。

接下来的消息中,<00127>比较有意思,WM_NCMOUSELEAVE,或许是因为窗口位置的改变造成的。<00128>消息实际为对对话框上的Cancel按钮的点击(00030AA0为该按钮的句柄)<00129>为针对对话框上有焦点控件的CTLCOLOR消息,此时对话框上焦点在OK按钮上。接着,就是对话框得到焦点WM_SETFOCUS消息,可以看出前一个焦点是就是CTLCOLOR消息的控件。

然后是窗口位置改变消息-->为客户区被销毁-->非客户区被销毁。WM_DESTROY发生时,表示窗口从屏幕消失,将要被销毁,此时,系统还会发送该消息到各个子窗口,此时子窗口还未被销毁。然而,WM_NCDESTROY发生时,子窗口已经被销毁,同时表示非客户区正在被销毁。该消息的响应函数中,可以释放任何已经分配的与该窗口相关的内存。

2.2           窗口激活

以对话框为例:对话框没有被激活(当前活动应用为另一个程序),鼠标左键在客户区空白区域点击。消息序列如下:

<00112> 002209E0 
     
      S WM_NCHITTEST
      xPos:345 yPos:253
     
<00113> 002209E0 R WM_NCHITTEST nHittest:HTCLIENT
     
<00114> 002209E0 
     
      S WM_MOUSEACTIVATE
      hwndTopLevel:002209E0 nHittest:HTCLIENT uMsg:WM_LBUTTONDOWN
     
<00115> 002209E0 R WM_MOUSEACTIVATE fuActivate:MA_ACTIVATE
     
<00116> 002209E0 
     
      S WM_WINDOWPOSCHANGING
      lpwp:0012FC34
     
<00117> 002209E0 R WM_WINDOWPOSCHANGING
     
<00118> 002209E0 
     
      S WM_WINDOWPOSCHANGED
      lpwp:0012FC34
     
<00119> 002209E0 R WM_WINDOWPOSCHANGED
     
<00120> 002209E0 
     
      S WM_ACTIVATEAPP
      fActive:True dwThreadID:00000000
     
<00121> 002209E0 R WM_ACTIVATEAPP
     
<00122> 002209E0 
     
      S WM_NCACTIVATE
      fActive:True
     
<00123> 002209E0 R WM_NCACTIVATE
     
<00124> 002209E0 
     
      S WM_ACTIVATE
      fActive:WA_CLICKACTIVE fMinimized:False hwndPrevious:(null)
     
<00125> 002209E0 
     
      S WM_ACTIVATETOPLEVEL
      fActive:True dwThreadID:
     
      0012F
     920
     
<00126> 002209E0 R WM_ACTIVATETOPLEVEL
     
<00127> 002209E0 
     
      S WM_CTLCOLORBTN
      hdcButton:
     
      5C
     
     
      010C
     54 hwndButton:001E
     
      0A
     80
     
<00128> 002209E0 R WM_CTLCOLORBTN hBrush:
     
      0110005A
     
     
<00129> 002209E0 R WM_ACTIVATE
     
<00130> 002209E0 
     
      S WM_SETCURSOR
      hwnd:002209E0 nHittest:HTCLIENT wMouseMsg:WM_LBUTTONDOWN
     
<00131> 002209E0 R WM_SETCURSOR fHaltProcessing:False
     
<00132> 002209E0 P WM_LBUTTONDOWN fwKeys:MK_LBUTTON xPos:289 yPos:111
     
<00133> 002209E0 
     
      S WM_NCHITTEST
      xPos:345 yPos:253
     
<00134> 002209E0 R WM_NCHITTEST nHittest:HTCLIENT
     
<00135> 002209E0 
     
      S WM_SETCURSOR
      hwnd:002209E0 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
     
<00136> 002209E0 R WM_SETCURSOR fHaltProcessing:False
     
<00137> 002209E0 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:289 yPos:111
     
<00138> 002209E0 
     
      S WM_NCHITTEST
      xPos:345 yPos:253
     
<00139> 002209E0 R WM_NCHITTEST nHittest:HTCLIENT
     
<00140> 002209E0 
     
      S WM_SETCURSOR
      hwnd:002209E0 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
     
<00141> 002209E0 R WM_SETCURSOR fHaltProcessing:False
     
<00142> 002209E0 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:289 yPos:111
     
<00143> 002209E0 
     
      S WM_CTLCOLORBTN
      hdcButton:
     
      5C
     
     
      010C
     54 hwndButton:001E
     
      0A
     80
     
<00144> 002209E0 R WM_CTLCOLORBTN hBrush:
     
      0110005A
     
     
<00145> 002209E0 
     
      S WM_NCHITTEST
      xPos:345 yPos:253
     
<00146> 002209E0 R WM_NCHITTEST nHittest:HTCLIENT
     
<00147> 002209E0 
     
      S WM_NCHITTEST
      xPos:345 yPos:253
     
<00148> 002209E0 R WM_NCHITTEST nHittest:HTCLIENT
     
<00149> 002209E0 
     
      S WM_NCHITTEST
      xPos:345 yPos:253
     
<00150> 002209E0 R WM_NCHITTEST nHittest:HTCLIENT
     
<00151> 002209E0 
     
      S WM_NCHITTEST
      xPos:345 yPos:253
     
<00152> 002209E0 R WM_NCHITTEST nHittest:HTCLIENT
     
<00153> 002209E0 
     
      S WM_SETCURSOR
      hwnd:002209E0 nHittest:HTCLIENT wMouseMsg:WM_LBUTTONUP
     
<00154> 002209E0 R WM_SETCURSOR fHaltProcessing:False
     
<00155> 002209E0 P WM_LBUTTONUP fwKeys:0000 xPos:289 yPos:111
     

其中,

1.       窗口因为鼠标点击由Inactive变为active,会产生WM_MOUSEACTIVATE。紧接着,窗口位置消息被发送。

2.       窗口激活消息组:

WM_ACTIVATEAPP:分别发送给被激活的窗口和另一个应用中失去激活状态的窗口。

WM_NCACTIVATE:指示非客户区需要响应窗口的激活。

WM_ACTIVATE:分别发送给应用中被激活和失去激活状态的窗口。激活的方式分为鼠标激活和其它方式激活,包括:键盘,该参数在该消息的wParam的低字节中。上述场景为鼠标激活。

WM_ACTIVATETOPLEVEL激活top-level窗口,但却在另一个非主线程中。(MFC内部消息, 类似WM_ACTIVATEAPP,但适用于属于不同进程的窗口混合在一个单一的窗口层次中,在OLE应用中较为普遍

3.       接着,OK按钮为默认焦点控件,需要绘制。因此,系统会发送一个WM_CTLCOLORBTN消息。如果为别的焦点控件,将会是别的CTLCOLOR消息。

4.       接着是我们熟悉的鼠标系列消息

WM_SETCURSOR,WM_LBUTTONDOWN,WM_NCHITTEST,WM_LBUTTONUP

其间,系统还会发送一个WM_CTLCOLORBTN消息。

2.3           窗口创建

2.4           窗口调整尺寸

3          绘图消息

3.1           窗口被其它窗口覆盖,其它窗口移开时

当窗口为对话框时,消息序列如下:

<00004> 
     
      000C
     0BDC 
     
      S WM_SYNCPAINT
     
     
<00005> 
     
      000C
     0BDC 
     
      S WM_NCPAINT
      hrgn:E
     
      804073A
     
     
<00006> 
     
      000C
     0BDC R WM_NCPAINT
     
<00007> 
     
      000C
     0BDC 
     
      S WM_ERASEBKGND
      hdc:18010CB0
     
<00008> 
     
      000C
     0BDC 
     
      S WM_CTLCOLORDLG
      hdcDlg:18010CB0 hwndDlg:
     
      000C
     0BDC
     
<00009> 
     
      000C
     0BDC R WM_CTLCOLORDLG hBrush:
     
      0110005A
     
     
<00010> 
     
      000C
     0BDC R WM_ERASEBKGND fErased:True
     
<00011> 
     
      000C
     0BDC R WM_SYNCPAINT
     
<00012> 
     
      000C
     0BDC P WM_PAINT hdc:00000000
     
<00013> 
     
      000C
     0BDC 
     
      S WM_CTLCOLORBTN
      hdcButton:
     
      2A
     010CCB hwndButton:000E0BDA
     
<00014> 
     
      000C
     0BDC R WM_CTLCOLORBTN hBrush:
     
      0110005A
     
     
<00015> 
     
      000C
     0BDC 
     
      S WM_CTLCOLORBTN
      hdcButton:
     
      2A
     010CCB hwndButton:
     
      001C
     0BBA
     
<00016> 
     
      000C
     0BDC R WM_CTLCOLORBTN hBrush:
     
      0110005A
     
     
其中, 
1WM_SYNCPAINT为操作系统同步不同线程的top-level窗口的绘制而发送的。应用不需要处理该消息,根据当前窗口的非客户区是否需要被绘制和背景是否必须被擦除,系统会将该消息转换成WM_NCPAINTWM_ERASEBKGND,应用可以处理这两个消息。 
2. WM_CTLCOLORDLG:在系统决定绘制对话框客户区前(从消息序列可以看出,该消息在WM_ERASEBKGND之后发出),会发送该消息,应用可以使用传入的dc,设置文本的前景和背景色。在WM_CTLCOLOR开头的消息中,唯独该消息被发送到窗口本身,其它消息均发送到控件的Owner窗口,通常也就是所在的对话框。在本消息序列最后,能看到WM_CTLCOLORBTN消息,共有两个按钮。 
3WM_PAINT:客户区绘制消息,为post消息。系统将在消息队列空闲,且存在无效区域时,发送该消息。既然是post消息,那么意味着是一种异步绘画。关于绘图,还存在一种直接(同步)绘图,请参见《DC和绘图》 

4          菜单消息

5          MFC中的消息分发

 

(未完,待续......)

 

你可能感兴趣的:(c,.net,mfc,dialog,border,resources)