[翻譯]-WinCE 程式設計 (3rd 版)–1.6 Hello3

來源:互聯網
上載者:User

Hello3
回顧的夠多了,是時候做一個完整的Windows 應用程式--Hello3了。雖然Hello3的整個程式檔案以及書中全部例子都可以在附書光碟片裡找到,但我還是建議,對於初期的例子,您應當避免簡單的從CD上裝載工程檔案,而是應該手工輸入整個例子。通過這種略微有些枯燥的工作,你會體會到標準Win32程式與Windows CE程式之間在開發過程的不同以及在程式上的細微差別。清單1-3給出了Hello3的全部原始碼。

清單1-3:程式Hello3
Hello3.cpp
//======================================================================
// Hello3 - A simple application for Windows CE
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h>                 // For all that Windows stuff
  
LRESULT CALLBACK MainWndProc (HWND, UINT, WPARAM, LPARAM);
  
//======================================================================
// Program entry point
//
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPWSTR lpCmdLine, int nCmdShow) {
    WNDCLASS wc;
    HWND hWnd;
    MSG msg;
  
    // Register application main window class.
    wc.style = 0;                             // Window style
    wc.lpfnWndProc = MainWndProc;             // Callback function
    wc.cbClsExtra = 0;                        // Extra class data
    wc.cbWndExtra = 0;                        // Extra window data
    wc.hInstance = hInstance;                 // Owner handle
    wc.hIcon = NULL,                          // Application icon
    wc.hCursor = LoadCursor (NULL, IDC_ARROW);// Default cursor
    wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
    wc.lpszMenuName =  NULL;                  // Menu name
    wc.lpszClassName = TEXT("MyClass");       // Window class name
  
    if (RegisterClass (&wc) == 0) return -1;
  
    // Create main window.
    hWnd = CreateWindowEx(WS_EX_NODRAG,       // Ex style flags
                          TEXT("MyClass"),    // Window class
                          TEXT("Hello"),      // Window title
                          // Style flags
                          WS_VISIBLE | WS_CAPTION | WS_SYSMENU,
                          CW_USEDEFAULT,      // x position
                          CW_USEDEFAULT,      // y position
                          CW_USEDEFAULT,      // Initial width
                          CW_USEDEFAULT,      // Initial height
                          NULL,               // Parent
                          NULL,               // Menu, must be null
                          hInstance,          // Application instance
                          NULL);              // Pointer to create
                                              // parameters
    if (!IsWindow (hWnd)) return -2;  // Fail code if not created.
  
    // Standard show and update calls
    ShowWindow (hWnd, nCmdShow);
    UpdateWindow (hWnd);
  
    // Application message loop
    while (GetMessage (&msg, NULL, 0, 0)) {
        TranslateMessage (&msg);
        DispatchMessage (&msg);
    }
    // Instance cleanup
    return msg.wParam;
}
//======================================================================
// MainWndProc - Callback function for application window
//
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
                              LPARAM lParam) {
    PAINTSTRUCT ps;
    RECT rect;
    HDC hdc;
  
    switch (wMsg) {
    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;
  
    case WM_DESTROY:
        PostQuitMessage (0);
        break;
    }
    return DefWindowProc (hWnd, wMsg, wParam, lParam);
}

Hello3展示了Windows程式的各個方面,從註冊視窗類別到建立視窗及視窗過程。和頭兩個例子一樣,Hello3有著相同的進入點--WinMain。但是因為Hello3建立了自己的視窗,所以它必須為主視窗註冊一個視窗類別,建立視窗並且提供一個訊息迴圈來為視窗處理訊息。

註冊視窗類別
在WinMain中,Hello3為主視窗註冊了視窗類別。註冊一個視窗類別只是簡單的填充一個描述視窗類別的有些大的結構並調用RegisterClass函數。RegisterClass和WNDCLASS結構定義如下:

ATOM RegisterClass (const WNDCLASS *lpWndClass);
  
