1. 程式人生 > >Win32訊息機制(視窗實現過程)

Win32訊息機制(視窗實現過程)

Dos的過程驅動與Windows的事件驅動

在講本程式的訊息迴圈之前,我想先談一下Dos與Windows驅動機制的區別:

DOS程式主要使用順序的,過程驅動的程式設計方法。順序的,過程驅動的程式有一個明顯的開始,明顯的過程及一個明顯的結束,因此程式能直接控制程式事件或過程的順序。雖然在順序的過程驅動的程式中也有很多處理異常的方法,但這樣的異常處理也仍然是順序的,過程驅動的結構。

而Windows的驅動方式是事件驅動,就是不由事件的順序來控制,而是由事件的發生來控制,所有的事件是無序的,所為一個程式設計師,在你編寫程式時,你並不知道使用者先按哪個按紐,也不知道程式先觸發哪個訊息。你的任務就是對正在開發的應用程式要發出或要接收的訊息進行排序和管理。事件驅動程式設計是密切圍繞訊息的產生與處理而展開的,一條訊息是關於發生的事件的訊息。

Windows程式設計的特點:

C語言程式設計至少有一個主程式,其名字是main()。Windows程式則至少兩個主程式,一個是WinMain(),

int WINAPI WinMain(

          HINSTANCE hInstance,   // handle to current instance

          HINSTANCE hPrevInstance,  // handle to previous instance

          LPSTR lpCmdLine,     // command line

          int nCmdShow     // show state

         );

另一個是視窗過程函式WndProc,它的函式原型為:

long FAR PASCAL WndProc(HWND hWnd,WORD message,WORD wParam,LONG lParam);

Windows應用程式的程式設計就圍繞這兩個部份進行的。其中WinMain函式為應用程式的入口點,它的名字一定要是WinMain。

在Windows中,應用程式通過要求Windows完成指定操作,而承擔這項通訊任務的API函式就是Windows的相應視窗函式WndProc。在dos裡,程式能直接控制事件的發生順序,結果等。而在Windows裡,應用程式不直接呼叫任何視窗函式,而是等待Windows呼叫視窗函式,請求完成任務或返回資訊。為保證Windows呼叫這個視窗函式,這個函式必須先向Windows登記,然後在Windows實施相應操作時回撥,所以視窗函式又稱為回撥函式。WndProc是一個主回撥函式,Windows至少有一個回撥函式。

回撥函式WndProc在哪裡定義的呢,請看這個語句:wc.lpfnWndProc = WndProc ;將在第七講裡詳談.

例項:在Windows中,能多次同時運行同一個應用程式,即執行多個副本,每個副本叫做一個“例項”。

現在讓我們把這個程式層層剝解開來,我把自己的理解慢慢地展示給你:

我把這個程式支解為7塊:(一)建立,註冊視窗類.(二)建立視窗.(三)顯示和更新視窗.(四)建立訊息迴圈.(五)終止應用程式.(六)視窗過程.(七)處理訊息.

(一)註冊視窗類:

(1)建立視窗類

WinMain()是程式的入口,它相當於一箇中介人的角色,把應用程式(指小視窗)介紹給windows.首要的一步是登記應用程式的視窗類.

視窗種類是定義視窗屬性的模板,這些屬性包括視窗式樣,滑鼠形狀,選單等等,視窗種類也指定處理該類中所有視窗訊息的視窗函式.只有先建立視窗種類,才能根據視窗種類來建立Windows應用程式的一個或多個視窗.建立視窗時,還可以指定視窗獨有的附加特性.視窗種類簡稱視窗類,視窗類不能重名.在建立視窗類後,必須向Windows登記.

建立視窗類就是用WNDCLASS結構定義一個結構變數,在這個程式中就是指 WNDCLASS wc ;然後用自己設計的視窗屬性的資訊填充結構變數wc的域.

