1. 程式人生 > >Windows程式設計學習筆記(三)——視窗和訊息

Windows程式設計學習筆記(三)——視窗和訊息

MessageBox函式會建立一個‘視窗’。在Windows中,一個視窗就是螢幕上一個矩形區域,它接收使用者的輸入並以文字或圖形的格式顯示輸出內容。
MessageBox函式建立一個視窗,但只是一個功能有限的特殊視窗。訊息視窗有一個帶關閉按鈕的標題列、一個選項圖示、一行或多行文字,以及最多四個按鈕。當然必須選擇Windows提供的。
要新增選單,或是在訊息框中顯示圖形,就需要建立自己的視窗。
自己的視窗
建立視窗很簡單,只需呼叫CreateWindow。第一個引數就是‘視窗類別名稱’,並且該視窗類別連線所謂的‘視窗訊息處理程式’
總體結構
Windows程式設計實際上是在進行一種物件導向的程式設計(OOP)。視窗:應用程式視窗和對話方塊。‘子視窗’、‘控制元件視窗’、‘子視窗控制元件’


‘Windows給程式傳送訊息’是指Windows呼叫程式中的一個函式,該函式的引數描述了這個特定訊息。這種位於Windows程式中的函式稱為‘視窗訊息處理程式’。
訊息佇列:用來存放改程式可能建立的各種不同視窗的訊息。訊息迴圈:指一小段程式程式碼,用來從佇列中取出訊息,並將它們傳送給相應的視窗訊息處理程式。當然,有些訊息直接傳送給視窗訊息處理程式,不用放在佇列裡。

HELLOWIN程式
建立一個視窗首先需要註冊一個視窗類別,那需要一個視窗訊息處理程式來處理視窗訊息。處理視窗訊息對每個Windows程式都來了負擔。
通盤考量
包含一個WinMain函式和一個WndProc(視窗訊息處理程式)。
Windows函式呼叫

HELLOWIN至少呼叫了18個Windows函式。下面按它們在HELLOWIN中出現次序列出這些函式:
LoadIcon 載入圖示供程式使用;
LoadCursor 載入滑鼠游標供程式使用;
GetStockObject 取得一個圖形物件(在這個例子中,是取得繪製視窗背景的畫刷物件)。
RegisterClass 為程式視窗註冊視窗類別。
MessageBox 顯示訊息框;
CreateWindow 根據視窗類別建立一個視窗;
ShowWindow 在螢幕上顯示視窗;
UpdateWindow 指示視窗自我更新;
GetMessage 從訊息佇列中取得訊息;
TranslateMessage 轉譯某些鍵盤訊息;
DispatchMessage 將訊息傳送給視窗訊息處理程式;
PlaySound 播放一個聲音檔案;
BeginPaint 開始繪製視窗。
GetClientRect 取得視窗顯示區域的大小;
DrawText顯示字串;
EndPaint 結束繪製視窗。
PostQuitMessage 在訊息佇列中插入一個‘退出程式’訊息;
DefWindowProc 執行內定的訊息處理。
這些函式均在Platform SDK檔案中說明,並在不同的表頭檔案中宣告,其中絕大多數宣告在WINUSER.H中。
大寫字母識別符號
在HELLOWIN.C程式中,有幾個大寫的識別符號,這些識別符號是在Windows表頭檔案中定義的。有些識別符號含有兩個字母或者三個字母的字首,這些字首後頭接著一個底線。這都是簡單的數值常數。字首指示該常數所屬的類別:CS,視窗類別樣式;CW,建立視窗;DT,繪製文字;IDT,圖示ID;IDC遊標ID;MB,訊息框;SND,聲音;WM,視窗訊息;WS,視窗樣式。
新的資料型態
其它新的資料型態也在Windows表頭檔案中使用typedef敘述或#define敘述加以定義了。
有時這些新的資料型態是方便縮寫,例如用於WndProc的第二個引數的UINT資料型態只是一個unsigned int(無正負號整數),用於WinMain的第三個引數的PSTR資料型態是指向一個字串的指標,即是一個char*。
而WndProc的第三和第四個引數分別被定義為WPARAM和LPARAM,這些名字的來源有點歷史背景。
WndProc函式傳回了一個型態為LRESULT的值,該值簡單地被定義為一個LONG。WinMain函式被指定了一個WINAPI型態(在表頭檔案中定義的所有Windows函式都被指定這種型態),而WndProc函式被指定一個CALLBACK型態。這兩個識別符號都被定義為_stdcall,表示在Windows本身和使用者的應用程式之間發生的函式呼叫的呼叫引數傳遞方式。
HELLOWIN還使用了Windows表頭檔案中定義的四種資料結構:MSG,訊息結構;WNDCLASS,視窗類別結構;PAINTSTRUCT,繪圖結構;RECT,矩形結構。前兩個在WinMain中使用,後兩個在WndProc中使用。
控制代碼簡介
最後還有三個大寫識別符號,用於不同形態的‘控制代碼’:HINSTANCE,執行實體(程式自身)控制代碼;HWND,視窗控制代碼;HDC,裝置內容控制代碼。
另外會遇到HICON(圖示控制代碼)、HCURSOR(滑鼠游標控制代碼)和HBRUSH(畫刷控制代碼)。
控制代碼是一個(通常32位)證書,代表一個物件。程式幾乎總是通過呼叫Windows函式取得控制代碼。
匈牙利表示法
變數名以一個或者多個小寫的字母開始,這些字母表示變數的資料型態。例如szCmdLine的sz代表‘以0結尾的字串’。在hInstance和hPrevInstance中的h字首表示‘控制代碼’;在iCmdShow中的i字首表示‘整數’。WndProc的後兩個引數也使用匈牙利表示法。
在命名結構變數時,可以用結構名(或結構名的一種縮寫)的小寫作為變數名的字首,或者用作整個變數名。例如,在HELL.C的WinMain函式中,msg變數時MSG型態的結構;wndclass是WNDCLASSEX型態的一個結構。在WndProc函式中,ps是一個PAINTSTRUCT結構,rect是一個RECT結構。
常用的變數字首:c,char或WCHAR或TCHAR;by,BYTE(無正負號字元);n,short;i,int;x,y,int分別用作x座標和y座標;cx,cy,int分別用作x長度和y長度;C代表‘計數器’;b或f,BOOL(int);f代表‘旗標’;w,WORD(無正負號短整數);l,LONG(長整數);dw,DWORD(無正負號長整數);fn,function(函式);s,string(字串);sz,以位元組值0結尾的字串;h,控制代碼;p,指標。