typedef struct _WNDCLASS {
    UINT style;
    WNDPROC lpfnWndProc;
    int cbClsExtra;
    int cbWndExtra;
    HANDLE hInstance;
    HICON hIcon;
    HCURSOR hCursor;
    HBRUSH hbrBackground;
    LPCTSTR lpszMenuName;
    LPCTSTR lpszClassName;
} WNDCLASS;

給WNDCLASS結構各個域賦的值為Hello3主視窗的所有執行個體定義了行為表現。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。

建立視窗
一旦視窗類別註冊成功,就可以建立主視窗了。所有Windows程式員在他們的我Windows編程生涯裡都會學習到CreateWindow和CreateWindowEx函數調用。CreateWindowEx 的原型如下:
HWND CreateWindowEx (DWORD dwExStyle, LPCTSTR lpClassName,
                     LPCTSTR lpWindowName, DWORD dwStyle,
                     int x, int y, int nWidth, int nHeight,
                     HWND hWndParent, HMENU hMenu,
                     HINSTANCE hInstance, LPVOID lpParam);

雖然參數數量讓人畏懼,但是一旦你瞭解了這些參數,會發現它們很有邏輯性。第1個參數是擴充風格標誌位。Windows CE能夠支援的擴充風格標誌如下:
WS_EX_TOPMOST  視窗置頂
WS_EX_WINDOWEDGE  視窗有凸起的邊框
WS_EX_CLIENTEDGE  視窗有凹陷的邊框
WS_EX_STATICEDGE  靜態視窗具有3D外觀
WS_EX_OVERLAPPEDWINDOW  是WS_EX_WINDOWEDGE 和WS_EX_CLIENTEDGE兩個風格的組合
WS_EX_CAPTIONOKBUTTON  在標題列上有OK按鈕
WS_EX_CONTEXTHELP      在標題列上有協助按鈕
WS_EX_NOACTIVATE       點擊視窗時,視窗不成為使用中視窗
WS_EX_NOANIMATION      當視窗建立的時候,頂層視窗沒有彈出矩形,在任務條上也沒有按鈕。
WS_EX_NODRAG        防止視窗被移動

參數dwExStyle是CreateWindowEx和CreateWindow之間唯一有差別的地方。實際上,如果你在Windows CE標頭檔裡看CreateWindow的聲明,會發現CreateWindow只是簡單的將dwExStyle設定為0並調用CreateWindowEx而已。

第2個參數是執行個體化視窗所使用的視窗類別的名字。在Hello3裡,類名是MyClass,也就是用RegisterClass註冊的類的名字。

第3個參數用做視窗文本。在Windows其它版本裡,它是用做標準視窗標題列裡的文字。在H/PC裡,主視窗很少有標題列,這個文字只用在任務條按鈕上。在Pocket PC裡,這個文字出現在顯示屏頂部導航條裡。文字使用TEXT宏,確保字串在Windows CE下可以轉換成Unicode。

風格標誌規定了視窗的初始風格。風格標誌即用於系統裡所有相關視窗的普通風格,也用於特定類的風格,比如按鈕類或者列表框類的風格。在這種情況下,我們需要指定的就是用WS_VISIBLE標誌來指明視窗在建立時可視。經驗豐富的Win32程式員應該查閱關於CreateWindow的文檔,因為許多視窗風格標誌在Windows CE下並不支援。

接下來的四個參數指定了視窗初始位置和大小。因為Wimdows CE下大部分應用程式都是全屏視窗,所以每一個大小和位置域都使用CW_USEDEFAULT標誌作為預設值。在Windows CE目前的版本裡,預設值建立的視窗,其大小覆蓋整個螢幕。注意不要為Windows CE裝置假設任何特殊的尺寸,因為不同的實現有不同的螢幕尺寸。

下一個域填寫為父視窗的控制代碼。因為是頂層視窗,所以父視窗網域設定為NULL。菜單域也設定為NULL,因為Windows CE不支援頂層視窗菜單。