要WinMain登記視窗類,首先要填寫一個WNDCLASS結構,其定義如下所示:

   typedef struct _WNDCLASSA

   {

     UINT style ;         //視窗類風格

     WNDPROC lpfnWndProc ;    //指向視窗過程函式的指標

     int cbClsExtra ;       //視窗類附加資料

     int cbWndExtra ;       //視窗附加資料

     HINSTANCE hInstance ;    //擁有視窗類的例項控制代碼

     HICON hIcon ;        //最小視窗圖示

     HCURSOR hCursor ;      //視窗內使用的游標

     HBRUSH hbrBackground ;   //用來著色視窗背景的刷子

     LPCSTR lpszMenuName ;    //指向選單資源名的指標

     LPCSTR lpszClassName ;   // 指向視窗類名的指標

   }

在VC6.0裡面,把游標定位在WNDCLASS上,按F1,即可啟動MSDN,在MSDN裡你可看到這個結構原形.在下節講解這些引數在本程式中的具體用法.

(2)註冊視窗類

(1)第一個引數:成員style控制視窗的某些重要特性,在WINDOWS.H中定義了一些字首為CS的常量,在程式中可組合使用這些常量.也可把sytle設為0.本程式中為wc.style = CS_HREDRAW | CS_VREDRAW,它表示當視窗的縱橫座標發生變化時要重畫整個視窗。你看:無論你怎樣拉動視窗的大小,那行字都會停留在視窗的正中部,而假如把這個引數設為0的話,當改動視窗的大小時,那行字則不一定處於中部了。

(2)第二個引數:lpfnWndProc包括一個指向該視窗類的訊息處理函式的指標,此函式稱為視窗過程函式。它將接收Windows傳送給視窗的訊息,並執行相應的任務。其原型為:

long FAR PASCAL WndProc(HWND ,unsigned,WORD,LONG);並且必須在模快定義中回撥它。WndProc是一個回撥函式(見第五節),如果暫時無法理解這個模糊的概念意味著什麼,可先放過,等到講訊息迴圈時再詳談。

(3)第三,四個引數:cbWndExtra域指定用本視窗類建立的所有視窗結構分配的額外位元組數。當有兩個以上的視窗屬於同一視窗類時,如果想將不同的資料和每個視窗分別相對應。則使用該域很有用。這般來講,你只要把它們設為0就行了,不必過多考慮。

(4)第五個引數:hInstance域標識應用程式的例項hInstance,當然,例項名是可以改變的。wc.hInstance = hInstance ;這一成員可使Windows連線到正確的程式。

(5)第六個引數:成員hIcon被設定成應用程式所使用圖示的控制代碼,圖示是將應用程式最小化時出現在工作列裡的的圖示,用以表示程式仍駐留在記憶體中。Windows提供了一些預設圖示,我們也可定義自己的圖示,VC裡面專有一個製作圖示的工具。

(6)第七個引數: hCursor域定義該視窗產生的游標形狀。LoadCursor可返回固有游標控制代碼或者應用程式定義的游標控制代碼。IDC_ARROW表示箭頭游標.

(7)第八個引數:wc.hbrBackground域決定Windows用於著色視窗背景的刷子顏色,函式GetStockObject返回視窗的顏色,本程式中返回的是白色,你也可以把它改變為紅色等其他顏色.試試看

(8)第九個引數:lpszMenuName用來指定選單名,本程式中沒有定義選單,所以為NULL。

(9)第十個引數:lpszClassName指定了本視窗的類名。

當對WNDCLASS結構域一一賦值後,就可註冊視窗類了,在建立視窗之前,是必須要註冊視窗類的,註冊視窗類用的API函式是RegisterClass,註冊失敗的話,就會出現一個對話方塊如程式所示,函式RegisterClass返回0值,也只能返回0值,因為註冊不成功,程式已經不能再進行下去了。

在本程式中註冊視窗類如下:

if (!RegisterClass (&wc)) {

       MessageBox (NULL, TEXT ("This program requires Windows NT!"),

               szAppName,MB_IConERROR) ;

       return 0 ;

             }

(二)建立視窗