註冊視窗類別
視窗依照某一視窗類別建立,視窗類別用以標識處理視窗訊息的視窗訊息處理程式。
不同視窗可以依照同一種視窗類別建立。例如,Windows中的所有按鈕視窗-包括按鍵、複選框,以及單選按鈕-都是一句同一種視窗類別建立的。視窗類別定義了視窗訊息處理程式和依據此類別建立的視窗的其他特徵。在建立視窗時,要定義一些該視窗所獨有的特徵。
在為程式建立視窗之前,必須首先呼叫RegisterClass註冊一個視窗類別。該函式只需要一個引數,即指向型態為WNDCLASS的結構指標。此結構包括兩個指向字串的欄位,因此結構在WINUSER.H表頭檔案中定義了兩種不同的方式,第一個是ASCII版的WNDCLASSA

typedef struct tagWNDCLASSA
{
  UINT style;
  WNDPROC    lpfnWndProc;
  int  cbClsExtra;
   int  cbWndExtra;
  HINSTANCE  hInstance;
  HICON  hIcon;
   HCURSOR  hCursor;
   HBRUSH  hbrBackground;
   LPCSTR  lpszMenuName;
   LPCSTR   lpszClassName;
}
  WNDCLASSA,*PWNDCLASSA,NEAR *NPWNDCLASSA,FAR*LPWNDCLASSA;

提示一下資料型態和匈牙利表示法:lpfn字首代表‘指向函式的長指標’。(在Win32 API中,長指標和短指標(或者近程指標)沒有區別。這只是16位Windows的遺物。)
cb字首代表‘位元組數’而且通常作為一個常數來表示一個位元組的大小。h字首是一個控制代碼。而hbr字首代表‘一個畫刷的代號’。lpsz字首代表‘指向以0結尾符號串的指標’。

Unicode 版的結構定義如下
typedef struct tagWNDCLASSW
{
  UINT  style;
WNDPROC  lpfnWndProc;
int      cbClsExtra;
int    cbWndExtra;
HINSTANCE  hInstance;
HICON   hIcon;
HCURSOR  hCursor;
HBRUSH   hbrBackground;
LPCWSTR  lpszMenuName;
LPCWSTR   lpszClassName;
}
WNDCLASSW, *PWNDCLASSW,NEAR * NPWNDCLASSW, FAR * LPWNDCLASSW;

與前者唯一的區別在於最後兩個欄位定義為指向寬字串常數,而不是指向ASCII字串常數。
WINUSER.H定義了WNDCLASSA和WNDCLASSW結構(以及指向結構的指標)以後,表頭檔案依據對UNICODE識別符號的解釋,定義了WNDCLASS和指向WNDCLASS的指標(包括一些向後相容的程式程式碼):

#ifdef UNICODE
typedef  WNDCLASSW  WNDCLASS;
typedef  PWNDCLASSW  PWNDCLASS;
typedef   NPWNDCLASSW  NPWNDCLASS;
typedef  LPWNDCLASSW  LPWNDCLASS;
#else
typedef  WNDCLASSA   WNDCLASS;
typedef  PWNDCLASSA  PWNDCLASS;
typedef  NPWNDCLASSA  NPWNDCLASS;
typedef  LPWNDCLASSA  LPWNDCLASS;
#endif

本書後面列出結構時,將只列出功用相同的結構定義,對WNDCLASS就像這樣:
typedef struct
{
  UINT  style;
  WNDPROC  lpfnWndProc;
   int    cbClsExtra;
    int    cbWndExtra;
   HINSTANCE  hInstance;
   HICON   hIcon;
   HCURSOR  hCursor;
   HBRUSH  hbrBackground;
   LPCTSTR   lpszMenuName;
   LPCTSTR   lpszClassName;
}
WNDCLASS,* PWNDCLASS;

一個程式寫作者的程式不應該因為使用以LP或NP為字首的不同指標型態而被攪亂。
在WinMain中為WNDCLASS定義一個結構,通常像這樣:
WNDCLASS  wndclass;
然後,就可以初始化該結構的10個欄位,並呼叫RegisterClass。

在WNDCLASS結構中最重要的兩個欄位是第二個和最後一個,第二個欄位(lpfnWndProc)是依據這個類別來建立的所有視窗所使用的視窗訊息處理程式的地址。在HELLOWIN.C中,這是
WndProc函式。最後一個欄位是視窗類別的文字名稱,程式寫作者可以隨意定義其名稱。在只建立一個視窗的程式中,視窗類別名稱通常設定為程式名稱。
WNDCLASS結構中的每個欄位:

wndclass.style = CS_HREDRAW | CS_VREDRAW;
使用C的位‘或’運運算元結合了兩個‘視窗類別樣式’識別符號。在表頭檔案WINUSER.H中,已定義了一整組以CS為字首的識別符號:

(未完待續)《Windows程式設計(第五版)》