windows窗口基礎2—參數說明

 

1.5、句柄
  Windows應用程序中存在許多對象,例如選單、窗口、圖標、內存對象、位圖、刷子、設備對象和程序實例等,在Windows中,對象使用句柄進行標識,這樣,通過使用一個句柄,應用程序可以訪問一個對象。
  在Windows軟件開發工具中,句柄被定義爲一種新的數據類型。在應用程序中,對句柄的使用一般只有賦值(句柄可以被賦以初始值、被改變爲用於標識同類對象中的另一個對象和被用作函數的參數)、與NULL進行相等比較(判定一個句柄是否爲一個有效的句柄)和與標識同類對象的另一個句柄進行相等比較(判定兩個句柄是否標識同一個對象),沒有其它的運算。雖然在有的書中介紹說句柄是一個十六位的整數,但實際情況並不這樣簡單,它的長度將會隨着不同的計算機平臺和Windows的發展而有所變化,例如,在32Windows中,句柄將是一個32位的數據,並且不是整數類型。
  一種通用句柄類型爲HANDLE,在Windows 3.1以前的版本中,它可被用於標識所有種類的對象,在Windows 3.1中,部分地保留了這一特點,在本教程的程序中也有所反映。在Windows 3.1中,從HANDLE類型又派生出了一些新的句柄數據類型,每種類型的句柄用於標識一種類型的對象,下面是一些常見的句柄類型:

類型

說明

HANDLE

通用句柄類型

HWND

標識一個窗口對象

HDC

標識一個設備對象

HMENU

標識一個選單對象

HICON

標識一個圖標對象

HCURSOR

標識一個光標對象

HBRUSH

標識一個刷子對象

HPEN

標識一個筆對象

HFONT

標識一個字體對象

HINSTANCE

標識一個應用程序模塊的一個實例

HLOCAL

標識一個局部內存對象

HGLOBAL

標識一個全局內存對象


1.6
、數據類型及常量
  爲便於開發Windows應用程序,Windows的開發者新定義了一些數據類型。這些數據類型或是與C/C++中已有的數據類型同義,或是一些新的結構數據類型。引入這些類型的主要目的是爲便於程序員開發Windows應用程序,同時也是爲了增強程序的可讀性;另一個目的是爲了便於程序將來能被移植到其它種類的計算機平臺上或適應Windows將來的新版本的變化。例如,本教程目前使用16APIApplication Program Interface),現在Windows的版本使用32API,只要將HANDLE等句柄類型定義爲32位長,然後重新編譯程序,就可以很方便地將一個使用16APIWindows應用程序改爲使用32API的程序,使其能運行在32API Windows上。大部分的數據類型在Windows.h中定義,下面是在這個文件中定義的部分類型:

  
#define  PASCAL     pascal
  #define  NEAR      near
  #define  FAR       far
  typedef  unsigned char  BYTE
  typedef  unsigned short WORD
  typedef  unsigned long  DWORD
  typedef  long      LONG
  typedef  char       *PSTR
  typedef   char NEAR    *NPSTR
  typedef  char FAR    *LPSTR
  typedef  void      VOID
  typedef  int       *LPINT
  typedef  LONG      (PASCAL FAR * FARPROC)();

  在Windows.h中,使用typedef還定義了一些新的結構類型。這些結構類型的名字也使用大寫形式的標識符:

類型

說明

MSG

消息結構

WNDCLASS

窗口的類的結構

PAINTSTRUCT

繪圖結構

POINT

點的座標的結構

RECT

矩形結構


  我們在這裏以類型MSG爲例來說明類型的定義方法,對於其它類型,在以後用到時再作詳細地說明。類型MSG是一個消息結構,它的定義方式及其各域的含義如下:

  
typedef struct tagMSG {
     HWND     hWnd;  // 窗口對象的標識符,該條消息傳遞到它所標識的窗口上
     UINT    message;  // 消息標識符,標識某個特定的消息
     WPARAM   wParam;  // 隨同消息傳遞的16位參數
     LPARAM   lParam;  // 隨同消息傳遞的32位參數
     DWORD     time;  // 消息產生的時間
     POINT      pt;  // 產生消息時光標在屏幕上的座標
  } MSG;
  typedef MSG FAR *LPMSG;

  其中的POINT類型的定義是:

  
typedef struct tagPOINT {
    int x;     /* X座標 */
    int y;    /* Y座標 */
  } POINT;
  typedef POINT FAR *LPPOINT;

  Windows.h在定義大部分類型的同時,還定義了該類型的指針類型。例如,上例中的LPPOINTLPMSG等,其中字母前綴LP表示遠指針類型;若使用NP作爲一個類型的前綴,則表示近指針類型;若使用P作爲一個類型的前綴時,則表示一般的指針類型,這時由編譯程序時所使用的內存模塊決定這種指針是遠指針或是近指針。在Windows.h中說明的大部分指針類型都採用這裏介紹的方法進行說明,例如,LPRECT,它表示一個RECT類型的遠指針。
  在Windows.h中說明的大部分指針類型使用了C/C++的關鍵字const,如果一個指針類型的名字前綴爲LPCNPCPC,則其中的字母C表示這種類型的指針變量所指向的變量不能通過該指針變量來修改,這種指針類型一般採用下述方法進行說明:

  
typedef const POINT FAR * LPCPOINT;
  typedef const REC  FAR * LPCRECT;

  一個使用const修飾的指針(稱其爲const指針)可以指向沒有使用const修飾的變量,但沒有使用const修飾的指針不能指向const修飾的變量,例如:

  
const POINT pt;
  LPCPOINT lpPoint = &pt;   // 正確
  LPPOINT  lpPoint = &pt;   // 錯誤

  當向函數傳遞參數時,必須特別注意這個問題,例如:

  
void fun(LPPOINT lppt) ;
  ......
  LPCPOINT lpPoint ;
  fun(lpPoint) ;

  編譯器將指示這個函數調用語句是錯誤的。所以,在一個函數不修改一個指針參數所指向的變量的情況下,最好將該參數說明爲const指針,使const類型的指針也能用於該函數的參數。Windows.h中說明的大部分函數使用了const指針參數。
  在Windows.h中,大多數語句是用於定義一個常量,例如:

  
#denfine WM_QUIT 0X0012

  該語句用標識符WM_QUIT來表示編號爲0X0012的消息。每個常量由一個前綴和表示其含義的單詞組成的標識符組成,兩者之間用下畫線隔開。前綴表明這些常量所屬的一般範疇。下面是一些前綴和它們所屬的範疇的說明。