註冊視窗類後,就可以建立視窗了,本程式中建立視窗的有關語句如下:

 hwnd = CreateWindow (szAppName,                     // window class name

                          TEXT ("歡迎你的到來!"),    // window caption

                          WS_OVERLAPPEDWINDOW,        // window style

                          CW_USEDEFAULT,              // initial x position

                          CW_USEDEFAULT,              // initial y position

                          CW_USEDEFAULT,              // initial x size

                          CW_USEDEFAULT,              // initial y size

                          NULL,                       // parent window handle

                          NULL,                       // window menu handle

                          hInstance,                  // program instance handle

                          NULL) ;                     // creation parameters

引數1:登記的視窗類名,這個類名剛才咱們在註冊視窗時已經定義過了。

引數2:用來表明視窗的標題。

引數3: 用來表明視窗的風格,如有無最大化,最小化按紐啊什麼的。

引數4,5: 用來表明程式執行後窗口在螢幕中的座標值。

引數6,7: 用來表明視窗初始化時(即程式初執行時)視窗的大小,即長度與寬度。

引數8: 在建立視窗時可以指定其父視窗,這裡沒有父視窗則引數值為0。

引數9: 用以指明視窗的選單,選單以後會講,這裡暫時為0。

最後一個引數是附加資料,一般都是0。

CreateWindow()的返回值是已經建立的視窗的控制代碼,應用程式使用這個控制代碼來引用該視窗。如果返回值為0,就應該終止該程式,因為可能某個地方出錯了。如果一個程式建立了多個視窗,則每個視窗都有各自不同的控制代碼.

(三)顯示和更新視窗

    API函式CreateWindow建立完視窗後,要想把它顯示出現,還必須呼叫另一個API函式ShowWindows.形式為:

ShowWindow (hwnd, iCmdShow); 
其第一個引數是視窗控制代碼,告訴ShowWindow()顯示哪一個視窗,而第二個引數則告訴它如何顯示這個視窗:最小化(SW_MINIMIZE),普通(SW_SHOWNORMAL),還是最大化(SW_SHOWMAXIMIZED)。WinMain在建立完視窗後就呼叫ShowWindow函式,並把iCmdShow引數傳送給這個視窗。你可把iCmdShow改變為這些引數試試。

WinMain()呼叫完ShowWindow後,還需要呼叫函式UpdateWindow,最終把視窗顯示了出來。呼叫函式UpdateWindow將產生一個WM_PAINT訊息,這個訊息將使視窗重畫,即使視窗得到更新.

(四)建立訊息迴圈

主視窗顯示出來了,WinMain就開始處理訊息了,怎麼做的呢?

Windows為每個正在執行的應用程式都保持一個訊息佇列。當你按下滑鼠或者鍵盤時,Windows並不是把這個輸入事件直接送給應用程式,而是將輸入的事件先翻譯成一個訊息,然後把這個訊息放入到這個應用程式的訊息佇列中去。應用程式又是怎麼來接收這個訊息呢?這就講講訊息迴圈了。

應用程式的WinMain函式通過執行一段程式碼從她的佇列中來檢索Windows送往她的訊息。然後WinMain就把這些訊息分配給相應的視窗函式以便處理它們,這段程式碼是一段迴圈程式碼,故稱為"訊息迴圈"。這段迴圈程式碼是什麼呢?好,往下看:

在咱們的第二隻小板凳中,這段程式碼就是:

......

MSG msg; //定義訊息名

while (GetMessage (&msg, NULL, 0, 0))

     {

          TranslateMessage (&msg) ; //翻譯訊息

          DispatchMessage (&msg) ; //撤去訊息

     }

     return msg.wParam ;

MSG結構在標頭檔案中定義如下:

typedef struct tagMSG

{

HWND hwnd;

UINT message;

WPARAM wParam;

LPARAM lParam;

DWORD time;

POINT pt;

} MSG, *PMSG;

MSG資料成員意義如下:

