第二天課程 — 建立項目,建立視窗
在本章,你有機會運行另外一個短程式,一個完整的 Windows 程式。通過它可以瞭解到如何建立 Windows 項目。如果你對這個簡單的 Windows 程式進行正確的編譯和運行有困難的話,這一章正是解決這些問題的。 在本章你將學會如何完成下列工作:
- 在 Visual C++ 中建立項目
- 使用 makefile 檔案
- 使用模組定義檔案(DEF檔案)
- 彈出一個傳統的視窗,它可以改變大小,縮成表徵圖和最大化
本章的目標是使你避開大量的配置問題而集中精力於編寫 Windows 代碼。
2.1 模組定義檔案(DEF)和 Makefile 檔案
標準的 Windows 程式通常至少由3個檔案組成:
1.程式的一個這個主模組或多個模組,副檔名為 .CPP。
2.一個模組定義檔案(module-defination file),副檔名為 .DEF。
3.一個專案檔(Project)或 Makefile 檔案,副檔名為 .MAK。
另外,C/C++ 程式設計人員也大量地使用資源檔(resource file),它的源副檔名為 .RC。現 在我還不準備討論這個題目。當然,許多程式還包括標頭檔(head file)。
幾乎所有的 Windows 程式設計人員,尤其是 Windows 3.x 的設計人員都使用模組定義檔案(DEF 文 件),它所要做的事情正如其名字所代表的意思那樣,用來定義一個程式主模組的特徵,包括:
- 檔案名稱
- 它的用途或主要特徵的簡單描述
- 它的棧和堆的大小(WIN 32 的選項)
- 用來定義程式處理記憶體方式的幾條語句(WIN 32 中不需要這樣定義)
不過,在定義模組定義檔案之前就建立一個 Windows 應用程式也是可以的。事實上,在 WIN 32 的應用 程式中,這種做法簡直就成了一種習慣。當然,缺少模組定義檔案程式會給出一個警告資訊,但是一般來說這種 事情並不嚴重。在發出警告後,編譯器會用預設值來代替應在 DEF 檔案中出現的值。在大多數情況下,讓編 譯程式使用這些預設值是可行的。
makefile 檔案協助你把一個項目中各自獨立的源檔案編譯成一個可執行檔。因為在 Microsoft 的 IDE 環境中,這些檔案的建立全是自動進行的,在此我就不探討 makefile 的文法了。
2.2 用 Microsoft 工具建立項目和 Makefile 檔案
用 Microsoft 工具的使用者在 IDE 中可以很容易地自動建立 makefile 檔案,只是用這種方法產生的文 件長而且複雜。
要在 Microsoft 的 IDE 中建立一個 makefile 檔案,應先進入 Microsoft IDE 並選擇 File|New, 在 New 對話方塊中選擇 Project,在 New Project 對話方塊中,按下列步驟做:
然後你可以選擇 Project|Add to Project|Files 項,將你事先編輯好的 .CPP(或者 .RC 檔案,如果 有的話)檔案插入。
接著,你可以選擇 Build|Rebuild All 來編譯你的程式,然後按 Ctrl+F5 來運行它。
2.3 建立視窗
到目前為止,你已經看到了幾個不同的 Windows 程式,但你還沒有看到一個真正的視窗。搭起一個視窗 的操作也有三步處理過程。只要編寫一次這樣程式,在以後的許多不同程式中就都可以重複使用,幾乎完全不 需要做任何修改。
下面你可以看到一個 50 行的短程式(程式清單 2.1),我用它建立了一個真正的視窗,它能完成你期 望一個視窗能完成的動作。在這個階段,你沒有必要理解它,只要知道它大概是如何工作的,也不必為其中的 任何細節而煩心。你先按程式清單 2.1 將程式建立起來並運行它,你就會看到這些代碼所做的全部事情,也 就是一個真正的 Windows 程式所做的事情。這就是說,你可以把視窗最大化、最小化或改變它的尺寸,這些 正是任何專業的 Windows 程式所應具備的功能。
程式清單 2.1 為 MakeWin 程式,該程式建立了一個傳統的視窗,它有一個標題、邊界和系統功能表。
程式清單 2.1 MakeWin 程式// Program MakeWin.cpp#define STRICT #include<windows.h>#include<windowsx.h>#include<string.h>char Name[]="MakeWin";LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);#pragma warning (disable:4068)#pragma argsused int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInstance,LPSTR lpszCmdParam,int nCmdShow){ HWND hwnd; MSG Msg; WNDCLASS WndClass; memset(&WndClass,0,sizeof(WNDCLASS)); WndClass.style = CS_HREDRAW|CS_VREDRAW; WndClass.lpfnWndProc = WndProc; WndClass.hInstance = hInst; WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); WndClass.lpszClassName = Name; RegisterClass(&WndClass); hwnd = CreateWindow(Name,Name,WS_OVERLAPPEDWINDOW,10,10,600,400,NULL,NULL,hInst,NULL); ShowWindow(hwnd,nCmdShow); UpdateWindow(hwnd); while(GetMessage(&Msg,NULL,0,0)) {TranslateMessage(&Msg);DispatchMessage(&Msg); } return Msg.wParam;} LRESULT CALLBACK WndProc(HWND hwnd,UINT Message,WPARAM wParam,LPARAM lParam) { if(Message==WM_DESTROY) {PostQuitMessage(0);return 0; } return DefWindowProc(hwnd,Message,wParam,lParam);} MakeWin 程式包含兩個主要部分:1. WinMain 函數2. WndProc 函數WinMain 函數可分為三個部分:
第一部分(18 到 24 行)用來指明視窗在哪兒註冊:18: memset(&WndClass,0,sizeof(WNDCLASS));19: WndClass.style = CS_HREDRAW|CS_VREDRAW;20: WndClass.lpfnWndProc = WndProc;21: WndClass.hInstance = hInst;22: WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);23: WndClass.lpszClassName = Name;24: RegisterClass(&WndClass);
Register 過程告訴 Windows 一個視窗類別的有關特性。但目前你還是暫時不要關心為什麼要這樣做以及 每一步的用途。你只需知道,這是典型的 WinMain 過程要做的第一件事。在下一章中註冊一個複雜動作將被 分隔成獨立的一個過程。
- 第二部分(26 到 30 行)是建立視窗:
26: hwnd = CreateWindow(Name,Name,WS_OVERLAPPEDWINDOW,27:10,10,600,400,NULL,NULL,hInst,NULL);28:29: ShowWindow(hwnd,nCmdShow);30: UpdateWindow(hwnd);
建立一個視窗分成兩步,第一步是調用 CreateWindow 函數(第 26、27 行),第二步分別 ShowWindow 函數(第 29 行)和 UpdateWindow(第 30 行)。建立一個視窗的動作如同註冊視窗過程一樣,通常也處理 成一個單獨的過程,這樣做是為了分解一個複雜的問題並建立一個良好的結構化的健壯的程式。但現在我把所以 的這些事情放在一起是為了讓你感覺到政治家們常說的“大場面”。
當使用者移動滑鼠或按鍵時,訊息就被發送給訊息迴圈,這相當於一個 Windows 程式的“駕駛員座位”, 是命令中心,一個迴圈保持著一個程式生命的重複性經曆。
到此為止,如果你還沒弄懂什麼,也不必焦急。本章最後這一段的目的只是簡單地給你一個經典的 Windows 程式的概貌,下一章你會瞭解到你所期望知道的細節。
現在你只要記住這個明確的結論:一個 Windows 程式過程有三個基本部分:1. 註冊視窗2. 建立視窗3. 進入訊息迴圈
這就像數 1,2,3 那樣簡單!
MakeWin 程式的另一個關鍵區段是 WndProc 過程,它響應程式收到的訊息。
41: LRESULT CALLBACK WndProc(HWND hwnd,UINT Message,42:WPARAM wParam,LPARAM lParam) 43: { 44: if(Message==WM_DESTROY)45: {46: PostQuitMessage(0);47: return 0;48: }49: return DefWindowProc(hwnd,Message,wParam,lParam);50: }
MakeWin 程式只直接響應 WM_DESTROY 訊息(第 44 行),其它所有訊息都轉給 DefWindowProc 去 處理。在下一章你會看到,DefWindowProc 過程只處理與視窗有關的預設行為。也就是說,當你要最大化一 個視窗或最小化一個視窗時,DefWindowProc 過程會處理你發來的訊息也知道該怎麼處理。
但是 DefWindowProc 並不處理 WM_DESTROY 訊息,這是程式員的職責。當訊息來到時,只調用 PostQuitMessage(46 行)。這樣做可以退出 WinProc,其傳回值為 0(47 行)。
我知道,在讀本章時有些讀者對什麼是訊息,訊息在 Windows 程式設計中的重要性不清楚。如果你是 新手,不熟悉這個題材也不必著急。現在,只要接受這樣一個事實:Windows 程式對訊息是響應而不是中斷。 這與許多程式設計員的習慣不同,你會發現面向訊息的作業系統具有非常簡單而且精巧的設計。
在下一章中,你會看到如何用宏來處理標準的 Windows 訊息,所以你可以把訊息響應函數放在 WndProc 之外。這如同把 Create 和 Register 函數移到 WinMain 過程之外一樣。這樣,使得 Windows 程式設計 人員能夠建立結構良好的程式,這種程式便於維護和調試。這裡我把這些過程放回到 WinMain 和 WndProc 函數中是使得你能看到一個典型的 Windows 程式的全貌。
至此,對 MakeWin 程式你只需知道這些,讓我們總結一下:
·程式中有兩個主要過程:WinMain 過程和 WndProc 過程。
·WinMain 過程有三個部分:首先註冊視窗,然後建立視窗,最後是通過一個迴圈把訊息發送給視窗。
·發送給視窗的任何訊息都通過 WndProc 傳送。Window 過程可以直接處理訊息或將訊息傳遞給 DefWindowProc 函數,它是預設訊息處理器。事實上,某些程式首先由它自己處理訊息,然後再將它傳給 DefWindowProc 作進一步的處理。
這就是現階段你需要瞭解的有關一個 Windows 程式的全部內容。關鍵是要掌握一個典型的 Windows 應 用程式的整個流程,在你的頭腦中對全域有清晰的概念,這樣在學習下一章時才能弄清細節。