对 《windows程序设计 第5版》第11章对话框ABOUT2程序的理解

昨晚看到ABOUT2程序,感到很困惑,在网上也没有找到想要的答案,搞了半天最终才算明白。如果读者觉得我哪里理解的不对,欢迎指正。

先贴代码:

</pre><pre name="code" class="cpp">/*------------------------------------------
   ABOUT2.C -- About Box Demo Program No. 2
               (c) Charles Petzold, 1998
  ------------------------------------------*/
 
#include <windows.h>
#include "resource.h"

LRESULT CALLBACK WndProc      (HWND, UINT, WPARAM, LPARAM) ;
BOOL    CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ;
     
int iCurrentColor  = IDC_BLACK,
    iCurrentFigure = IDC_RECT ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName[] = TEXT ("About2") ;
     MSG          msg ;
     HWND         hwnd ;
     WNDCLASS     wndclass ;
     
     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (hInstance, szAppName) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = szAppName ;
     wndclass.lpszClassName = szAppName ;
     
     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"),
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }
     
     hwnd = CreateWindow (szAppName, TEXT ("About Box Demo Program"),
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ; 
     
     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}

void PaintWindow (HWND hwnd, int iColor, int iFigure)
{
     static COLORREF crColor[8] = { RGB (  0,   0, 0), RGB (  0,   0, 255),
                                    RGB (  0, 255, 0), RGB (  0, 255, 255),
                                    RGB (255,   0, 0), RGB (255,   0, 255),
                                    RGB (255, 255, 0), RGB (255, 255, 255) } ;

     HBRUSH          hBrush ;
     HDC             hdc ;
     RECT            rect ;
     
     hdc = GetDC (hwnd) ;
     GetClientRect (hwnd, &rect) ;
     hBrush = CreateSolidBrush (crColor[iColor - IDC_BLACK]) ;
     hBrush = (HBRUSH) SelectObject (hdc, hBrush) ;
     
     if (iFigure == IDC_RECT)
          Rectangle (hdc, rect.left, rect.top, rect.right, rect.bottom) ;
     else
          Ellipse   (hdc, rect.left, rect.top, rect.right, rect.bottom) ;
     
     DeleteObject (SelectObject (hdc, hBrush)) ;
     ReleaseDC (hwnd, hdc) ;
}

void PaintTheBlock (HWND hCtrl, int iColor, int iFigure)
{
     InvalidateRect (hCtrl, NULL, TRUE) ;
     UpdateWindow (hCtrl) ;
     PaintWindow (hCtrl, iColor, iFigure) ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static HINSTANCE hInstance ;
     PAINTSTRUCT      ps ;
     
     switch (message)
     {
     case WM_CREATE:
          hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;
          return 0 ;
          
     case WM_COMMAND:
		// MessageBox(hwnd,TEXT("WM_COMMAND"),NULL,NULL);
          switch (LOWORD (wParam))
          {
          case IDM_APP_ABOUT:
               if (DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc))
                    InvalidateRect (hwnd, NULL, TRUE) ;
               return 0 ;
          }
          break ;
          
     case WM_PAINT:
		// MessageBox(hwnd,TEXT("WM_PAINT"),NULL,NULL);
          BeginPaint (hwnd, &ps) ;
          EndPaint (hwnd, &ps) ;
               
          PaintWindow (hwnd, iCurrentColor, iCurrentFigure) ;
          return 0 ;
               
     case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}

BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message, 
                            WPARAM wParam, LPARAM lParam)
{
     static HWND hCtrlBlock ;
     static int  iColor, iFigure ;
     
     switch (message)
     {
     case WM_INITDIALOG:
          iColor  = iCurrentColor ;
          iFigure = iCurrentFigure ;
          
          CheckRadioButton (hDlg, IDC_BLACK, IDC_WHITE,   iColor) ;
          CheckRadioButton (hDlg, IDC_RECT,  IDC_ELLIPSE, iFigure) ;
          
          hCtrlBlock = GetDlgItem (hDlg, IDC_PAINT) ;
          
          SetFocus (GetDlgItem (hDlg, iColor)) ;
          return FALSE ;
          
     case WM_COMMAND:
          switch (LOWORD (wParam))
          {
          case IDOK:
               iCurrentColor  = iColor ;
               iCurrentFigure = iFigure ;
               EndDialog (hDlg, TRUE) ;
               return TRUE ;
               
          case IDCANCEL:
               EndDialog (hDlg, FALSE) ;
               return TRUE ;
               
          case IDC_BLACK:
          case IDC_RED:
          case IDC_GREEN:
          case IDC_YELLOW:
          case IDC_BLUE:
          case IDC_MAGENTA:
          case IDC_CYAN:
          case IDC_WHITE:
               iColor = LOWORD (wParam) ;
               CheckRadioButton (hDlg, IDC_BLACK, IDC_WHITE, LOWORD (wParam)) ;
               PaintTheBlock (hCtrlBlock, iColor, iFigure) ;
               return TRUE ;
               
          case IDC_RECT:
          case IDC_ELLIPSE:
               iFigure = LOWORD (wParam) ;
               CheckRadioButton (hDlg, IDC_RECT, IDC_ELLIPSE, LOWORD (wParam)) ;
               PaintTheBlock (hCtrlBlock, iColor, iFigure) ;
               return TRUE ;
          }
          break ;
          
     case WM_PAINT:
		// MessageBox(hDlg,TEXT("对话框WM_PAINT"),NULL,NULL);
          PaintTheBlock (hCtrlBlock, iColor, iFigure) ;
          break ;
     }
     return FALSE ;
}


