Windows SDK(二)

來源:互聯網
上載者:User

註:以下摘自侯捷老師《深入淺出MFC》部分內容,有刪節。原文基於VC5.0,部分之處陳舊但不影響整體。

Windows程式簡述

Windows 程式分為「程式碼」和「UI(User Interface)資源」兩大部份,兩部份最後以連接器整合為一個完整的EXE 檔案。所謂UI 資

源是指功能菜單、對話方塊外貌、程式表徵圖、游標形狀等等東西。這些UI 資源的實際內容(二進位代碼)系藉助各種工具產生,並以各種副檔名

存在,如.ico、.bmp、.cur 等等。程式員必須在一個所謂的資源描述檔(.rc)中描述它們。RC 編譯器(RC.EXE)讀取RC 檔的描述後將所

有UI資源檔集中製作出一個.RES 檔,再與程式碼結合在一起,這才是一個完整的Windows可執行檔。

(.LIB)

眾所周知Windows 支援動態連接。換句話說,應用程式所調用的Windows API 函數是在「執行時期」才連接上的。那麼,「連接時期」所

需的函數庫做什麼用?有哪些?並不是延伸檔名為.dll 者才是動態連接函數庫(DLL,Dynamic Link Library),事實

上.exe、.dll、.fon、.mod、.drv、.ocx 都是所謂的動態連接函數庫。Windows 程式調用的函數可分為C Runtimes 以及Windows API

兩大部分。

以下是它們的命名規則與使用時機:

■ LIBC.LIB - 這是C Runtime 函數庫的靜態連接版本。

■ MSVCRT.LIB - 這是C Runtime 函數庫動態連接版本(MSVCRT40.DLL)的

import 函數庫。如果連接此一函數庫,你的程式執行時必須有MSVCRT40.DLL在場。

另一組函數,Windows API,由作業系統本身(主要是Windows 三大模組GDI32.DLL 和USER32.DLL 和KERNEL32.DLL)提供。雖說動

態連接是在執行時期才發生「連接」事實,但在連接時期,連接器仍需先為調用者(應用程式本身)準備一些適當的資訊,才能夠在執行時期

順利「跳」到DLL 執行。如果該API 所屬之函數庫尚未載入,系統也才因此知道要先行載入該函數庫。這些適當的資訊放在所謂的「import

函數庫」中。32 位Windows 的三大模組所對應的import 函數庫分別為GDI32.LIB 和USER32.LIB和KERNEL32.LIB。

Windows 發展至今,逐漸加上的一些新的API 函數(例如Common Dialog、ToolHelp)並不放在GDI 和USER 和KERNEL 三大模組中,

而是放在諸如COMMDLG.DLL、TOOLHELP.DLL 之中。如果要使用這些APIs,連接時還得加上這些DLLs 所對應的import 函數庫,諸如

COMDLG32.LIB 和TH32.LIB。

可參考:

1.MSVC:關於編譯、連結、裝載、庫相關的一些概念

2.關於形如--error LNK2005: xxx 已經在 msvcrtd.lib ( MSVCR90D.dll ) 中定義--的問題分析解決

(.H)

所有Windows 程式都必須包含WINDOWS.H。除非你十分清楚什麼API 動作需要什麼標頭檔,否則為求便利,單單一個WINDOWS.H

也就是了。不過,WINDOWS.H 只照顧三大模組所提供的API 函數,如果你用到其它system DLLs,例如COMMDLG.DLL 或MAPI.DLL 或

TAPI.DLL 等等,就得包含對應的標頭檔,例如COMMDLG.H 或MAPI.H 或TAPI.H 等等。

事件為驅動,訊息為基礎

Windows 程式的進行系依靠外部發生的事件來驅動。換句話說,程式不斷等待(利用一個while 迴路),等待任何可能的輸入,然後做判

斷,然後再做適當的處理。上述的「輸入」是由作業系統捕捉到之後,以訊息形式(一種資料結構)進入程式之中。作業系統如何捕捉外圍設

備(如鍵盤和滑鼠)所發生的事件呢?噢,USER 模組掌管各個外圍的驅動程式,它們各有偵測迴路。如果把應用程式獲得的各種「輸入」分