引數1:hwnd是訊息要傳送到的那個視窗的控制代碼,這個視窗就是咱們用CreateWindows函式建立的那一個。如果是在一個有多個視窗的應用程式中,用這個引數就可決定讓哪個視窗接收訊息。

引數2:message是一個數字,它唯一標識了一種訊息型別。每種訊息型別都在Windows檔案中定義了,這些常量都以WM_開始後面帶一些描述了訊息特性的名稱。比如說當應用程式退出時,Windows就嚮應用程式傳送一條WM_QUIT訊息。

引數3:一個32位的訊息引數,這個值的確切意義取決於訊息本身。

引數4:同上。


引數5:訊息放入訊息佇列中的時間,在這個域中寫入的並不是日期,而是從Windows啟動後所測量的時間值。Windows用這個域來使用訊息保持正確的順序。

引數6:訊息放入訊息佇列時的滑鼠座標.

訊息迴圈以GetMessage呼叫開始,它從訊息佇列中取出一個訊息:

GetMessage(&msg,NULL,0,0),第一個引數是要接收訊息的MSG結構的地址,第二個引數表示視窗控制代碼,NULL則表示要獲取該應用程式建立的所有視窗的訊息;第三,四引數指定訊息範圍。後面三個引數被設定為預設值,這就是說你打算接收發送到屬於這個應用程式的任何一個視窗的所有訊息。在接收到除WM_QUIT之外的任何一個訊息後,GetMessage()都返回TRUE。如果GetMessage收到一個WM_QUIT訊息,則返回FALSE,如收到其他訊息,則返回TRUE。因此,在接收到WM_QUIT之前,帶有GetMessage()的訊息迴圈可以一直迴圈下去。只有當收到的訊息是WM_QUIT時,GetMessage才返回FALSE,結束訊息迴圈,從而終止應用程式。 均為NULL時就表示獲取所有訊息。

訊息用GetMessage讀入後(注意這個訊息可不是WM_QUIT訊息),它首先要經過函式TranslateMessage()進行翻譯,這個函式會轉換成一些鍵盤訊息,它檢索匹配的WM_KEYDOWN和WM_KEYUP訊息,併為視窗產生相應的ASCII字元訊息(WM_CHAR),它包含指定鍵的ANSI字元.但對大多數訊息來說它並不起什麼作用,所以現在沒有必要考慮它。

下一個函式呼叫DispatchMessage()要求Windows將訊息傳送給在MSG結構中為視窗所指定的視窗過程。我們在講到登記視窗類時曾提到過,登記視窗類時,我們曾指定Windows把函式WindosProc作為咱們這個視窗的視窗過程(就是指處理這個訊息的東東)。就是說,Windows會呼叫函式WindowsProc()來處理這個訊息。在WindowProc()處理完訊息後,程式碼又迴圈到開始去接收另一個訊息,這樣就完成了一個訊息迴圈。

下一個出場的東東就是視窗過程了,先歇一會兒再說吧??

(五)終止應用程式:

Windows是一種非剝奪式多工作業系統。只有的應用程式交出CPU控制權後,Windows才能把控制權交給其他應用程式。當GetMessage函式找不到等待應用程式處理的訊息時,自動交出控制權,Windows把CPU的控制權交給其他等待控制權的應用程式。由於每個應用程式都有一個訊息迴圈,這種隱式交出控制權的方式保證合併各個應用程式共享控制權。一旦發往該應用程式的訊息到達應用程式佇列,即開始執行GetMessage語句的下一條語句。

當WinMain函式把控制返回到Windows時,應用程式就終止了。應用程式的啟動訊息迴圈前要檢查引匯出訊息迴圈的每一步,以確保每個視窗已註冊,每個視窗都已建立。如存在一個錯誤,應用程式應返回控制權,並顯示一條訊息。

但是,一旦WinMain函式進入訊息迴圈,終止應用程式的唯一辦法就是使用PostQuitMessage把訊息WM_QUIT傳送到應用程式佇列。當GetMessage函式檢索到WM_QUIT訊息,它就返回NULL,並退出訊息外迴圈。通常,當主視窗正在刪除時(即視窗已接收到一條WM_DESTROY訊息),應用程式主視窗的視窗函式就傳送一條WM_QUIT訊息。