類型

說明

CS

窗口類的風格(Class Style

IDI

預定義的圖標對象的標識符(IDentity of Icon

IDC

預定義的光標對象的標識符(IDentity of Cursor

WS

窗口的風格(Windows Style

CW

創建窗口(Create Windows

WM

窗口消息(Windows Message

DT

繪製文本(Drawing Text


  在變量名的表示方法方面,Windows推薦使用一種稱爲匈牙利表示法的方法。每個變量名用小寫字母或描述了變量的數據類型的字母作爲前綴,變量的名字緊跟其後,且用大寫字母開始的單詞(一個或多個單詞)表示其含義,這樣每個變量都能附加上其數據類型的助記符。例如:

  
WORD wOffset ;  /* w表示WORD類型 */
  DWORD dwValue ;  /* dw表示DWORD類型 */

  下面是Windows中常使用的一些字母前綴和它們所代表的數據類型:

類型

說明

b

BOOL,布爾類型

by

BYTE類型

c

char類型

dw

DWORD類型

fn

函數類型

i

整型

l

LONG類型

lp

遠(長)指針(long pointer

n

短整型

np

近(短)指針(near pointer

p

指針

s

字符串

sz

'/0'結尾的字符串

w

WORD類型

x

short,用於表示X座標時

y

short,用於表示Y座標時


  Windows程序員也可以根據上述思想和使用目的,發明一些其他的前綴,但要注意,對這些前綴的使用必須保持前後一致。在Windows中,所有的函數根據其用途來命名,它們一般由23個英文單詞組成,每個單詞的第一個字母大寫,例如,函數CreateWindow(),由該函數的名字可以知道它的用途是創建一個窗口。

1.7、應用程序使用的一些術語
  本節介紹Winodws應用程序使用的一些術語及其相關概念,在後面的章節中介紹有關的內容時,再對其中的概念進行詳細的討論。

  1.7.1 模塊
  在Windows中,術語模塊一般是指任何能被裝入內存中運行的可執行代碼和數據的集合。更明確地講,模塊指的就是一個.EXE文件(又稱爲應用程序模塊),或一個動態鏈接庫(DLL — Dynamic Linking Library,又被稱爲動態鏈接庫模塊或DLL模塊),或一個設備驅動程序,也可能是一個程序包含的能被另一個程序存取的數據資源。模塊一詞也被用於特指自包含的一段程序。例如,一個可單獨編譯的源文件,或該源文件被編譯器處理之後所生成的目標程序。當製作一個程序時,模塊一詞用於指被連接在一起的許多模塊中的某個模塊。
  Windows本身由幾個相關的模塊組成,Windows API函數就是在Windows啓動時裝入內存中的幾個動態鏈接庫模塊實現的。其中的三個主要模塊是USER.EXE(用於窗口管理等)、KERNEL.EXE(用於內存管理的多任務調度)和GDI.EXE(圖形設備接口,用於圖形輸出等)。

  1.7.2 應用程序
  一個Windows應用程序是被Windows調用或在Windows下運行的一個程序,這個程序可以調用靜態連接庫(也就是C的運行時間庫)中的函數和DLL的函數,它也可以啓動其它的應用程序。一個應用程序在運行時的輸入被Windows捕獲,並以消息的形式傳送到應用程序的活動窗口上。一個應用程序的輸出也是通過Windows進行的,所有的輸出首先被送給Windows。許多MS-DOS應用程序基本上佔據整個計算機,並認爲所有的計算機資源只屬於該應用程序,應用程序告訴相對被動的MS-DOS應做什麼。在一個Windows應用程序中,Windows自身是非常主動的,並且和應用程序協同得非常緊密。Windows管理着計算機的所有資源,並調度這些資源,使它們可爲正在Windows上運行的所有應用程序共享。

  1.7.3 任務和實例
  Windows將運行的應用程序實例作爲不同的任務。當一個應用程序的多個實例在運行時,它們也被Windows當作不同的任務。Windows爲一個模塊的每一個實例都裝入一個缺省數據段,但可執行代碼只能裝入一次。也就是說,同一個模塊的實例共享相同的代碼,但有自己私用的數據段。
  對每一個模塊、任務或實例,Windows分別使用一個句柄來標識它。在窗口對象的私有數據存儲區存儲有一個應用程序的任務句柄、實例句柄和模塊句柄。任務句柄被Windows的任務調度程序用於進行任務調度。通過模塊句柄,Windows可以知道一個模塊當前有多少實例正在運行。同一個模塊的不同實例有相同的模塊句柄,但有不同的任務和實例句柄。當Windows由於內存管理的需要而廢棄了一個實例的代碼段時,通過模塊句柄,Windows可以從模塊中重新裝入這個實例所需的代碼。

  1.7.4 動態鏈接庫
  DLL是一種有別於MS-DOS應用程序所使用的庫模塊(例如 C的運行時間庫)的一種特殊的庫模塊,它含有可能 被其它應用程序調用的函數。一個DLL在運行時被動態地連接到一個應用程序中或另一個DLL中,而不是在製作應用程序時靜態地連接到應用程序中的(這種方法是在製作MS-DOS應用程序中使用的方法,它們在Windows應用程序中仍然可以繼續被使用)。使用DLL的好處在於,當有多個應用程序使用同一個DLL並且同時在Windows中運行時,該DLL在內存中只有一個實例。

  1.7.5 應用程序設計接口
  應用程序設計接口(API)是應用程序用於操作周圍環境的一組函數調用接口。Windows API大約有600多個函 數,學習Windows程序設計的許多工作就是學習如何使用這些API

  1.7.6 Windows下的函數
  在進行Windows應用程序設計中,程序員除了需要知道有關一個函數的常用信息(例如函數的名字,近函數或遠函數,返回類型以及應如何調用)之外,同時還要知道更多的內容:一個回調函數、引出函數或是一個引入函數。
  引出函數:這個術語與一個函數如何在一個模塊中說明而在另一個模塊中被調用有關。引出函數是在一個模塊中定義而在這個模塊之外被調用的一種函數;或是被Windows或是被另一個模塊調用。這些函數必須以一種特定的方式進行說明,並被編譯器作特殊的處理。這樣,當它們被調用時,它們會被正確地束定到合適的數據段上。DLL爲其它模塊提供要被調用的函數,因此,每個DLL一般都帶有一個DLL庫,以便應用程序可以合法地調用DLL中的函數。DLL庫由DLL中每個引出函數的入口點組成。整個Windows API就是由構成Windows環境的不同的模塊所引出的函數組成,這些API函數的入口點在一個名爲IMPORT.LIBDLL庫中說明。
  引入函數:在DLL中引出的函數若要能爲一個模塊調用,必須在這個模塊中將這個函數說明爲引入函數。由此可見引出函數和引入函數表達的是從兩種角度處理同一個函數的術語:引出模塊中的一個函數使得這個函數能被其它模塊調用;調用引出函數的模塊通過引入這個函數才能調用它。在製作Windows應用程序時,連接器自動包含一個名爲IMPORT.LIB的庫文件。這個文件允許應用程序調用Windows API中的函數。這個文件被稱爲引入庫。引入庫提供了應用程序與一個到多個DLL中可被這個應用程序調用的函數之間的連接。
  回調函數:回調函數是一種特殊的引出函數,是由 Windows環境直接調用的函數。一個應用程序至少要有一個回調函數。當一條消息要交給應用程序處理時,Windows調用這個回調函數。這個函數對應於一個活動窗口,被稱爲這個窗口的窗口函數。因爲許多應用程序至少建立一個窗口,並且Windows需要向這個窗口發送消息,所以,處理消息的函數必須由Windows調用。在請求Windows枚舉它所維護的對象時,例如字體或窗口,Windows也要調用應用程序中的回調函數。當向Windows提出這樣的請求時,就必須向Windows提供回調函數的地址。
  由於引出函數是在不同的模塊中被調用的,也就是說,調用者的代碼段與被調用的引出函數的代碼段不在同一個段中,因此,在所開發的Windows應用程序中,引出函數都被說明爲遠函數。爲了程序運行的效率原因,引出函數都使用Pascal調用約定,這種調用約定不同於C調用約定的地方在於:

  • 最左邊的參數先入棧:Pascal調用約定的參數進入棧的順序是函數調用中最左邊的參數先入棧。C的調用約定與此相反,它採用最右邊的參數先入棧。
  • 被調用的函數負責從展中清除參數:Pascal調用約定的函數在返回時負責清除棧中的參數;C調用約定的函數不作這種工作,而由調用者來作;這樣,當程序中調用了大量的使用C調用約定的函數時,爲清除棧中的參數,在程序中要額外地增加許多代碼。
  • 全局標識符不保持原來的大小寫(一般被爲大寫形式),也不在標識符前面加下劃線。

  爲便於程序開發活動,在Windows.h中定義了兩個類型名,用於在程序說明引出函數:

類型

說明

WINAPI

等價於FAR PASCAL,說明該函數是一個引出函數,這個類型名只用於在DLL中說明引出函數,或在應用程序中對DLL中的引出函數進行函數說明時。

CALLBACK

等價於FAR PASCAL,說明該函數是一個回調函數,它常被用在應用程序模塊中說明一個窗口函數或其它種類的回調函數。


1.8
、事件和消息
  在Windows中,用戶或系統中所發生的任何活動被當作事件來處理,例如,用戶按下了鼠標按鈕,就產生一鼠標事件。對於所發生的每一個事件,Windows將其轉換成消息的形式放在一個稱爲消息隊列的內存區中,然後由Windows的消息發送程序選擇適合的對象,將消息隊列中的消息發送到欲接受消息的對象上。Windows的消息可分爲四種類型:
  (1輸入消息:對鍵盤和鼠標輸入作反應。這類輸入消息首先放在系統消息隊列中,然後Windows將它們送入應用程序的消息隊列,使消息得到處理。
  (2控制消息:用來與Windows的特殊控制對象,例如,對話框、列表框、按鈕等進行雙向通信。這類消息一般不通過應用程序的消息隊列,而是直接發送到控制對象上。
  (3系統消息:對程式化的事件或系統時鐘中斷作出反應。有些系統消息,例如大部分DDE消息(程序間進行動態數據交換時所使用的消息)要通過Windows的系統消息隊列。而有些系統消息,例如窗口的創建及刪除等消息直接送入應用程序的消息隊列。
  (4用戶消息:這些消息是程序員創建的,通常,這些消息只從應用程序的某一部分進入到該應用程序的另一部分而被處理,不會離開應用程序。用戶消息經常用來處理選單操作:一個用戶消息與選單中的一選項相對應,當它在應用程序隊列中出現時被處理。
  Windows應用程序通過執行一段稱爲消息循環的代碼來輪詢應用程序的消息隊列,從中檢索出該程序要處理的消息,並立即將檢索到的消息發送到有關的對象上。典型的Windows應用程序的消息循環的形式爲:

  
MSG  msg;
  while (GetMessage(&msg, NULL, 0, 0L))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  函數GetMessage從應用程序隊列中檢索出一條消息,並將它存於具有MSG類型的一個變量中,然後交由函數TranslateMessage對該消息進行翻譯,緊接着,函數DispatchMessage將消息發送到適當的對象上。有關這三個函數的更多的細節在1.10節裏介紹

1.9、窗口對象
  對Windows用戶和程序員而言,窗口對象(簡稱窗口)是一類非常重要的對象。尤其對程序員,窗口的定義和創建以及對窗口的處理過程最能直觀地反映出Windows中面向對象的程序設計的四個基本機制(類、對象、方法、和消息)。

  1.9.1 窗口類
  如前所述,在程序中創建對象,必須先定義對象所屬的類。在Windows中,窗口類是在類型爲WNDCLASS的結構變量中定義的,在Windows.h中,結構類型WNDCLASS的說明爲:

  
typedef struct tagWNDCLASS {
     DWORD style;         /* 窗口風格 */
     WNDPROC *lpfnWndProc;    /* 窗口函數 */
     int cbClsExtra;       /* 類變量佔用的存儲空間 */
     int cbWndExtra;       /* 實例變量佔用的存儲空間 */
     HINSTANCE hinstance;    /* 定義該類的應用程序實例的句柄 */
     HICON hicon;        /* 圖標對象的句柄 */
     HCURSOR hCursor;      /* 光標對象的句柄 */
     HBRUSH hbrBackground;    /* 用於擦除用戶區的刷子對象的句柄 */
     LPCSTR lpszMenuName;    /* 標識選單對象的字符串 */
     LPCSTR lpszClassName;    /* 標識該類的名字的字符串 */
  } WNDCLASS;

  WNDCLASS類型有十個域,它描述了該類的窗口對象所具有的公共特徵和方法。在程序中可以定義任意多的窗口類,每個類的窗口對象可以具有不同的特徵。lpszClassName是類的名字,在創建窗口對象時用於標識該窗口對象屬於哪個類。lpfnWndProc是指向函數的一個指針,所指向的函數應具有下述的函數原型:

  
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,LPARAM lParam);

  該函數被稱爲窗口函數,其中定義了處理髮送到該類的窗口對象的消息的方法。窗口函數是一個回調函數,因此在定義窗口函數時要使用CALLLBACK類型進行說明。參數hWnd是一個窗口對象的句柄。通過該句柄,一個窗口函數可以檢測出當前正在處理哪個窗口對象的消息。參數message是消息標識符,參數wParamlParam是隨同消息一起傳送來的參數,隨着消息的不同,這兩個參數所表示的含義也不大相同,在定義消息時對這兩個參數的含義一同進行定義。
  域hIconhCursorhbrBackground分別定義窗口變成最小時所顯示的圖標對象的句柄,當光標進入該類的窗口對象的顯示區域時所顯示的光標對象的句柄,當需要擦除用戶區域顯示的消息時所使用的刷子對象的句柄(該刷子作用的結果形成窗口用戶區的背景色)。
  域style規定窗口的風格,它可用下列常量經位或運算之後形成:

類型

說明

CS_HREDRAW

如果窗口的水平尺寸被改變,則重畫整個窗口

CS_VREDRAW

如果窗口的垂直尺寸被改變,則重畫整個窗口

CS_BYTEALIGNCLIENT

在字節邊界上(在X方向上)定位用戶區域的位置

CS_BYTEALIGNWINDOW

在字節邊界上(在X方向上)定位窗口的位置

CS_DBLCLKS

當連續兩次按動鼠標鍵時向窗口發送該事件的消息

CS_GLOBALCLASS

定義該窗口類是一個全局類。全局類由應用程序或庫建立,並且所有的應用程序均可使用全局類

CS_NOCLOSE

禁止系統選單中的Close選項


  還有其他一些常量,在後面的章節中介紹有關內容時再進行討論。
  域lpszMenuName指向一個以‘/0’字符(稱爲空字符或NULL字符)結尾的字符串,用於標識該窗口類的所有對象所使用的缺省選單對象。如果該域爲NULL,則表示沒有缺省選單。
  域hInstance用於標識定義該窗口類的應用程序的實例句柄。每一個窗口類需要一個實例句柄來區分註冊窗口類的應用程序或DLL,該實例句柄用於確定類屬。當註冊窗口類的應用程序或DLL被終止時,窗口類被刪除。
  WNDCLASS類型規定了該類窗口對象的基本數據表示和處理消息的窗口函數,但是,在有些應用程序中,單有這些是不夠的。因此,該類型提供了兩個域cbClsExtracbWndExtra,指示系統分配額外的存儲空間用於存儲一些附加數據。其中cbClsExtra定義可以爲該類的所有對象共用的數據佔用的存儲空間的大小(以字節計);而cbWndExtra用於定義該類的每個對象私用的數據佔用的存儲空間的大小(以字節計),一個對象可以在該私有存儲空間中存儲一些數據,但該類的其他對象不能訪問到這個對象所存儲的這些私用數據。而在公用存儲空間中所存的數據可被該類的所有對象訪問到。函數SetClassWord/SetClassLongGetClassWord/GetClassLong用於訪問公用數據,函數SetWindowWord/SetWindowLong和函數GetWindowWord/GetWindowLong用於訪問特定對象的私用數據,這些函數在窗口對象一章討論。
  當程序員設置了WNDCLASS變量的各個域之後,使用函數RegisterClassWindows註冊這個類,至此,完成了定義一個窗口類的過程。函數RegisterClass的原型爲:

  
BOOL RegisterClass(LPWNDCLASS lpWndClass);

  該函數唯一的一個參數是指向WNDCLASS類型的變量的指針。函數返回非零,表示註冊成功,否則註冊失敗。不能向Windows註冊具有相同名字(lpszClassName域指向相同的兩個字符串)的兩個類,否則第二次註冊失敗並被忽略。下面是定義和註冊窗口類的程序示例說明:

  
WNDCLASS wndclass;
  wndclass.style = CS_HREDRAW|CS_VREDRAW;
  wndclass.lpfnWndProc = WndProc;
  wndclass.cbClsExtra = 0;
  wndclass.cbWndExtra = 0;
  wndclass.hInstance = hInstance;
  wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  wndclass.hbrBackground = (HBRUSH )GetStockObject( BLACK_BRUSH);
  wndclass.lpszMenuName = NULL;
  wndclass.lpszClassName = "Window";

  if (!RegisterClass(&wndclass))
    ...  / * 處理類註冊錯誤 * /

  其中,WndProc是一個窗口函數名,變量hInstance存儲着當前程序實例的句柄。Windows預定義了一些圖標、光標和刷子對象,函數LoadIcon返回預定義的應用程序圖標的句柄,該圖標由第二個參數IDI_APPLICATION來定義。函數LoadCursor返回標準箭頭光標(IDC_ARROW)的句柄,函數GetStockObject返回庫存對象中一個白色刷子(WHITE_BRUSH)的句柄。

  1.9.2 創建窗口對象
  在上一節中,我們介紹了窗口類的定義方法,窗口的某些特徵(如窗口的顏色等)屬於窗口類中定義的,並由該窗口 類的所有實例共享。在註冊了窗口類之後,程序員使用函數CreateWindow創建窗口,得到窗口類的一個實例(一個窗口對象)的句柄。一個窗口可以是一個重疊式窗口,或是一個彈出式窗口,或是一個隸屬窗口,或是一個子窗口,這在使用CreateWindow函數時指定。每一個子窗口都有一個父窗口,每一個隸屬窗口都有一個擁有者,這個擁有者是另一個窗口對象,彈出式窗口是一種特殊的窗口,這些內容在窗口對象一章介紹。

  1-1 CreateWindow 函數

用 途

創建一個重疊窗口、彈出式窗口、隸屬窗口或子窗口

 

原 型

HWND CreateWindow(

 

  LPCSTR lpClassName,

類名,指定該窗口所屬的類。

  LPCSTR lpWindowName,

窗口的名字,即在標題欄中顯示的文本。

  DWORD dwStyle,

該窗口的風格,在後面詳細介紹。

  int x,

窗口左上角相對於屏幕左上角的初始X座標。

  int y,

窗口左上角相對於屏幕左上角的初始Y座標。

  int nWidth,

窗口的寬度。

  int nHeight,

窗口的高度。

  HWND hWndParent,

一個子窗口的父窗口的句柄,或隸屬窗口的擁有者窗口的句柄,若沒有擁有或者父窗口,則爲NULL

  HMENU hMenu,

選單句柄,如果爲NULL,則使用類中定義的選單。如果建立的是一個子窗口,該參數是一個子窗口標識符,使用此標識符來區分多個窗口。

  HINSTANCE hInstance,

創建窗口對象的應用程序的實例句柄。

  VOID FAR * lpParam

創建窗口時指定的額外參數。

);

 

 
 

返回值

返回值是標識所創建的窗口對象的句柄,如果返回值爲NULL,則窗口沒有被創建。

 


  函數CreateWindow的第三個參數指定窗口的風格,表1-2是在Windows.h中定義的一些常用到的風格常量,通過將這些常量使用位運算組合在一起,形成所要求的窗口風格。

  1-2 窗口風格

類型

說明

WS_BORDER

創建一個有邊框的窗口

WS_CAPTION

創建一個有標題欄的窗口

WS_CHILDWINDOWor WS_CHILD

創建一個子窗口(不能與WS_POPUP一起使用)

WS_CLIPCHILDREN

當在父窗口內繪製時,把子窗口占據的區域剪切在外,即不在該區域內繪圖

WS_CLIPSIBLINGS

裁剪相互有關係的子窗口,不在被其它子窗口覆蓋的區域內繪圖,僅與WS_CHILD一起使用

WS_DISABLED

創建一個初始被禁止的窗口

WS_DLGFRAME

創建一個有雙邊框但無標題的窗口

WS_HSCROLL

創建一個帶水平滾動槓的窗口

WS_VSCROLL

創建一個帶垂直滾動槓的窗口

WS_ICONIC

創建一個初始爲圖標的窗口,僅可以與WS_OVERLAPPEDWINDOWS一起使用

WS_MAXIMIZE

創建一個最大尺寸的窗口

WS_MINIMIZE

創建一個最小尺寸的窗口(即圖標)

WS_MAXIMIZEBOX

創建一個帶有極大框的窗口

WS_MINIMIZEBOX

創建一個帶有極小框的窗口

WS_OVERLAPPED

創建一個重疊式窗口,重疊式窗口帶有標題和邊框

WS_POPUP

創建一個彈出式窗口,不能與WS_CHILD一起使用

WS_SYSMENU

窗口帶有系統選單框,僅用於帶標題欄的窗口

WS_THICKFRAME

創建一個邊框的窗口,使用戶可以直接縮放窗口

WS_VISIBLE

創建一個初始可見的窗口


  在Windows.h中,還定義了風格WS_OVERLAPPEDWINDOWWS_POPUPWINDOW。其中,WS_OVERLAPPEDWINDOW由下面的宏進行定義:

  
#define WS_OVERLAPPEDWINDOW(
    WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
    WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
  )

  WS_POPUPWINDOW定義爲:

  
#define WS_POPUPWINDOW ( WS_BORDER | WS_POPUP | WS_SYSMENU )

  但是,在使用WS_POPUPWINDOW時,必須組合WS_CAPTION ,否則不能使系統選單(WS_SYSMENU)在窗口上可見。另外兩個窗口風格是WS_GROUPWS_TABSTOP,這兩個窗口風格的意義在介紹對話框時進行介紹,在介紹對話框時,還將介紹其它窗口風格。
  CreateWindow函數的xy參數是窗口左上角相對於屏幕左上角的座標。這兩個參數可以使用常量CW_USEDFAULT,用於表示使用缺省位置。缺省時,Windows顯示各個重疊窗口的位置在水平方向的垂直方向上均與屏幕左上角有一個相應的偏移值。nWindthnHeight參數也可以使用常量CW_USEDEFAULT來指定,這時,Windows使用缺省的窗口尺寸。缺省的窗口尺寸在水平方向延伸到屏幕的右邊界,在垂直方向延伸到屏幕底部顯示圖標區域的上方。
  下面的程序說明在Windows程序中創建一個窗口對象的基本方法,所創建的窗口對象所屬的類爲在1.9.1節定義的“Window”窗口類。

  
HWND hWnd;

  hWnd = CreateWindow(
    "Windows",
    "Sample Program",
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT,CW_USEDEFAULT,
    CW_USEDEFAULT,CW_USEDEFAULT,
    NULL,   // 沒有父窗口
    NULL,   // 使用類選單
    hInstance, // 變量hInstance中存儲有當前程序實例的句柄
    NULL,   // 沒有額外數據
  };

  其中所使用的符號“//”C++語言新增加的單行註釋符,它表示從“//”開始到它所在的行的結尾所有內容都是註釋。

  1.9.3 窗口函數
  在前面兩小節中,我們介紹了定義類和創建對象的過程。本節介紹窗口對象如何接收和處理所有影響窗口的事件(如擊鍵或按動鼠標鍵)的消息。一個窗口對象所接受到的消息的響應是由該對象的方法決定的,這些方法被定義在一個稱爲窗口函數的函數中。同一類的所有對象共用同一個窗口函數。窗口函數決定着對象如何用內部方法對消息作出響應,例如,如何在屏幕上畫出窗口自身。
  一個最簡單的窗口函數爲:

  
LRESULT CALLBACK WndProc(HWND hwnd, UNIT message, WPARAM wParam, LPARAM lParam)
  {
    return DefWindowProc (hwnd, message, wParam, lParam);
  }

  該窗口函數通過調用Windows的函數DefWindowProc(缺省窗口函數),讓Windows的缺省窗口函數來處理所有發送到窗口對象上的消息。
  當用戶操作屏幕上的一個窗口對象時(例如用戶改變了屏幕上窗口對象的位置或大小)或發生其它事件時,該事件的消息被存於應用程序的消息隊列中, 消息循環首先從該隊列中檢索出該消息,然後將消息發送到某個對象上。發送過程由Windows來控制,Windows根據消息結構中的hWnd域所指示的消息發送的目標對象,調用該對象所在類的窗口函數完成消息的發送工作。窗口函數根據消息的種類 ,選擇執行一段代碼(方法),對消息進行處理,並通過return語句回送一個處理結果或狀態。消息循環、Windows和窗口函數協同配合,完成一條消息的發送和處理。在處理完一條消息之後,如果應用程序隊列中還有其他消息,繼續進行上述處理過程,否則,應用程序在消息循環處理進行等待。

  1.9.4 處理消息
  窗口對象接收到的每條消息由參數message來標識,隨同該消息一傳遞過來的其它數據由參數wParamlParam給出。wParam用於十六位的數據,而lParam用於32位的數據。
  在窗口函數中,使用switch語句來判斷窗口函數接收到什麼消息,通過執行相應的語句對消息進行處理。當處理完一條消息時,窗口函數要返回一個值,表示消息的處理結果,許多消息返回0值,有些要求返回其它的值,這由具體的消息決定。窗口函數不打算處理的消息必須交由DefWindowProc()進行處理,並且函數必須返回DefWindowProc()的返回值。
  窗口函數的基本結構爲:

  LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  {
    變量說明語句
    初始化語句
    switch (message)
    {
      case 消息 1:
        處理消息 1”的語句序列
        return 表達式 1;

      case 消息 2:
        處理消息 2”的語句序列
        return 表達式 2;

         .......

      case 消息 n:
        處理消息 n”的語句序列
        return 表達式 n;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
  }

  Windows爲預定義的每種消息都指定了一個以WMWindow Message)爲前綴的標識符常量。下面的窗口函數處理一條WM_DESTROY消息。

  LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  {
    switch (message)
    {
      case WM_DESTROY:
        PostQuitMessage(0):
               return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
  }

  在1.11節,我們結合對Windows程序結構的介紹再詳細解釋窗口是如何處理WM_DESTROY消息的。

1.10Windows應用程序的面向對象認識
  面向對象作爲一種方法學,要求將程序中的數據和操作(代碼)歸結到某些對象名下,將數據看作對象的屬性,要改變這些屬性,必須通過操作來進行。
  進行面向對象的程序設計最好使用面向對象的語言,如C++SamllTalk等。面向對象的語言的語言所起的作用,就是給程序員們提供一些進行面向對象的程序設計時必需的約束,使數據和操作的銜接有一種顯式的描述,並進行一些技術性的事務管理。但是,如果我們能理解面向對象程序設計的原理和方法,即使不使用面向對象的語言,也能實現面向對象的程序設計。
  Windows本身並不是一個面向對象的程序設計環境,但Windows的某些部分還是明顯地受到面向對象的軟件的概念的影響。從某種程度上說,在進行Windows程序設計時,程序員是在進行面向對象的程序設計。理解Windows的面向對象的思想和應用程序設計的面向對象方法對設計結構合理的應用程序會有很大的幫助。
  前面已給出了對象的定義:每個對象包含有數據和代碼,代碼描述了對象可執行的一系列預定義的動作,而數據是對象私有的,它們由相關的可執行代碼存取。預定義的動作和私有數據的結合稱爲封裝。在C中,我們使用一個函數來封裝一個對象的私有數據和動作,使用switch語句來定義預定義的動作,這些動作只存取爲該函數本身所知道的數據。
  WindowsWindows應用程序是怎樣發送消息的呢?在Windows及其應用程序中,消息被表示爲一個數據結構,並能在對象之間傳遞。發送消息等價於執行其參數表示消息數據的函數調用,參數之一是一個標識該消息的預定義的消息標識符,當一個對象接受到一條消息時,消息標識符決定該對象執行何種動作。消息傳遞是以函數調用的形式來實現的,這種調用可以發生在程序的任何地方。
  Windows程序員必須清楚用消息引發動作的技術。不同的對象能以不同的動作響應同樣的消息。這樣,一個特定的消息可代表一個通用事件。例如,按鍵操作、移動鼠標或繪製用戶區等;而任何一個特定的消息可以在不同的對象中引發不同的動作,例如,不同的窗口對象以不同的動作處理同樣的WM_KEYDOWNWM_MOUSEMOVEWM_PAINT消息。
  一個消息可以有一個對象發送到另一個對象,或由Windows發送到某個對象。例如,WM_KEY_DOWN之類的消息是由Windows產生的。有些消息在對象的窗口函數對其處理完畢後就消失了,而有些消息在處理時有產生新的消息:一個對象通過向其它對象或自己發送一條或多條消息來處理一條消息。這樣,Windows應用程序的控制流程不象MS-DOS應用程序那樣易於跟蹤,程序的調試也比MS-DOS應用程序困難。
  除了個別消息以外,對象接受消息的順序是不可預知的,但對象處理每條消息所採取的動作是顯式定義在窗口函數中的。對象並不顯式地定義所有可能消息的動作,對於不顯示處理的消息,都交由DefWindowProc進行缺省處理。
  消息傳遞的途徑很簡單:從一個對象傳遞到另一對象,但由於DefWindowProc對有些消息提供了缺省處理,因此,程序員在設計程序時必須考慮在一個窗口函數中捕獲某條消息時是否還應交給DefWindowProc函數作進一步的處理。DefWindowProc能處理所有的消息,但對大部消息只是簡單地廢棄之,不作具有實際意義的處理,在窗口函數捕獲這些廢棄消息是安全的;若要捕獲其它消息,則必須瞭解DefWindowProc是怎樣處理這條消息的,並在窗口函數的處理代碼中能提供類似的處理(或將該消息交由DefWindowProc作進一步的處理)。
  現在我們討論窗口函數對對象的私有數據的處理問題。窗口類也說明了對象的私有數據,當調用CreateWindow創建一個窗口對象時,Windows爲創建的窗口對象分配私有數據存儲區,其中存儲有窗口的實例句柄、父窗口句柄、窗口函數的地址和其它Windows用於管理窗口對象的數據。對這些私有數據的的操作只能使用GetWindowWord/GetWindowLong等函數。對於程序中說明的變量,如何在窗口函數中將它們與相關的對象銜接在一起就比較複雜,因爲窗口函數爲該類的所有對象共享,該類的所有對象在接收到消息時都執行相同的代碼。
  在過去,Windows推薦使用的程序設計語言是C,由於C語言不具備將一個對象的私有數據和操作這些私有數據的代碼銜接在一起的語言成份(面向對象的語言的事務性工作之一就是爲程序完成這個工作),這個工作只能由程序員來作。程序員心中必須清楚程序中所說明或分配的變量私有於哪個對象,並採用合適的數據結構來表示它們,以便程序在使用它們時,能根據不同的對象將它們區別開來。
  有幾種方法可用於區分對象的私有數據:

  • 程序員編制額外的代碼來判斷一個對象應使用哪些數據。
  • 使用窗口附加字節。
  • 使用屬性表。

  當使用第一種方法時,程序實際是使用對象句柄作索引來檢索與該對象相關的私有數據,Windows也使用這種方法使用句柄來檢索一張表,這個表中存儲着該句柄所標識的對象的私有數據。Windows的許多函數需要一個對象的句柄作爲第一參數,其原因就是爲區分對象的私有數據,以便使用相同的函數處理不同的對象(的數據)。
  後兩種方法與第一種方法本質是一樣的(我們會將在後面的章節對其進行介紹),只是Windows提供了一些相關的函數來簡化程序的工作。
  由於C沒有繼承這種語言成分,因爲,也就不能形成對象的等級結構。繼承是面嚮對象語言的另一個重要成分。繼承使得程序中的對象形成一個分層次的對象結構,低層次的對象可以將它不處理的消息發送到高層對象上進行缺省處理。由於在C中不能(或說很難)建立對象的這種等級結構,但爲了簡化應用程序的設計,又必須要求支持消息的缺省處理(否則應用程序要定義一個窗口對象可能接收到的所有消息的處理代碼),因此只能使用DefWindowProc提供消息的缺省處理。這就要求對一個窗口對象所有消息的處理定義在一個函數中,就帶來了定義窗口函數的返回值和參數類型時使用了一種較難爲人理解的方法。因爲不同的消息可以帶有不同類型和個數的參數,並且返回數據的類型也不相同,Windows的設計者採用了一個折中的方法:爲消息規定一個十六位的參數和一個32位的參數,將返回類型指定爲LRESULT,這種類型的長度能容下C中所有預定義類型的數據。
  由於不同類的窗口對象定義有自己的窗口函數,但C語言不具備根據接受消息的對象自動決定調用該對象的窗口函數的能力(在面向對象的語言中,這種能力被稱爲多態性)。因此,向不同的窗口對象發送消息時使用函數SendMessage對窗口函數作間接調用,由Windows根據該函數調用中所使用的對象標識符來調用該對象的窗口函數。
  在程序設計中由於窗口函數的限制,需經常進行各種各樣的數據類型轉換。例如:

  
SendMessage(hwnd, WM_USER, (WPARAM)5, MAKELPARAM(89, 3267));

  在這個例子中,爲了組建一個LPARAM類型的數據,使用了宏MAKEPARAM。它將兩個十六位的數據組裝成一個32位的數據(低位字爲MAKEPARAM的第一個參數,高位字爲第二個參數)。當需要從一個LPARAM類型的數據中分離出低位字和高位字時,使用宏LOWORDHIWORD。例如,處理上個例子中所發送的WM_USER消息的窗口函數的代碼可能爲:

  
WORD wStart = LOWORD(lParam);
  WORD wStart = LOWORD(lParam);

  宏MAKELRESULTMAKELPARAM類似,它被用於裝配LRESULT類型的數據。宏MAKELONG用於裝配LONG類型的數據,當需要從LRESULTLONG類型的數據中分離出高位字和低位字時,使用宏HIWORDLOWORD
  基於上面的介紹,我們在設計Windows應用程序時,要明確程序中存在哪些對象,對象之間是如何通過消息傳遞程序控制的,哪些數據是對所有對象公有的,哪些數據是私有於某一個對象的,公有數據和對象的私有數據必須是存儲在靜態生存期的變量中(局部生存期的變量在窗口函數返回後就消失了,不能在下次調用該函數時保存上次的值。換句話說,存儲對象的數據的變量的生存期不應小於對象的生存期)。
  由於Windows應用程序各個模塊之間主要是通過消息傳遞控制,因此,Windows應用程序的邏輯結構就不同於MS-DOS應用程序的邏輯結構,如圖1-1所示。從圖1-1可以看出,Windows應用程序的各個模塊通過消息傳遞被聯繫在一起,因此,如果正確地組織程序,程序的模塊性和結構較MS-DOS應用程序要好。

1-1 DOS應用程序與Windows應用程序邏輯結構的比較示意說明


1.11
Windows程序的組織
  將1.9節介紹的程序按照C/C++語言的要求組織起來,就得到一個完整的Windows程序。一個Windows程序必須有一個名爲WinMain的主函數。

  
// 1-1.c 代碼片段
  #include <windows.h>

  LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

  int PASCAL WinMain(
    HINSTANCE hInstance,   // 應用程序的實例句柄
    HINSTANCE hPrevInstance, // 該應用程序前一個實例的句柄
    LPSTR lpszCmdLine,    // 命令行參數串
    int nCmdShow)       // 程序在初始化時如何顯示窗口
  {
     char szAppName[] = "Window";
    HWND hwnd;
    MSG msg;
    WNDCLASS wndclass;
    if (!hPrevInstance) {
      // 該實例是程序的第一個實例,註冊窗口類
      wndclass.style = CS_VREDRAW | CS_HREDRAW;
      wndclass.lpfnWndProc = WndProc;
      wndclass.cbClsExtra = 0;
      wndclass.cbWndExtra = 0;
      wndclass.hInstance = hInstance;
      wndclass.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
      wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
      wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
      wndclass.lpszMenuName = NULL;
      wndclass.lpszClassName = szAppName;

      if (!RegisterClass(&wndclass))
        // 如果註冊失敗
        return FALSE;
    }

    // 對每個實例,創建一個窗口對象
    hwnd = CreateWindow(
      szAppName,
      "Sample Program",
      WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, CW_USEDEFAULT,
      CW_USEDEFAULT, CW_USEDEFAULT,
      NULL,
      NULL,
      hInstance,
      NULL);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while(GetMessage(&msg, NULL, 0, 0)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }

    return msg.wParam;
  }

  WinMain函數是Windows應用程序開始執行時的入口點,它的返回類型爲intWinMain函數的作用十分類似於MS-DOS中的C應用程序的main函數。
  WinMain帶有四個參數。參數hInstancehPrevInstance是程序的實例句柄。在Windows環境下,可以運行同一個程序的多個拷貝,每一個拷貝都是該應用程序的一個句柄,每個實例使用一個實例句柄進行標識。hInstance是標識當前程序的實例的句柄,它的值不會爲NULL。如果在此之前Windows中已經運行了該程序的另一個實例,則這個實例的句柄由參數hPrevInstace給出。如果在運行該程序時,Windows環境中不存在該程序的另一個實例,則hPrevInstanceNULL
  我們曾經說過,對同一個類,不能向Windows註冊一次以上。在這個程序中,通過判別hPrevInstance的值是否爲NULL,來決定是否應向Windows註冊窗口類。這樣的程序邏輯保證了只在該程序的第一個實例中註冊窗口類。
  參數lpszCmdLine中包含有運行程序時傳遞給程序的命令行參數。例如,若以這樣的命令運行該程序。Sample.exe Programming Windows。則lpszCmdLine將指向字符串“Programming Windows”
  最後一個參數nCmdShow是一個int類型的整數,用以說明在程序被裝如內存時,Windows以何種方式顯示這個程序的窗口。根據運行程序的方式不同,該參數被設置爲SW_SHOWNORMALSW_SHOWMINNOACTIVESW的含義是“Show Window”(顯示窗口),這兩個參數的含義在後面介紹。

在程序Sample.CPP中,有幾個函數我們未曾介紹。表1-3給出了這些函數的說明。

  1-3-1 ShowWindow 函數

用 途

顯示或改變給定的窗口

 

原 型

BOOL ShowWindow(

 

  HWND hWnd,

指定一個窗口對象。

  int nCmdShow

指定窗口的顯示方式。

);

 

 
 

返回值

返回該窗口更新前的窗口狀態。對先前可見的窗口,其值爲非零。對先前隱藏的窗口,其值爲零。

 


  顯示方式(nCmdShow)可以是下列常量之一:

類型

說明

SW_HIDE

隱藏該窗口(並是另一個窗口激活)

SW_MINIMIZE

使窗口變成圖標(並激活窗口管理表的頂層窗口)

SW_SHOW

激活一個窗口,並根據其當前的尺寸和位置顯示該窗口

SW_SHOWMAXIMIZED

激活並以全屏方式顯示一個窗口

SW_SHOWMINIMIZED

激活並以圖標方式顯示一個窗口

SW_SHOWMINNOACTIVE

以圖標方式顯示一個窗口,當前活動的窗口仍保持活動

SW_SHOWNA

以當前狀態顯示一個窗口,當前活動的窗口仍保持活動

SW_SHOWNOACTIVE

以最近的大小和位置顯示一個窗口,當前活動的窗口仍保持活動

SW_SHOWNORMAL

激活並顯示一個窗口,若其爲圖標或全屏方式顯示,則恢復爲它的原始大小和位置

SW_RESTORE

SH_SHOWNORMAL


  1-3-2 UpdateWindow 函數

用 途

若應用程序的消息隊列中存在WM_PAINT消息(繪製用戶區消息),則該函數使Windows立即調用窗口函數,向其傳遞WM_PAINT。否則該函數不作爲任何動作。

 

原 型

VOID UpdateWindow(

 

  HWND hWnd,

標識被刷新的窗口的句柄。

);

 

 
 

返回值

 


  1-3-3 GetMessage 函數

用 途

從應用程序中的消息隊列中檢索一條消息。

 

原 型

BOOL GetMessage(

 

  LPMSG lpMsg,

指向MSG類型的變量的遠指針,它包含有從應用程序消息隊列中檢索到的一條消息的數據。

  HWND hWnd,

指定爲哪個窗口檢索消息,如果hWndNULL,則檢索調用該函數的應用程序的所有的消息(不檢索屬於其它應用程序的消息)。

  UINT wMin,

 

  UINT wMax

以下兩個基本參數指定檢索在wMinwMax範圍內的消息。如果這兩個參數都爲零,該函數檢索所有的可用的消息。

);

 

 
 

返回值

在檢索出WM_QUIT消息時,返回零值,在其它情況下返回非零值。

 


  1-3-4 DispatchMessage 函數

用 途

將消息發送到指定的窗口對象上(窗口函數被調用)。

 

原 型

LRESULT DispatchMessage(

 

  LPCMSG lpMsg

指向MSG類型變量的遠指針,該變量中存儲有來自應用程序消息隊列中的消息。

);

 

 
 

返回值

若有一個WM_CHAR消息被放到應用程序的消息隊列中,返回非零,否則返回零。該函數不改變lpMsg所指向的變量中存儲的消息數據。

 


  Windows的主函數都是首先以初始化(註冊類、創建對象等)這一步開始,而且緊跟着就是消息循環運行這一步。這些步驟對所有的Windows應用程序都大同小異。Windows應用程序主要的不同點在窗口函數的定義上,由於一個應用程序所解決的任務不同,它的窗口函數對消息的處理方式也就不相同,因而每個應用程序需要定義不同的窗口函數。

  LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  {
    switch (message)
    {
      case WM_DESTROY:
        PostQuitMessage(0):
               return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
  }

  這個窗口函數僅處理一條WM_DESTROY消息。這條消息是在用戶關閉了屏幕上的窗口時,Windows發送給窗口對象的。該函數對這條消息的處理只是簡單地調用Windows函數PostQuitMessage。表1-4給出了函數PostQuitMessage的說明。當主函數的消息循環中的GetMessage函數檢索出WM_QUIT消息時,函數GetMessage返回零,這樣,消息循環終止,程序也隨之被終止。存儲消息數據的變量msgwParam域的值是在調用函數PostQuitMessage時所提供的實參的值。如果程序正常結束,調用PostQuitMessage函數時使用零作爲該函數的參數,如果需要表示程序由於出現了異常或錯誤而必須終止時,使用非零值(一般使用-1)作爲該函數的參數。在調用PostQuitMessage使用的參數值被主函數用語句:

  
return msg.wParam;

  返回給Windows,供Windows或其它應用程序使用。因此,我們也稱PostQuitMessage使用的參數爲程序的退出碼。

  1-4 PostQuitMessage 函數

用 途

通知Windows,應用程序希望中止。它一般用於響應WM_DESTROY消息。該函數將消息WM_QUIT消息放入應用程序的消息隊列中。

 

原 型

PostQuitMessage(

 

  int nExitCode

指定應用程序的退出代碼,它用作WM_QUIT消息的wParam參數。

);

 

 
 

返回值

 


  小結
  本章首先介紹了圖形用戶界面的優點和麪向對象的程序設計方法。從某種意義上說,Windows是面向對象的,它主要建立在把窗口作爲一個對象的概念上。窗口之間通過消息進行消息傳遞。
  Windows支持直接操作技術。直接操作是對屏幕對象的操作,數據和函數的封裝允許該對象自己響應它們接收到的消息。在用戶界面上發生的任何事件被作爲消息發送給窗口對象。程序員在設計程序時,只須關心一個對象要接受哪些消息和怎樣處理這些消息。消息傳遞工作由Windows負責。因而,使用Windows操作環境可以極大地方便程序開發用戶界面的工作,並使程序的結構合理、模塊化程序高。更重要的是,支持直接操作技術的Windows支持用戶進行有創造性的界面設計。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章