最初看这段代码时有很多疑问,最主要的是下面两个疑问:

1.  182行中,对话框接收WM_PAINT消息调用 PaintTheBlock (hCtrlBlock, iColor, iFigure) ;而 PaintTheBlock函数中第一句就调用了 InvalidateRect。我们知道InvalidateRect总是产生一个WM_PAINT消息,那这样的话不就产生了一个死循环了吗?虽然有人会说WM_PAINT消息是低优先级,程序遇到InvalidateRect不会停留直接跳过,但是我想作为这么经典的教科书,作者不会留下这样的bug。

2.  父窗口是怎么重画它的窗口的呢?换句话说,当对话框退出后,父窗口怎么接收到WM_PAINT消息的?


对于第一个问题的解释:

        其实我们被对话框窗口过程中的WM_PAINT消息误导了!之前我们在窗口过程中编写WM_PAINT消息的处理过程,然后当窗口过程接收到WM_PAINT消息后,它总是按照我们的意愿进行处理。然而我们似乎忘记了作者在前面讲对话框消息中的几句话了:1.WM_INITDLALOG是对话框过程接收到的第一条消息;2.除上述消息以外,对话框过程只处理WM_COMMAND。(见《windows程序设计(第5版 珍藏版)》392页)。看到这里才知道为什么184行的WM_PAINT消息与85行的 InvalidateRect不会构成死循环。那么问题来了,挖掘机技术...呃,不对,那对话框中的WM_PAINT消息是干什么用的呢?我用183行的MessageBox测试时发现,只有对话框刚创建时才会接收到WM_PAINT消息,后面就不会再接收WM_PAINT消息了。那么问题又来了,对话框是如果画那个矩形和椭圆的呢?85行的InvalidateRect(hCtrl, NULL, TRUE)好像给我们提供了答案。原书314页指出:InvalidateRect函数会让windows把WM_PAINT消息放在窗口过程的消息队列中。WM_PAINT消息在windows中的默认处理方式仅仅是调用BeginPaint和EndPaint来使窗口有效。(对话框窗口过程除了刚创建时接收一次WM_PAINT消息,以后都不再通过窗口过程中自己定义的过程,而丢给了windows的默认处理方式)。因为InvalidateRect第三个参数指定了旧背景应该被删除,所以调用BeginPaint会使windows产生一条WM_ERASEBKGND(擦除背景)消息。windows会对该消息进行处理,它使用窗口类指定的画刷来擦除这个客户区背景。代码中170行和177行的PaintTheBlock第一个参数是由142行的GetDlgItem (hDlg, IDC_PAINT) 获取到的句柄。在ABOUT2程序中, IDC_PAINT指定的是一个空白文本控件(在资源文件ABOUT2.RC中定义的),所以windows会用空白的画刷来擦除这个文本区(即绘图区)。而后调用PaintWindow (hCtrl, iColor, iFigure) 画出图形。如果把 InvalidateRect注释掉,那么windows就不会用空白的画刷来擦除背景,这对于画矩形没有什么影响,但是如果画椭圆的话,你会发现四个角上还留着原来的背景颜色。


对于第二个问题的解释:

对话框退出后,退出码如果是IDOK,那么它就会给父窗口发送一个WM_PAINT消息,然后父窗口接收到WM_PAINT消息后自己重画。具体对话框是如何给父窗口发送WM_PAINT消息的,这个问题我还不清楚,想解决这个问题还需要看windows内部机制。如果退出码是IDCANCLE,则父窗口不会接收到WM_PAINT消息。这个我们可以理解,因为点击“取消”的话就表明没有做出修改,那么父窗口也不需要做出什么其他动作。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值