雖然WinMain指定了返回值的資料型別,但Windows並不使用返回值。不過,在除錯一應用程式時,返回值地有用的。通常,可使用與標準C程式相同的返回值約定:0表示成功,非0表示出錯。PostQuitMessage函式允許視窗函式指定返回值,這個值複製到WM_QUIT訊息的wParam引數中。為了的結束訊息迴圈之後返回這個值,我們的第二隻小板凳中使用了以下語句:

return msg.wParam ; //表示從PostQuitMessage返回的值

例如:當Windows自身終止時,它會撤消每個視窗,但不把控制返回給應用程式的訊息迴圈,這意味著訊息迴圈將永遠不會檢索到WM_QUIT訊息,並且的迴圈之後的語句也不能再執行。Windows的終止前的確傳送一訊息給每個應用程式,因而標準C程式通常會的結束前清理現場並釋放資源,但Windows應用程式必須隨每個視窗的撤消而被清除,否則會丟失一些資料。

(六)視窗過程,視窗過程函式

如前所述,函式GetMessage負責從應用程式的訊息佇列中取出訊息,而函式DispatchMessage()要求Windows將訊息傳送給在MSG結構中為視窗所指定的視窗過程。然後出臺的就是這個視窗過程了,這個視窗過程的任務是幹什麼呢?就是最終用來處理訊息的,就是訊息的處理器而已,那麼這個函式就是WindowProc,在Visual C++6.0中按F1啟動MSDN,按下面這個路徑走下來:

PlatForm SDK-->User Interface services-->Windows user Interface-->Windowing-->Window Procedures-->Window Procedure Reference-->Windows Procedure Functions-->WindowProc

啊,太累了,不過我們終於的MSDN中找到了這個函式,前幾次我講解這些API函式的時候,都是的知道的情況下搜尋出來的,所以沒有詳細給出每個函式的具體位置,而這次我卻是一點點去找的,還好,沒被累死,體會到MSDN的龐大了吧,不過我用的是MSDN2000,是D版的,三張光碟裝。你用的MSDN如果按這個路徑走下去的話,可能會找不到,不過我想大致也是在這個位置了,找找看!!!

LRESULT CALLBACK WindowProc

(

HWND hwnd, // handle to window

UINT uMsg, // message identifier

WPARAM wParam, // first message parameter

LPARAM lParam // second message parameter

);

這個函式我們的第二隻小板凳裡被我們稱為WndProc.

下面講解:

不知你注意到了沒有,這個函式的引數與剛剛提到的GetMessage呼叫把返回的MSG結構的前四個成員相同。如果訊息處理成功,WindowProc的返回值為0.

Windows的啟動應用程式時,先呼叫WinMain函式,然後呼叫視窗過程,注意:在我們的這個程式中,只有一個視窗過程,實際上,也許有不止一個的視窗過程。例如,每一個不同的視窗類都 有一個與之相對應的視窗過程。無論Windows何時想傳遞一個訊息到一視窗,都將呼叫相應的視窗過程。當Windows從環境,或從另一個應用程式,或從使用者的應用程式中得到訊息時,它將呼叫視窗過程並將資訊傳給此函式。總之,視窗過程函式處理所有傳送到由此視窗類建立的視窗所得到的訊息。並且視窗過程有義務處理Windows扔給它的任何訊息。我們在學習Windows程式設計的時候,最主要的就是學習這些訊息是什麼以及是什麼意思,它們是怎麼工作的。

令我們不解的是,在程式中我們看不出來是哪一個函式在呼叫視窗過程。它其實是一個回撥函式.前面已經提到,Windows把發生的輸入事件轉換成輸入訊息放到訊息佇列中,而訊息迴圈將它們傳送到相應的視窗過程函式,真正的處理是在視窗過程函式中執行的,在Windows中就使用了回撥函式來進行這種通訊。

