從Windows訊息的角度看視窗應用程式的運行過程

來源:互聯網
上載者:User
一個典型的Win32視窗應用程式的架構是這樣的:
    程式進入點(WinMain函數)-->註冊視窗類別(調用RegisterClass函數或RegisterClassEx函數)-->建立主視窗(調用CreateWindow函數或CreateWindowEx函數)-->顯示主視窗(調用ShowWindow函數)-->更新主視窗(調用UpdateWindow函數)-->進入訊息迴圈(GetMessage、TranslateMessage、DispatchMessage)並處理各種Windows訊息(視窗過程函數)-->程式出口點(WinMain返回)。就像下面這個例子一樣:

#include<windows.h>
#include<tchar.h>
//視窗類別名和視窗標題
TCHAR szWindowClass[]=_T("HELLOWINDOWS");
TCHAR szWindowTitle[]=_T("This is the MAIN window");
//視窗過程函數
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
 //LogMessage(logfile,msg,wParam,lParam);//
 switch(msg)
 {
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 default:
  return DefWindowProc(hWnd,msg,wParam,lParam);
 }
}

int WINAPI _tWinMain( HINSTANCE hInstance,HINSTANCE,LPTSTR lpCmdLine,int nCmdShow)
{
 //註冊視窗類別
 WNDCLASSEX wcex;
 wcex.cbSize   = sizeof(WNDCLASSEX);
 wcex.style  = CS_HREDRAW | CS_VREDRAW;
 wcex.lpfnWndProc = (WNDPROC)WndProc;
 wcex.cbClsExtra  = 0;
 wcex.cbWndExtra  = 0;
 wcex.hInstance  = hInstance;
 wcex.hIcon   = NULL;
 wcex.hCursor  = LoadCursor(NULL, IDC_ARROW);
 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
 wcex.lpszMenuName = NULL;
 wcex.lpszClassName = szWindowClass;
 wcex.hIconSm  = NULL;
 RegisterClassEx(&wcex);
 //建立主視窗
 HWND hWnd = CreateWindowEx(0,szWindowClass, szWindowTitle, WS_OVERLAPPEDWINDOW,
  128, 96, 512, 480, HWND_DESKTOP, NULL, hInstance, NULL);
 
 if (!hWnd)
  return FALSE;
 //顯示並更新主視窗
 ShowWindow(hWnd,nCmdShow);
 UpdateWindow(hWnd);
 // 進入訊息迴圈
 MSG msg;
 while (GetMessage(&msg, NULL, 0, 0))
 {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }
 return msg.wParam;
 //程式退出
}

    這個例子很十分簡單,只是顯示一個視窗就完事了;不過麻雀雖小,五髒俱全,它基本上可以作為一個Win32視窗程序的架構了。
下面來看它的視窗過程WndProc,它只處理了一個訊息:WM_DESTROY,其餘的訊息都交給了Windows去處理(調用DefWindowProc)。對於一個實際的Windows程式來說,要在視窗過程中處理的訊息會很多;然而Windows訊息成百上千,無論你處理多少訊息,剩下的你還是得調用DefWindowProc交給Windows系統去處理。這次我們就來看看,從程式啟動到退出,DefWindowProc到底要幫我們做多少的工作。
    實驗的思想很簡單,把所有傳遞給視窗過程的訊息都記錄在一個Log檔案中,我們就可以察看在一個程式的生命過程中的所有訊息了。在上面的例子中,我們在視窗函數WndProc的最開始調用一個方法(形如注釋掉的那一行:LogMessage(logfile,msg,wParam,lParam)),把傳遞來的訊息類型,WPARAM參數,LPARAM參數順序都記錄下來,就會形成一個WIndows訊息Log檔案了。
    下面是兩次實驗的結果(假設上面的例子編譯後得到HelloWin.EXE):
     實驗一:在檔案管理其中選中HelloWin.EXE,按斷行符號鍵啟動,顯示主視窗後馬上按下ALT+F4把它關閉,得到的LOG檔案如下:

時間                   訊息碼        參數                 參數                  描述
HH:MM:SS.MSS    MSG       WPARAM        LPARAM         DECRIPTION
03:21:39.187    0x0024    0x00000000    0x0012F910     WM_GETMINMAXINFO  擷取最大化最小化資訊
03:21:39.187    0x0081    0x00000000    0x0012F908     WM_NCCREATE  視窗非客戶區被建立
03:21:39.187    0x0083    0x00000000    0x0012F930     WM_NCCALCSIZE  計算非客戶區的大小
(WPARAM:FALSE->不必指出視窗客戶區的有效地區)
03:21:39.187    0x0001    0x00000000    0x0012F8D4     WM_CREATE  主視窗被建立
03:21:39.187    0x0018    0x00000001    0x00000000     WM_SHOWWINDOW  
(WPARAM:TRUE->顯示視窗 LPARAM:0->指出這個訊息是調用ShowWindow函數發來的)
03:21:39.187    0x0046    0x00000000    0x0012FEB0     WM_WINDOWPOSCHANGING 視窗位置(包括大小)正在改變
03:21:39.203    0x001C    0x00000001    0x00000584     WM_ACTIVATEAPP  視窗進程啟用狀態改變
(WPARAM:TRUE->Activate啟用 LPARAM:線程ID=0x0584)
03:21:39.203    0x0086    0x00000001    0x00000000     WM_NCACTIVATE  非客戶區的啟用狀態需要改變
(WPARAM:TRUE->Activate啟用)
03:21:39.203    0x007F    0x00000002    0x00000000     WM_GETICON  擷取小表徵圖2
(WPARAM:2---->ICON_SMALL2)
03:21:39.203    0x007F    0x00000000    0x00000000     WM_GETICON  擷取小表徵圖
(WPARAM:0---->ICON_SMALL)
03:21:39.203    0x007F    0x00000001    0x00000000     WM_GETICON  擷取大表徵圖
(WPARAM:1---->ICON_BIG)
03:21:39.203    0x0006    0x00000001    0x00000000     WM_ACTIVATE  視窗啟用狀態改變
(WPARAM:1---->WA_ACTIVE啟用)
03:21:39.203    0x0281    0x00000001    0xC000000F     WM_IME_SETCONTEXT IMETRUE->Active 
03:21:39.203    0x0282    0x00000002    0x00000000     WM_IME_NOTIFY  IMEIMN_OPENSTATUSWINDOW
03:21:39.203    0x0007    0x00000000    0x00000000     WM_SETFOCUS  視窗獲得輸入焦點
03:21:39.218    0x0085    0x00000001    0x00000000     WM_NCPAINT  非客戶區需要重畫
(WPARAM:1---->整個視窗(Window Frame)都需要重畫
03:21:39.218    0x0014    0x0101005D    0x00000000     WM_ERASEBKGND  擦除背景
03:21:39.218    0x0047    0x00000000    0x0012FEB0     WM_WINDOWPOSCHANGED 視窗位置(包括大小)已經改變
03:21:39.218    0x0083    0x00000001    0x0012FAEC     WM_NCCALCSIZE  計算非客戶區的大小
(WPARAM:TRUE->NCCALCSIZE_PARAMS參數有效(根據該參數來計算重畫地區)
03:21:39.218    0x0085    0x00000001    0x00000000     WM_NCPAINT  非客戶區需要重畫
(WPARAM:1---->整個視窗(Window Frame)都需要重畫
03:21:39.218    0x0014    0x890109B8    0x00000000     WM_ERASEBKGND  擦除背景
03:21:39.218    0x0005    0x00000000    0x01BE01F8     WM_SIZE   視窗大小已經改變
(WPARAM:0->SIZE_RESTORED 既不是最大化也不是最小化(MSDN如此說……))
03:21:39.218    0x0003    0x00000000    0x007E0084     WM_MOVE   視窗已被移動
(LPARAM:x=84[132] y=7e[126] 指出了視窗客戶區的新座標)
03:21:39.218    0x000F    0x00000000    0x00000000     WM_PAINT   視窗客戶區需要重畫
03:21:39.218    0x007F    0x00000002    0x00000000     WM_GETICON  2---->ICON_SMALL2
03:21:39.218    0x007F    0x00000000    0x00000000     WM_GETICON  0---->ICON_SMALL
03:21:39.218    0x007F    0x00000001    0x00000000     WM_GETICON  1---->ICON_BIG
===================================================================================
03:21:39.375    0x0101    0x0000000D    0xC01C0001     WM_KEYUP   鍵盤有按鍵被按下
(WPARAM:0D--->VK_RETURN(斷行符號鍵) LPARAM:RECNT:1;SCCODE:1C;NON-EXKEY)
03:21:41.000    0x0104    0x00000012    0x20380001     WM_SYSKEYDOWN  系統鍵被按下
(WPARAM:12--->VK_MENU(ALT鍵)    LPARAM:RECNT:1;SCCODE:38;ALT-DOWN)
03:21:41.609    0x0104    0x00000073    0x203E0001     WM_SYSKEYDOWN  系統鍵被按下
(WPARAM:73--->VK_F4(F4鍵)     LPARAM:RECNT:1;SCCODE:3E;ALT-DOWN(ALT鍵同時被按下))
=======================================================================================
03:21:41.609    0x0112    0x0000F060    0x00000000     WM_SYSCOMMAND  系統命令
(WPARAM:F060->SC_CLOSE)
03:21:41.609    0x0010    0x00000000    0x00000000     WM_CLOSE   視窗需要被關閉
03:21:41.609    0x0046    0x00000000    0x0012F8A0     WM_WINDOWPOSCHANGING 視窗位置(包括大小)正在改變
03:21:41.609    0x0047    0x00000000    0x0012F8A0     WM_WINDOWPOSCHANGED 視窗位置(包括大小)已經改變
03:21:41.609    0x0086    0x00000000    0x00000000     WM_NCACTIVATE  視窗非客戶區啟用狀態改變
(WPARAM:FALSE->InActivate非啟用狀態)
03:21:41.625    0x0006    0x00000000    0x00000000     WM_ACTIVATE  視窗啟用狀態改變
(WPARAM:0----->WA_INACTIVATE非啟用狀態)
03:21:41.625    0x001C    0x00000000    0x00000584     WM_ACTIVATEAPP  視窗進程啟用狀態改變
(WPARAM:FALSE->InActivate   TID=0584)
03:21:41.625    0x0008    0x00000000    0x00000000     WM_KILLFOCUS  視窗失去輸入焦點
03:21:41.625    0x0281    0x00000000    0xC000000F     WM_IME_SETCONTEXT IMEFALSE->InActivate
03:21:41.625    0x0282    0x00000001    0x00000000     WM_IME_NOTIFY  IMEIMN_CLOSESTATUSWINDOW
03:21:41.625    0x0002    0x00000000    0x00000000     WM_DESTROY  視窗被銷毀 
03:21:41.625    0x0082    0x00000000    0x00000000     WM_NCDESTROY  視窗非客戶區被銷毀

   實驗二:在檔案管理其中用滑鼠雙擊HelloWin.EXE啟動程式,顯示主視窗後馬上移動滑鼠到視窗右上方的關閉按鈕把它關閉,得到的LOG檔案如下:

時間                    訊息碼       參數                   參數                 描述
HH:MM:SS.MSS    MSG       WPARAM        LPARAM         DECRIPTION
04:29:01.421    0x0024    0x00000000    0x0012F910     WM_GETMINMAXINFO
04:29:01.421    0x0081    0x00000000    0x0012F908     WM_NCCREATE
04:29:01.421    0x0083    0x00000000    0x0012F930     WM_NCCALCSIZE
04:29:01.421    0x0001    0x00000000    0x0012F8D4     WM_CREATE
04:29:01.421    0x0018    0x00000001    0x00000000     WM_SHOWWINDOW
04:29:01.421    0x0046    0x00000000    0x0012FEB0     WM_WINDOWPOSCHANGING
04:29:01.437    0x001C    0x00000001    0x00000584     WM_ACTIVATEAPP
04:29:01.437    0x0086    0x00000001    0x00000000     WM_NCACTIVATE
04:29:01.437    0x007F    0x00000002    0x00000000     WM_GETICON
04:29:01.437    0x007F    0x00000000    0x00000000     WM_GETICON
04:29:01.437    0x007F    0x00000001    0x00000000     WM_GETICON
04:29:01.437    0x0006    0x00000001    0x00000000     WM_ACTIVATE
04:29:01.437    0x0281    0x00000001    0xC000000F     WM_IME_SETCONTEXT
04:29:01.437    0x0282    0x00000002    0x00000000     WM_IME_NOTIFY
04:29:01.437    0x0007    0x00000000    0x00000000     WM_SETFOCUS
04:29:01.437    0x0085    0x00000001    0x00000000     WM_NCPAINT
04:29:01.437    0x0014    0x840108B2    0x00000000     WM_ERASEBKGND
04:29:01.437    0x0047    0x00000000    0x0012FEB0     WM_WINDOWPOSCHANGED
04:29:01.437    0x0083    0x00000001    0x0012FAEC     WM_NCCALCSIZE
04:29:01.437    0x0085    0x00000001    0x00000000     WM_NCPAINT
04:29:01.437    0x0014    0x0F01093A    0x00000000     WM_ERASEBKGND
04:29:01.437    0x0005    0x00000000    0x01BE01F8     WM_SIZE
04:29:01.437    0x0003    0x00000000    0x007E0084     WM_MOVE
04:29:01.437    0x000F    0x00000000    0x00000000     WM_PAINT
04:29:01.437    0x007F    0x00000002    0x00000000     WM_GETICON
04:29:01.437    0x007F    0x00000000    0x00000000     WM_GETICON
04:29:01.437    0x007F    0x00000001    0x00000000     WM_GETICON
========================================================================================
04:29:01.828    0x0084    0x00000000    0x009E0266     WM_NCHITTEST 游標移動或滑鼠鍵被按下 
(LPARAM:當前游標位置(相對於螢幕左上方)x=0266[614] y=009E[236])
04:29:01.828    0x0020    0x00A40268    0x02000001     WM_SETCURSOR 滑鼠導致的游標移動
(LPARAM:Hit-Test碼:01->HTCLIENT在客戶區 滑鼠訊息碼:0200->MOUSEMOVE滑鼠移動)
04:29:01.828    0x0200    0x00000000    0x002001E2     WM_MOUSEMOVE 滑鼠移動
(LPARAM:滑鼠當前位置 x=01E2[482] y=0020[32])
*************此處省略N個[WM_NCHITTEST-WM_SETCURSOR-WM_MOUSEMOVE]訊息***************
04:29:01.984    0x0084    0x00000000    0x007F0268     WM_NCHITTEST 
(LPARAM:當前游標位置(相對於螢幕左上方)x=0268[616] y=007F[127])
04:29:01.984    0x0020    0x00A40268    0x02000001     WM_SETCURSOR 
(LPARAM:Hit-Test碼:01->HTCLIENT在客戶區 滑鼠訊息碼:0200->MOUSEMOVE滑鼠移動)
04:29:01.984    0x0200    0x00000000    0x000101E4     WM_MOUSEMOVE 
(LPARAM:滑鼠當前位置x=01E4[484] y=0001[1])
04:29:02.000    0x0084    0x00000000    0x007D0269     WM_NCHITTEST 
(LPARAM:當前游標位置(相對於螢幕左上方)x=0269[617] y=007D[125])
04:29:02.000    0x0020    0x00A40268    0x02000002     WM_SETCURSOR 滑鼠導致的游標移動
(LPARAM:Hit-Test碼:02->HTCAPTION在標題區 滑鼠訊息碼:0200->MOUSEMOVE滑鼠移動)
04:29:02.000    0x00A0    0x00000002    0x007D0269     WM_NCMOUSEMOVE 非客戶區的滑鼠移動
(WPARAM:Hit-Test碼:02->HTCAPTION LPARAM:游標位置:x=0269[617] y=007D[125])
************此處省略N個[WM_NCHITTEST-WM_SETCURSOR-WM_MOUSEMOVE]訊息******************
04:29:02.234    0x0084    0x00000000    0x0075026D     WM_NCHITTEST 
(LPARAM:當前游標位置(相對於螢幕左上方)x=026D[621] y=0075[117])
04:29:02.234    0x0020    0x00A40268    0x02000014     WM_SETCURSOR 
(LPARAM:Hit-Test碼:14->HTTOPRIGHT在右上方 滑鼠訊息碼:0200->MOUSEMOVE滑鼠移動)
04:29:02.234    0x00A0    0x00000014    0x0075026D     WM_NCMOUSEMOVE 
(WPARAM:Hit-Test碼:14->HTTOPRIGHT     LPARAM:游標位置:x=026D[621] y=0075[117]
04:29:02.296    0x0084    0x00000000    0x0075026D     WM_NCHITTEST 
(LPARAM:當前游標位置(相對於螢幕左上方)x=026D[621] y=0075[117])
04:29:02.296    0x0020    0x00A40268    0x02010014     WM_SETCURSOR 
(LPARAM:Hit-Test碼:14->HTTOPRIGHT在右上方 滑鼠訊息碼:0201->LBUTTONDOWN 滑鼠左鍵被按下
04:29:02.296    0x00A1    0x00000014    0x0075026D     WM_NCLBUTTONDOWN  滑鼠左鍵在非客戶區按下
(LPARAM:當前游標位置(相對於螢幕左上方)x=026D[621] y=0075[117])
04:29:02.484    0x0215    0x00000000    0x00000000     WM_CAPTURECHANGED   正在失去滑鼠捕獲
(LPARAM:0---->HWND_DESKTOP 案頭將要獲得滑鼠)
===========================================================================================
04:29:02.484    0x0112    0x0000F060    0x0075026D     WM_SYSCOMMAND F060-->SC_CLOSE
04:29:02.484    0x0010    0x00000000    0x00000000     WM_CLOSE
04:29:02.484    0x0046    0x00000000    0x0012F5E4     WM_WINDOWPOSCHANGING
04:29:02.484    0x0047    0x00000000    0x0012F5E4     WM_WINDOWPOSCHANGED
04:29:02.484    0x0086    0x00000000    0x00000000     WM_NCACTIVATE
04:29:02.484    0x0006    0x00000000    0x00000000     WM_ACTIVATE
04:29:02.484    0x001C    0x00000000    0x00000584     WM_ACTIVATEAPP
04:29:02.484    0x0008    0x00000000    0x00000000     WM_KILLFOCUS
04:29:02.484    0x0281    0x00000000    0xC000000F     WM_IME_SETCONTEXT
04:29:02.484    0x0282    0x00000001    0x00000000     WM_IME_NOTIFY
04:29:02.484    0x0002    0x00000000    0x00000000     WM_DESTROY
04:29:02.484    0x0082    0x00000000    0x00000000     WM_NCDESTROY
     在實驗二中,打星號(*)的部分省略了很多個[WM_NCHITTEST-WM_SETCURSOR-WM_MOUSEMOVE]訊息,滑鼠移動過程中它們一直產生,唯一不同的是指示的滑鼠位置不同。
     由上兩個LOG檔案記錄的Windows訊息可以對比看出,從我們開始啟動程式(無論是從鍵盤還是滑鼠雙擊啟動)到主視窗最終顯示在我們面前,Windows已經處理了從開始的WM_GETMINMAXINFO訊息到第一條雙劃線前的WM_GETICON訊息;而從我們關閉一個視窗(無論是按下ALT+F4還是滑鼠點擊“關閉”按鈕)到程式最後退出,Windows要處理從第二條雙劃線後的WM_SYSCOMMAND到最後的WM_NCDESTROY訊息。而程式在運行過程中產生的訊息在兩條雙劃線之間(有上面兩個LOG檔案可以看出區別來,跟程式的行為相關的)。值得一提的是程式最後收到的一條訊息WM_QUIT,是由PostQuitMessage函數發送的,在訊息迴圈中被GetMessage捕獲,GetMessage函數捕獲到WM_QUIT訊息之後返回FALSE,於是WinMain函數退出訊息迴圈,是不交給視窗過程去處理的,所以上面沒有記錄下來;之後程式便退出了。
     在寫windows程式的時候,我們也可以通過記錄windows訊息的方式去偵錯工具的,至於怎麼來我就不羅嗦啦:)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.