程式中建立了主視窗,是以它必須為主視窗注冊一個視窗類,建立視窗并且提供一個消息循環來為視窗處理消息。
注冊視窗類
ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass)
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_HELLOCEME));
wc.hCursor = 0;
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = szAppName;
return RegisterClass(&wc);
}
style域為視窗設定了類的風格。在Windows CE中,類風格被限制為:
CS_GLOBALCLASS 表示類是全局的。這個标志隻是出于相容性才提供的,因為Windows CE中所有視窗類都是程序級全局類。
CS_HREDRAW 告訴系統如果視窗改變了水準大小,就強制重畫視窗。
CS_VREDRAW 告訴系統如果視窗改變了垂直大小,就強制重畫視窗。
CS_NOCLOSE 如果[關閉]按鈕出現在标題欄上,則使其失效。
CS_PARENTDC 讓視窗使用父視窗的裝置環境變量
CS_DBLCLKS 允許[輕按兩下]通知(Windows CE下敲擊兩次為輕按兩下)傳遞給父視窗
lpfnWndProc配置設定的是視窗的視窗過程的位址。因為該域定義為指向視窗過程的指針,是以在源代碼中,必須在域被設定之前,定義該過程的聲明。否則,編譯器類型檢查時會警告該行。
cbClsExra允許程式員為類結構增加額外的空間來存儲隻有應用程式才知道的類特定資料。cbWndExtra更加便于使用,這個域為Windows内部結構增加空間,該結構負責維護視窗每個執行個體的狀态。不在視窗結構本身裡存儲大量的資料,應用程式應該存儲一個指向應用程式特定結構的指針,該結構包含視窗每個執行個體的資料。在Windows CE裡,cbClsExtra 和cbWndExtra域必須時4位元組的倍數。
hInstance域設定為程式的執行個體句柄,該句柄指明擁有視窗的程序。hIcon域設定為視窗預設圖示的句柄,但在Windows CE中并不支援該域,是以該域應該設定為NULL。(在Windows CE中,會在類的第一個視窗被建立後設定類的圖示。對于Hello3,沒有圖示提供,并且與其它Windows版本不同,Windows CE中沒有任何預定義圖示用于裝載。)
除非應用程式是為帶滑鼠的Windows CE系統設計的,否則hCursor域應該設定為NULL。幸運的是,如果系統不支援光标,調用LoadCursor (IDC_ARROW) 函數會傳回NULL。
hbrBackground域規定Windows CE如何畫視窗背景。Windows用這個域中指定的刷子brush(一個小的預定義的像素數組)來畫視窗背景。Windows CE提供許多預定義的刷子,你可以用GetStockObject函數來裝載。如果hbrBackground域是NULL,視窗必須處理WM_ERASEBKGND消息,重畫視窗背景。
lpszMenuName域必須設定為NULL,因為Windows CE不直接支援有菜單的視窗。在Windows CE中,菜單由主視窗建立的指令工具條、指令帶或菜單條控件提供。
lpszClassName設定為程式員定義的字元串,用于為Windows指明類的名字。Hello3用的是“MyClass”做為類名。
整個WNDCLASS類被填充後,RegisterClass函數被調用,并用指向WNDCLASS結構的指針作為唯一的參數。如果函數成功,一個标記視窗類的值被返,如果失敗,函數傳回0。
消息循環
主視窗建立後,WinMain進入消息循環,這是每個Windows 應用程式的心髒。
while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
該循環很簡單:調用GetMessage函數,從應用程式消息隊列中擷取下一個消息。如果沒有消息可用,則調用進入等待期,阻塞應用程式線程直到消息可用。當消息可用,該函數傳回包含在MSG結構的消息資料。MSG結構自身包含幾個域,有的用于識别消息,有的提供特定消息參數,有的識别在消息被發送之前,被筆觸摸過的最後螢幕位置點。該位置資訊不同于标準Win32消息位置資料,在XP下,傳回的位置是目前滑鼠位置而不是最後點選(或者tapped,在Windows CE裡)的位置。
TranslateMessage 把适當的鍵盤資訊轉換成字元資訊。(後面會讨論其它資訊過濾器,比如IsDialogMsg。)DispatchMessage 接下來告訴Windows把消息發給應用程式适當的視窗。
擷取消息、轉換消息、分發消息這個過程會一直循環到GetMessage 收到WM_QUIT消息,這會使GetMessage傳回0,這一點不同于其它消息。從while子句可以看出,GetMessage傳回0将導緻循環終止。
消息循環終止後,程式除了清理和退出外幾乎不做什麼。在Hello3裡,程式簡單的從WinMain中傳回。WinMain的傳回值成為程式的傳回碼。傳統上,最後一個消息WM_QUIT的wParam參數值包含有傳回值。為了響應應用程式對PostQuitMessage的調用,WM_QUIT消息被發送出去,此時WM_QUIT的wParam參數值被填充。
視窗過程
發送或送出(send 或 post方式)主視窗的消息被送到MainWndProc過程中。和所有視窗過程一樣,MainWndProc原型如下:
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam);
傳回值類型LRESULT 實際上就是long型(在Windows裡long是一個32位值),寫成這種形式是為源代碼和機器之間提供一個中間級。雖然你可以輕易的從包含檔案中确定Windows程式設計時使用的變量的真實類型,但當你試圖把代碼做跨平台的轉換時會産生問題。雖然了解變量類型的大小對計算記憶體使用是有用的,但沒有什麼好的理由去使用(實際上有很多不使用的理由)windows.h檔案中提供的類型定義。
CALLBACK 類型指明該函數是EXE的外部入口點,這是Windows直接調用該過程所必須的。在桌面系統裡,CALLBACK 指出參數是按類Pascal風格從右到左方式壓程序式棧的,這和标準C語言方式相反。為外部入口點使用Pascal語言棧架構的原因可以追朔到Windows開發非常早的時期。使用固定大小、Pascal棧方式,意味着由被調用的過程來清理棧,而不是留給調用者來清理。這種方式可以有效的減少Windows及其附屬程式的大小,是以早期的微軟開發者認為這是一個好的方式。在Windows CE裡,應用程式對所有函數都使用C棧架構,不管是否是外部調用。
傳給視窗過程的第一個參數是視窗句柄,當您需要定義具體的視窗執行個體的時候,這個句柄是很有用的。wMsg參數表示發給視窗的消息。這不是WinMain消息循環裡使用的MSG結構,而是一個包含消息值的unsigned整型。剩餘兩個參數,wParam 和lParam, 傳遞和具體消息有關的資料給視窗過程。它們的名字來自Win16時代,那時wParam是個16位值而lParam是32位值。同其它Win32作業系統一樣,在Windows CE裡,兩個都是32位的。
和傳統的視窗過程一樣,Hello3的視窗過程通過一個switch語句解析wMsg消息ID。該switch語句包含2個case語句,一個用來解析WM_PAINT消息,另一個用來解析WM_DESTROY消息。這個視窗過程大概是視窗過程所能簡化到及至的一個視窗過程了。
WM_PAINT
繪制視窗,處理WM_PAINT消息,這在任何Windows 程式中都是很重要的功能之一。視窗的外觀是在程式處理WM_PAINT消息的過程中完成的。除了用您在注冊視窗類時指定的刷子繪制預設背景外,Windows對處理該消息不提供任何幫助。Hello3中處理WM_PAINT消息如下:
case WM_PAINT:
// Get the size of the client rectangle
GetClientRect (hWnd, &rect);
hdc = BeginPaint (hWnd, &ps);
DrawText (hdc, TEXT ("Hello Windows CE!"), -1, &rect,
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
EndPaint (hWnd, &ps);
return 0;
在視窗繪制之前,程式必須确定視窗大小。在Windows程式裡,一個标準視窗被劃分為兩個區域--非客戶區和客戶區。視窗标題欄和可變大小的邊框通常占據了視窗的非客戶區,這個區域由Windows負責繪制。客戶區屬于視窗的内部區域,由應用程式負責繪制。應用程式通過調用GetClientRect 函數來确定客戶區的大小和位置。該函數傳回一個RECT結構,包含左上角、右下角坐标等描述客戶區矩形邊界的資訊。分成客戶區和非客戶區的好處是,應用程式不必繪制那些視窗标準元素,例如标題欄。
其它版本的Windows提供一系列WM_NCxxx消息,允許您的應用程式繪制非客戶區。在Windows CE裡,視窗很少有标題欄。因為很少有非客戶區,是以Windows CE不發送非用戶端消息給視窗過程。
WM_PAINT消息裡執行的所有繪制工作都必須由兩個函數BeginPaint 和EndPaint包圍。BeginPaint 函數傳回裝置環境句柄HDC。裝置環境是實體顯示裝置(例如視訊顯示器或列印機)的邏輯代表。Windows程式從不直接修改顯示硬體。相反,Windows用裝置環境将程式與具體硬體隔離開。
BeginPaint 填充一個PAINTSTRUCT結構,其結構如下:
typedef struct tagPAINTSTRUCT {
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT;
hdc就是BeginPaint函數傳回的句柄。fErase指出視窗過程是否需要重畫視窗背景。rcPaint是RECT結構,定義了需要重畫的客戶區。并假設在每個WM_PAINT消息中,整個客戶區視窗都需要重畫。當性能是需要考慮的問題時,該域是很有用的,因為有時僅僅需要重畫部分視窗即可。即使當程式嘗試重畫rcPaint矩形以外的區域時,Windows也會阻止這麼做的。該結構的其它域,fRestore, fIncUpdate, 和rgbReserved,屬于Windows内部使用,應用程式可以忽略掉它們。
程式中唯一的繪制工作是在視窗繪制一行文本。程式調用DrawText函數來完成該繪制。如果您看一下該函數,很容易會明白這個調用在視窗上繪制了一行字元串“Hello Windows CE”。在DrawText傳回後,調用EndPaint來通知Windows程式已經完成了視窗更新。
EndPaint調用同時也使沒有被繪制的視窗其它區域有效。Windows保持一份無效視窗區域(也就是需要重畫的區域)清單和有效區域(也就是已經更新的區域)清單。不論您是否在視窗畫了什麼,通過成對的調用BeginPaint和EndPaint,會通知Windows由您來處理視窗的無效區域。實際上,您必須調用BeginPaint和EndPaint,或者通過其它方式使視窗無效區域變有效,否則Windows會不斷發送WM_PAINT消息給視窗,直到無效區域變有效。
WM_DESTROY
程式中處理的另一個消息是WM_DESTROY。當視窗即将被銷毀時,該消息被送出。因為該視窗是應用程式主視窗,當視窗被銷毀時應用程式将終止。處理WM_DESTROY消息的代碼調用PostQuitMessage消息來觸發該動作。PostQuitMessage函數将WM_QUIT消息放到到消息隊列裡。該函數的參數是傳回碼的值,該值放在WM_QUIT消息的wParam參數裡傳回應用程式。
如前所述,消息循環看到WM_QUIT消息就會退出循環。WinMain 接着調用TermInstance,在Hello3裡,該函數什麼也不做,隻是傳回。WinMain 接着傳回,并終止程式。
本文轉自xyz_lmn51CTO部落格,原文連結:http://blog.51cto.com/xyzlmn/818943,如需轉載請自行聯系原作者