回撥函式是輸出函式中特殊的一種,它是指那些在Windows環境下直接呼叫的函式。一個應用程式至少有一個回撥函式,因為在應用程式處理訊息時,Windows呼叫回撥函式。這種回撥函式就是我們前面提到的視窗過程,它對對應於一個活動的視窗,回撥函式必須向Windows註冊,Windows實施相應操作即行回撥。

每個視窗必須有一個視窗過程與之對應,且Windows直接呼叫本函式,因此,視窗函式必須採用FAR PASCAL呼叫約定。在我們的第二隻小板凳中,我們的視窗函式為WndProc,必須注意這裡的函式名必須是前面註冊的視窗類時,向域wc.lpfnWndProc所賦的WndProc。函式WndProc就是前面定義的視窗類所生成的所有視窗的視窗函式。

在我們的這個視窗函式中,WndProc處理了共有兩條訊息:WM_PAINT和WM_DESTROY.

視窗函式從Windows中接收訊息,這些訊息或者是由WinMain函式傳送的輸入訊息,或者是直接來自Windows的視窗管理訊息。視窗過程檢查一條訊息,然後根據這些訊息執行特定的動作未被處理的訊息通過DefWindowProc函式傳回給Windows作缺海上處理。

可以傳送視窗函式的訊息約有220種,所有視窗訊息都以WM_開頭,這些訊息在標頭檔案中被定義為常量。引起Windows呼叫視窗函式的原因有很多,,如改變視窗大小啊,改變視窗在螢幕上的位置啊什麼的。

Windows已經把任務扔給視窗過程了,視窗過程是怎麼處理訊息的呢?稍息一下,讓我們進行下一節:處理訊息......

注:可能你看這些東西的時候有些亂,不過沒關係,這很正常,多看幾下MSDN就慢慢明白了,有我寫這個專題的時候,很多概念也太不清楚,不過等我查資料寫下來後,感覺漸漸有些東西也有了點眉目,因為這本身也是個進步的過程。 ---小朱 
(七)處理訊息 
視窗過程處理訊息通常以switch語句開始,對於它要處理的每一條訊息ID都跟有一條case語句。大多數windows proc都有具有下面形式的內部結構:

switch(uMsgId)

{

case WM_(something):

//這裡此訊息的處理過程

return 0;

case WM_(something else):

//這裡是此訊息的處理過程

ruturn 0;

default:

//其他訊息由這個預設處理函式來處理

return DefWindowProc(hwnd,uMsgId,wParam,lParam);

}

在處理完訊息後,要返回0,這很重要-----它會告訴Windows不必再重試了。對於那些在程式中不準備處理的訊息,視窗過程會把它們都扔給DefWindowProc進行預設處理,而且還要返回那個函式的返回值。在訊息傳遞層次中,可以認為DefWindowProc函式是最頂層的函式。這個函式發出WM_SYSCOMMAND訊息,由系統執行Windows環境中多數視窗所公用的各種通用操作,例如,畫視窗的非使用者區,更新視窗的正文標題等等等等。

再提示一下,以WM_的訊息在Windows標頭檔案中都被定義成了常量,如WM_QUIT=XXXXXXXXXXX,但我們沒有必要記住這個數值,也不可能記得住,我們只要知道WM_QUIT就OK了。

在第二隻小板凳中我們只讓視窗過程處理了兩個訊息:一個是WM_PAINT,另一個是WM_DESTROY,先說說第一個訊息---WM_PAINT.

關於WM_PAINT:

無論何時Windows要求重畫當前視窗時,都會發該訊息。也可以這樣說:無論何時視窗非法,都必須進行重畫。 哎呀,什麼又是"非法視窗"?什麼又是重畫啊?你這人有沒有完,嗯?

稍安勿燥,我比你還煩呢?我午飯到現在還沒吃呢!你有點耐心,來點專業精神好不好???我開始在MSDN裡面找有關這個方面的內容了,別急,我找找看:

Platform SDK-->Graphics and Multimedia Services-->Windows GDI-->Painting and Drawing-->Using the WM_PAINT Message-----終於找到了。

下面是一大套理論:

讓我們把Windows的螢幕想像成一個桌面,把一個視窗想像成一張紙。當我們把一張紙放到桌面上時,它會蓋住其他的紙,這樣被蓋住的其他紙上的內容都看不到了。但我們只要把這張紙移開,被蓋住的其他紙上的內容就會顯示出來了---這是一個很簡單的道理,誰都明白。

對於我們的螢幕來說,當一個視窗被另一視窗蓋住時,被蓋住的視窗的某些部分就看不到了,我們要想看到被蓋住的視窗的全部面貌,就要把另一個視窗移開,但是當我們移開後,事情卻起了變化-----很可能這個被蓋住的視窗上的資訊被擦除了或是丟失了。當視窗中的資料丟失或過期時,視窗就變成非法的了---或者稱為"無效"。於是我們的任務就來了,我們必須考慮怎樣在視窗的資訊丟失時"重畫視窗"--使視窗恢復成以前的那個樣子。這也就是我們在這第二隻小板凳中呼叫UpdateWindow的原因。

你忘記了嗎?剛才我們在(三)顯示和更新視窗中有下面的一些文字:

WinMain()呼叫完ShowWindow後,還需要呼叫函式UpdateWindow,最終把視窗顯示了出來。呼叫函式UpdateWindow將產生一個WM_PAINT訊息,這個訊息將使視窗重畫,即使視窗得到更新.---這是程式第一次呼叫了這條訊息。

為重新顯示非法區域,Windows就傳送WM_PAINT訊息實現。要求Windows傳送WM_PAINT的情況有改變視窗大小,對話方塊關閉,使用了UpdateWindows和ScrollWindow函式等。這裡注意,Windows並非是訊息WM_PAINT的唯一來源,使用InvalidateRect或InvalidateRgn函式也可以產生繪圖視窗的WM_PAINT訊息......

通常情況下用BeginPaint()來響應WM_PAINT訊息。如果要在沒有WM_PAINT的情況下重畫視窗,必須使用GetDC函式得到顯示緩衝區的控制代碼。這裡面不再擴充套件。詳細見MDSN。

這個BeginPaint函式會執行準備繪畫所需的所有步驟,包括返回你用於輸入的控制代碼。結束則是以EndPaint();

在呼叫完BeginPaint之後,WndProc接著呼叫GetClientRect:

GetClientRect(hwnd,&rect);

第一個引數是程式視窗的控制代碼。第二個引數是一個指標,指向一個RECT型別的結構。查MSDN,可看到這個結構有四個成員。

WndProc做了一件事,他把這個RECT結構的指標傳送給了DrawText的第四個引數。函式DrawText的目的就是在視窗上顯示一行字----"你好,歡迎你來到VC之路!",有關這個函式的具體用法這裡也沒必要說了吧。

關於WM_DESTROY

這個訊息要比WM_PAINT訊息容易處理得多:只要使用者關閉視窗,就會發送WM_DESTROY訊息(在視窗從螢幕上移去後)。

程式通過呼叫PostQuitMessage以標準方式響應WM_DESTROY訊息:

PostQuitMessage (0) ;

這個函式在程式的訊息佇列中插入一個WM_QUIT訊息。在(四)建立訊息迴圈中我們曾有這麼一段話:

訊息迴圈以GetMessage呼叫開始,它從訊息佇列中取出一個訊息:

在接收到除WM_QUIT之外的任何一個訊息後,GetMessage()都返回TRUE。如果GetMessage收到一個WM_QUIT訊息,則返回FALSE,如收到其他訊息,則返回TRUE。因此,在接收到WM_QUIT之前,帶有GetMessage()的訊息迴圈可以一直迴圈下去。只有當收到的訊息是WM_QUIT時,GetMessage才返回FALSE,結束訊息迴圈,從而終止應用程式。