類,可以分為由硬體裝置所產生的訊息(如滑鼠移動或鍵盤被按下),放在系統隊列(system queue)中,以及由Windows 系統或其它

Windows 程式傳送過來的訊息,放在程式隊列(application queue)中。以應用程式的眼光來看,訊息就是訊息,來自哪裡或放在哪裡其

實並沒有太大區別,反正程式調用GetMessage API 就取得一個訊息,程式的生命靠它來推動。所有的GUI 系統,包括UNIX的X Window

以及OS/2 的Presentation Manager,都像這樣,是以訊息為基礎的事件驅動系統。

圖 :Windows程式本體與作業系統的關係

可想而知,每一個Windows 程式都應該有一個迴路如下:

MSG msg;

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

TranslateMessage(&msg);

DispatchMessage(&msg);

}

// 以上出現的函數都是Windows API 函數

訊息,也就是上面出現的MSG 結構,其實是Windows 內定的一種資料格式:

/* Queued message structure */

typedef struct tagMSG

{

HWND hwnd;

UINT message; // WM_xxx,例如WM_MOUSEMOVE,WM_SIZE...

WPARAM wParam;

LPARAM lParam;

DWORD time;

POINT pt;

} MSG;

視窗過程函數

接受並處理訊息的主角就是視窗。每一個視窗都應該有一個函數負責處理訊息,程式員必須負責設計這個所謂的「視窗函數」(window

procedure,或稱為window function)。如果視窗獲得一個訊息,這個視窗函數必須判斷訊息的類別,決定處理的方式。

以上就是Windows 程式設計最重要的觀念。至於視窗的產生與顯示,十分簡單,有專門的API 函數負責。稍後我們就會看到Windows 程式

如何把這訊息的取得、指派、處理動作表現出來。

程式進入點WinMain

main 是一般C 程式的進入點:

int main(int argc, char *argv[ ], char *envp[ ]);

{

...

}

WinMain 則是Windows 程式的進入點:

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPSTR lpCmdLine, int nCmdShow)

{

...

}

在Win32 中CALLBACK 被定義為__stdcall,是一種函數調用習慣,關係到參數擠壓到堆棧的次序,以及處理堆棧的責任歸屬。其它的函數調

用習慣還有 _pascal 和_cdecl。

可參考:

C/C++:函數的編譯方式與呼叫慣例

當Windows 的「外殼」(shell)偵測到使用者意欲執行一個Windows 程式,於是調用載入器把該程式載入,然後調用C startup code,

後者再調用WinMain,開始執進程式。WinMain 的四個參數由作業系統傳遞進來。

視窗類別別之註冊與視窗之誕生

一開始,Windows 程式必須做些初始化工作,為的是產生應用程式的工作舞台:視窗。這沒有什麼困難,因為API 函數CreateWindow 完

全包辦了整個巨大的工程。但是視窗產生之前,其屬性必須先設定好。所謂屬性包括視窗的「外貌」和「行為」,一個視窗的邊框、顏色、標

題、位置等等就是其外貌,而視窗接收訊息後的反應就是其行為(具體地說就是指視窗函數本身)。程式必須在產生視窗之前先利用API 函數

RegisterClass設定屬性(我們稱此動作為註冊視窗類別別)。RegisterClass 需要一個大型資料結構WNDCLASS 做為參數,

CreateWindow 則另需要11 個參數。

圖:RegisterClass與CreateWindow

 

初始化工作完成後,WinMain 進入所謂的訊息迴圈:

while (GetMessage(&msg,...)) {

TranslateMessage(&msg); // 轉換鍵盤訊息

DispatchMessage(&msg); // 指派訊息

}

其中的TranslateMessage 是為了將鍵盤訊息轉化,DispatchMessage 會將訊息傳給視窗函數去處理。沒有指定函數名稱,卻可以將訊息傳

送過去,豈不是很玄?這是因為訊息發生之時,作業系統已根據當時狀態,為它標明了所屬視窗,而視窗所屬之視窗類別別又已經明白標示了窗

口函數(也就是wc.lpfnWndProc 所指定的函數),所以DispatchMessage自有脈絡可尋。DispatchMessage 經過USER 模組的協助,才