hInstance參數就是傳遞給程式的執行個體控制代碼。視窗建立中執行個體控制代碼總會用的上,執行個體控制代碼是在程式開始時就被儲存的。最後的參數是一個指標,在WM_CREATE訊息期間,用於在CreateWindow調用時傳遞資料給視窗過程。本例中,沒有額外資料需要傳遞,所以該參數設定為NULL。

如果建立視窗成功,CreateWindow返回剛建立的視窗的控制代碼,如果函數調用期間有錯誤發生,則返回0。在通過錯誤檢查語句--if語句之後,該視窗控制代碼用在接下來的兩個語句裡(ShowWindow 和UpdateWindow裡)。ShowWindow函數修改視窗狀態,使其同傳給WinMain的參數nCmdShow中給的狀態一致。UpdateWindow函數則強制Windows發送WM_PAINT訊息給剛建立的視窗。

訊息迴圈
主視窗建立後,WinMain進入訊息迴圈,這是每個Windows 應用程式的心臟。Hello3的訊息迴圈定義如下:
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方式)到Hello3主視窗的訊息被送到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結構,定義了需要重畫的客戶區。Hello3忽略該域,並假設在每個WM_PAINT訊息中,整個客戶區視窗都需要重畫。當效能是需要考慮的問題時,該域是很有用的,因為有時僅僅需要重畫部分視窗即可。即使當程式嘗試重畫rcPaint矩形以外的地區時,Windows也會阻止這麼做的。該結構的其它域,fRestore, fIncUpdate, 和rgbReserved,屬於Windows內部使用,應用程式可以忽略掉它們。

Hello3中唯一的繪製工作是在視窗繪製一行文本。Hello3調用DrawText函數來完成該繪製。我將在第2章描述DrawText的細節,如果您看一下該函數,很容易會明白這個調用在視窗上繪製了一行字串“Hello Windows CE”。在DrawText返回後,調用EndPaint來通知Windows程式已經完成了視窗更新。

EndPaint調用同時也使沒有被繪製的視窗其它地區有效。Windows保持一份無效視窗地區(也就是需要重畫的地區)列表和有效地區(也就是已經更新的地區)列表。不論您是否在視窗畫了什麼,通過成對的調用BeginPaint和EndPaint,會通知Windows由您來處理視窗的無效地區。實際上,您必須調用BeginPaint和EndPaint,或者通過其它方式使視窗無效地區變有效,否則Windows會不斷髮送WM_PAINT訊息給視窗,直到無效地區變有效。

WM_DESTROY
Hello3中處理的另一個訊息是WM_DESTROY。當視窗即將被銷毀時,該訊息被送出。因為該視窗是應用程式主視窗,當視窗被銷毀時應用程式將終止。處理WM_DESTROY訊息的代碼調用PostQuitMessage訊息來觸發該動作。PostQuitMessage函數將WM_QUIT訊息放到到訊息佇列裡。該函數的參數是返回碼的值,該值放在WM_QUIT訊息的wParam參數裡傳回應用程式。

如前所述,訊息迴圈看到WM_QUIT訊息就會退出迴圈。WinMain 接著調用TermInstance,在Hello3裡,該函數什麼也不做,只是返回。WinMain 接著返回,並終止程式。

Hello3是典型的Windows程式。這種編程風格有時也成為Petzold式Windows編程,這是為了向Charles Petzold,這位Windows編程大師表示敬意。Charles所著的《Windows 程式設計》(Programming Microsoft Windows )當前是第5版,並且依然是學習Windows編程的最好的書。

我更喜歡為我的Windows程式選擇略微不同的設計。從某種意義上講,這是一個將Windows程式功能組件化的方法,這使的更加容易將程式的一部分複製到另一個程式。在本章最終的例子裡,我介紹這種編程風格,並一起介紹一些Windows CE應用程式所必須的額外特性。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.