把訊息交到視窗函數手中。

訊息迴圈中的DispatchMessage 把訊息分配到哪裡呢?它透過USER 模組的協助,送到該視窗的視窗函數去了。視窗函數通常利用

switch/case 方式判斷訊息種類,以決定處置方式。由於它是被Windows 系統所調用的(我們並沒有在應用程式任何地方調用此函數),所

以這是一種call back 函數,意思是指「在你的程式中,被Windows 系統調用」的函數。這些函數雖然由你設計,但是永遠不會也不該被你調

用,它們是為Windows 系統準備的。

程式進行過程中,訊息由輸入裝置,經由訊息迴圈的抓取,源源傳送給視窗並進而送到視窗函數去。視窗函數的體積可能很龐大,也可能很精

簡,依該視窗感興趣的訊息數量多寡而定。

至於視窗函數的形式,相當一致,必然是:

LRESULT CALLBACK WndProc(HWND hWnd,

UINT message,

WPARAM wParam,

LPARAM lParam)

注意,不論什麼訊息,都必須被處理,所以switch/case 指令中的default: 處必須調用DefWindowProc,這是Windows 內部預設的訊息處

理函數。

對話方塊

Windows 的對話方塊依其與父視窗的關係,分為兩類:

1. 「令其父視窗除能,直到對話方塊結束」,這種稱為modal 對話方塊。

2. 「父視窗與對話方塊共同運行」,這種稱為modeless 對話方塊。比較常用的是modal 對話方塊。

為了做出一個對話方塊,程式員必須準備兩樣東西:

1. 對話方塊模板(dialog template)。這是在RC 檔案中定義的一個對話方塊外貌,以各種方式決定對話方塊的大小、字形、內部有哪些控制組

件、各在什麼位置...等等。

2. 對話方塊函數(dialog procedure)。其類型非常類似視窗函數,但是它通常只處理WM_INITDIALOG 和WM_COMMAND 兩個訊息。對

話框中的各個控制組件也都是小小視窗,各有自己的視窗函數,它們以訊息與其管理者(父視窗,也就是對話方塊)溝通。而所有的控制組件傳

來的訊息都是WM_COMMAND,再由其參數分辨哪一種控制組件以及哪一種通告(notification)。Modal 對話方塊的啟用與結束,靠的是

DialogBox 和EndDialog 兩個API 函數。

 

圖:對話方塊的誕生、運行與結束

RC檔案

RC 檔案是一個以文字描述資源的地方。常用的資源有如下:ICON、CURSOR、BITMAP、FONT、DIALOG、MENU、TOOLBAR、

ACCELERATOR、STRING、VERSIONINFO。還可能有新的資源不斷加入。這些文字描述需經過RC 編譯器,才產生可使用的二進位代碼。

總結視窗的生命週期:

1. 程式初始化過程中調用CreateWindow,為程式建立了一個視窗,做為程式的螢幕舞台。CreateWindow 產生視窗之後會送出

WM_CREATE 直接給視窗函數,後者於是可以在此時機做些初始化動作(例如配置記憶體、開檔案、讀初始資料...)。

2. 程式活著的過程中,不斷以GetMessage 從訊息貯列中抓取訊息。如果這個訊息是WM_QUIT,GetMessage 會傳回0 而結束while 循

環,進而結束整個程式。

3. DispatchMessage 透過Windows USER 模組的協助與監督,把訊息指派至視窗函數。訊息將在該處被判別並處理。

4. 程式不斷進行2. 和3. 的動作。

5. 當使用者按下系統功能表中的Close 命令項,系統送出WM_CLOSE。通常程式的視窗函數不欄截此訊息,於是DefWindowProc 處理它。

6. DefWindowProc 收到WM_CLOSE 後, 調用DestroyWindow 把視窗清除。DestroyWindow 本身又會送出WM_DESTROY。

7. 程式對WM_DESTROY 的標準反應是調用PostQuitMessage。

8. PostQuitMessage 沒什麼其它動作,就只送出WM_QUIT 訊息,準備讓訊息迴圈中的GetMessage 取得,如步驟2,結束訊息迴圈。

圖:視窗的生命週期

相關文章

聯繫我們

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