Win32編程基礎知識
2007年10月03日 星期三 10:56
Win32編程基礎知識
正文:
盡管Windows應用程序千變萬(wàn)化,令人眼花繚亂,但,消息機制和窗口過(guò)程卻始終它們的基礎,掌握了這兩項技術(shù),也就相當于把握住了問(wèn)題的關(guān)鍵。
如果你以前是C程序員或是MFC的忠實(shí)用戶(hù),只要你學(xué)習過(guò)C語(yǔ)言的語(yǔ)法,自己親手編過(guò)一些簡(jiǎn)短的C程序,理解以下的Win32編程基礎也不是一件困難的事。
一個(gè)最簡(jiǎn)單的Win32程序
在以前的C語(yǔ)言編程中,一個(gè)最簡(jiǎn)單的程序可以只有兩行。
void main(void)
{ printf "Hello World!"; }
而要實(shí)現同樣功能的Windows程序卻最少也要寫(xiě)幾十行,這并不是說(shuō)明Windows應用程序效率低下,難于掌握,只是說(shuō)明程序在Windows環(huán)境下有更豐富的內涵。Windows程序的效率其實(shí)不低,在所有的Windows應用程序中,都有一個(gè)程序初始化的過(guò)程,這得用上幾十條語(yǔ)句,這段初始化的代碼對于任何Windows應用程序而言,都是大同小異的。下面以一個(gè)實(shí)現最簡(jiǎn)單功能的程序EasyWin為例,說(shuō)明Windows程序的基本框架。
打開(kāi)Visual C++ 6.0。
選擇File菜單的New,在出現的對話(huà)框中,選擇Projects欄目(新建工程),并點(diǎn)取其下的Win32 Application項,表示使用Win32環(huán)境創(chuàng )建應用程序。先在Locatin(路徑)中填入“c:\”,然后在Project Name(項目名稱(chēng))中填入“EasyWin”,其它按照缺省設置)。單擊OK按鈕。
再次選擇File菜單的New,在出現的對話(huà)框中,選擇Files欄目(新建文件),并點(diǎn)取其下的C++ Source File項,表示新建一個(gè)C++源文件。在右邊的File欄中輸入“EasyWin”,最后確定讓Add to project檢查框打上勾 )。單擊OK按鈕。
在EasyWin.cpp文件中輸入以下源程序代碼。
//*******************************************************************
// 工程:easywin
// 文件:easywin.cpp
// 內容:一個(gè)基本的Win32程序//*******************************************************************
#include <windows.h>
#include <windowsx.h>
//函數聲明
BOOL InitWindow( HINSTANCE hInstance, int nCmdShow );
LRESULT CALLBACK WinProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );
//*******************************************************************
//函數:WinMain()
//功能:Win32應用程序入口函數。創(chuàng )建主窗口,處理消息循環(huán)
//*******************************************************************
int PASCAL WinMain( HINSTANCE hInstance, //當前實(shí)例句柄
HINSTANCE hPrevInstance, //前一個(gè)實(shí)例句柄
LPSTR lpCmdLine, //命令行字符
int nCmdShow) //窗口顯示方式
{
MSG msg;
//創(chuàng )建主窗口
if ( !InitWindow( hInstance, nCmdShow ) )
return FALSE;
//進(jìn)入消息循環(huán):
//從該應用程序的消息隊列中檢取消息,送到消息處理過(guò)程,
//當檢取到WM_QUIT消息時(shí),退出消息循環(huán)。
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//程序結束
return msg.wParam;
}
//******************************************************************
//函數:InitWindow()
//功能:創(chuàng )建窗口。
//******************************************************************
static BOOL InitWindow( HINSTANCE hInstance, int nCmdShow )
{
HWND hwnd; //窗口句柄
WNDCLASS wc; //窗口類(lèi)結構
//填充窗口類(lèi)結構
wc.style = CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = (WNDPROC)WinProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon( hInstance, IDI_APPLICATION );
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = "EasyWin";
//注冊窗口類(lèi)
RegisterClass( &wc );
//創(chuàng )建主窗口
hwnd = CreateWindow(
"EasyWin", //窗口類(lèi)名稱(chēng)
"一個(gè)基本的Win32程序", //窗口標題
WS_OVERLAPPEDWINDOW, //窗口風(fēng)格,定義為普通型
100, //窗口位置的x坐標
100, //窗口位置的y坐標
400, //窗口的寬度
300, //窗口的高度
NULL, //父窗口句柄
NULL, //菜單句柄
hInstance, //應用程序實(shí)例句柄
NULL ); //窗口創(chuàng )建數據指針
if( !hwnd ) return FALSE;
//顯示并更新窗口
ShowWindow( hwnd, nCmdShow );
UpdateWindow( hwnd );
return TRUE;
}
//******************************************************************
//函數:WinProc()
//功能:處理主窗口消息
//******************************************************************
LRESULT CALLBACK WinProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch( message )
{
case WM_KEYDOWN://擊鍵消息
switch( wParam )
{
case VK_ESCAPE:
MessageBox(hWnd,"ESC鍵按下了!","Keyboard",MB_OK);
break;
}
break;
case WM_RBUTTONDOWN://鼠標消息
{
MessageBox(hWnd,"鼠標右鍵按下了!","Mouse",MB_OK);
break;
}
case WM_PAINT://窗口重畫(huà)消息
{
char hello[]="你好,我是EasyWin !";
HDC hdc;
PAINTSTRUCT ps;
hdc=BeginPaint( hWnd,&ps ); //取得設備環(huán)境句柄
SetTextColor(hdc, RGB(0,0,255)); //設置文字顏色
TextOut( hdc, 20, 10, hello, strlen(hello) );//輸出文字
EndPaint( hWnd, &ps ); //釋放資源
break;
}
case WM_DESTROY://退出消息
PostQuitMessage( 0 );//調用退出函數
break;
}
//調用缺省消息處理過(guò)程
return DefWindowProc(hWnd, message, wParam, lParam);
}
程序輸入完畢,即可編譯執行。在窗口中擊鼠標鍵或按ESC鍵時(shí),會(huì )彈出一個(gè)對話(huà)框以表示你的操作。
其實(shí),這個(gè)程序可以看成是所有Win32應用程序的框架,在以后所有的程序中,你會(huì )發(fā)現它們都是在這個(gè)程序的基礎之上再添加代碼。
WinMain()函數
WinMain()函數是應用程序開(kāi)始執行時(shí)的入口點(diǎn),通常也是應用程序結束任務(wù)退出時(shí)的出口點(diǎn)。它與DOS程序的main()函數起同樣的作用,有一點(diǎn)不同的是,WinMain()函數必須帶有四個(gè)參數,它們是系統傳遞給它的。WinMain()函數的原型如下:
int PASCAL WinMain( HINSTANCE hInstance, //當前實(shí)例句柄
HINSTANCE hPrevInstance, //前一個(gè)實(shí)例句柄
LPSTR lpCmdLine, //命令行字符
int nCmdShow) //窗口顯示方式
第一個(gè)參數hInstance,是標識該應用程序當前的實(shí)例的句柄。它是HINSTANCE類(lèi)型,HINSTANCE是Handle of Instance的縮寫(xiě),表示實(shí)例的句柄。hInstance是一個(gè)很關(guān)鍵的數據,它唯一的代表該應用程序,在后面初始化程序主窗口的過(guò)程中需要用到這個(gè)參數。
這里有兩個(gè)概念,一個(gè)是實(shí)例,一個(gè)是句柄。實(shí)例代表的是應用程序執行的整個(gè)過(guò)程和方法,一個(gè)應用程序如果沒(méi)有被執行,只是存在于磁盤(pán)上,那么就說(shuō)它是沒(méi)有被實(shí)例化的;只要一執行,則說(shuō)該程序的一個(gè)實(shí)例在運行。句柄,顧名思義,指的是一個(gè)對象的把柄。在Windows中,有各種各樣的句柄,它們都是32位的指針變量,用來(lái)指向該對象所占據的內存區。句柄的使用,可以極大的方便Windows管理其內存中的各種對象。
第二個(gè)參數是hPrevInstance,它是用來(lái)標識該應用程序的前一個(gè)實(shí)例句柄。對于基于Win32的應用程序來(lái)說(shuō),這個(gè)參數總是NULL。這是因為在Win95操作系統中,應用程序的每個(gè)實(shí)例都有各自獨立的地址空間,即使同一個(gè)應用程序被執行了兩次,在內存中也會(huì )為它們的每一個(gè)實(shí)例分配新的內存空間,所以一個(gè)應用程序被執行后,不會(huì )有前一個(gè)實(shí)例存在的可能。也就是說(shuō),hPrevInstance這個(gè)參數是完全沒(méi)有必要的,只是為了提供與16位Windows的應用程序形式上的兼容性,才保留了這個(gè)參數。在以前的16位Windows環(huán)境下(如Windows3.2),hPrevInstance用來(lái)標識與hInstance相關(guān)的應用程序的前一個(gè)句柄。
第三個(gè)參數是lpCmdLine,是指向應用程序命令行參數字符串的指針。如在Win95的“開(kāi)始”菜單中單擊“運行”,輸入“easywin hello”,則此參數指向的字符串為“hello”。
最后一個(gè)參數是nCmdShow,是一個(gè)用來(lái)指定窗口顯示方式的整數。這個(gè)整數值可以是SW_SHOW、SW_HIDE、SW_SHOWMAXIMIZED、SW_SHOWMINIMIZED等,關(guān)于這些值的含義,將在下一節說(shuō)明。
注冊窗口類(lèi)
一個(gè)應用程序可以有許多窗口,但只有一個(gè)是主窗口,它是與該應用程序的實(shí)例句柄唯一關(guān)聯(lián)的。上面的例程中,創(chuàng )建主窗口的函數是InitWindow()。
通常要對填充一個(gè)窗口類(lèi)結構WNDCLASS,然后調用RegisterClass()對該窗口類(lèi)進(jìn)行注冊。每個(gè)窗口都有一些基本的屬性,如窗口邊框、窗口標題欄文字、窗口大小和位置、鼠標、背景色、處理窗口消息函數的名稱(chēng)等等。注冊的過(guò)程也就是將這些屬性告訴系統,然后再調用CreateWindow()函數創(chuàng )建出窗口。這也就象你去裁縫店訂做一件衣服,先要告訴店老板你的身材尺寸、布料顏色、以及你想要的款式,然后他才能為你做出一件讓你滿(mǎn)意的衣服。
在VC的幫助中,可以看到WNDCLASS結構是這樣定義的:
typedef struct _WNDCLASS {
UINT style; //窗口的風(fēng)格*
WNDPROC lpfnWndProc; //指定窗口的消息處理函數的遠指針*
int cbClsExtra; //指定分配給窗口類(lèi)結構之后的額外字節數*
int cbWndExtra; //指定分配給窗口實(shí)例之后的額外字節數
HANDLE hInstance; //指定窗口過(guò)程所對應的實(shí)例句柄*
HICON hIcon; //指定窗口的圖標
HCURSOR hCursor; //指定窗口的鼠標
HBRUSH hbrBackground; //指定窗口的背景畫(huà)刷
LPCTSTR lpszMenuName; //窗口的菜單資源名稱(chēng)
LPCTSTR lpszClassName; //該窗口類(lèi)的名稱(chēng)*
} WNDCLASS;
在Win95和WinNT的具有新界面特性的系統中,為了支持新的窗口界面特性,還有一種擴展的窗口類(lèi)型WNDCLASSEX,它的定義如下:
typedef struct _WNDCLASSEX {
UINT cbSize; //指定WNDCLASSEX結構的大小
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HANDLE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
HICON hIconSm; //窗口的小圖標
} WNDCLASSEX;
WNDCLASS和WNDCLASSEX這兩個(gè)結構基本上是一致的,只是WNDCLASSEX結構中多了cbSize和hIconSm這兩個(gè)成員。WNDCLASS結構的各成員中,其注釋后打了星號的表示該項應特別注意。
WNDCLASS結構的第一個(gè)成員style表示窗口類(lèi)的風(fēng)格,它往往是由一些基本的風(fēng)格通過(guò)位的“或”操作(操作符位“|”)組合而成。下表列出了一些常用的基本窗口風(fēng)格:
風(fēng)格 含義
CS_HREDRAW 如果窗口客戶(hù)區寬度發(fā)生改變,重繪整個(gè)窗口
CS_VREDRAW 如果窗口客戶(hù)區高度發(fā)生改變,重繪整個(gè)窗口
CS_DBLCLKS 能感受用戶(hù)在窗口中的雙擊消息
CS_NOCLOSE 禁用系統菜單中的“關(guān)閉”命令
CS_OWNDC 為該窗口類(lèi)的各窗口分配各自獨立的設備環(huán)境
CS_CLASSDC 為該窗口類(lèi)的各窗口分配一個(gè)共享的設備環(huán)境
CS_PARENTDC 指定子窗口繼承其父窗口的設備環(huán)境
CS_SAVEBITS 把被窗口遮掩的屏幕圖象部分作為位圖保存起來(lái)。當該窗口被移動(dòng)時(shí),Windows使用被保存的位圖來(lái)重建屏幕圖象
在EasyWin應用程序中,是按如下方式對WNDCLASS結構進(jìn)行填充和注冊的:
wc.style = CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = (WNDPROC)WinProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon( hInstance, IDI_APPLICATION );
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = "EasyWin";
可以看到,wc.style被設為CS_VREDRAW | CS_HREDRAW,表示只要窗口的高度或寬度發(fā)生變化,都會(huì )重畫(huà)整個(gè)窗口。
第二個(gè)成員lpfnWndProc的值為(WNDPROC)WinProc。表明該窗口類(lèi)的消息處理函數是WinProc()函數。這里,要指定窗口的消息處理函數的遠指針,輸入消息處理函數的函數名稱(chēng)即可,必要時(shí)應該進(jìn)行強制類(lèi)型轉換,將其轉換成WNDPROC型。
接下來(lái)的cbClsExtra和wc.cbWndExtra在大多數情況下都會(huì )設為0。
然后的hInstance成員,給它的值是由WinMain()傳來(lái)的應用程序的實(shí)例句柄,表明該窗口與該實(shí)例是相關(guān)聯(lián)的。事實(shí)上,只要是注冊窗口類(lèi),該成員的值始終是該程序的實(shí)例句柄,你應該象背書(shū)一樣記住它。
下面的hIcon,是讓你給這個(gè)窗口指定一個(gè)圖標,調用 LoadIcon( hInstance, IDI_APPLICATION ),可以調用系統內部預先定義好的標志符為IDC_APPLICATION的圖標作為該窗口的圖標。
同樣,調用LoadCursor( NULL, IDC_ARROW )為該窗口調用系統內部預先定義好的箭頭型鼠標。
hbrBackground成員用來(lái)定義窗口的背景畫(huà)刷顏色,也就是該窗口的背景色。調用GetStockObject(WHITE_BRUSH)可以獲得系統內部預先定義好的白色畫(huà)刷作為窗口的背景色。
上面的LoadIcon()、LoadCursor()、GetStockObject()都是Windows的API函數,它們的用法可以參看VC的幫助,這里就不多介紹了。
lpszMenuName成員的值我們給它NULL,表示該窗口將沒(méi)有菜單。如果你想讓你的窗口擁有菜單,就把lpszMenuName成員賦值為標志菜單資源的字符串。
WNDCLASS結構的最后一個(gè)成員lpszClassName是讓你給這個(gè)窗口類(lèi)起一個(gè)唯一的名稱(chēng),因為Windows操作系統中有許許多多的窗口類(lèi),必須用一個(gè)獨一無(wú)二的名稱(chēng)來(lái)代表它們。通常,你可以用你的程序名來(lái)命名這個(gè)窗口類(lèi)的名稱(chēng)。這個(gè)名稱(chēng)將在創(chuàng )建窗口的CreateWindow()函數中用到。
填充完畢后,對于WNDCLASS結構,調用RegisterClass()函數進(jìn)行注冊;對于WNDCLASSEX結構,調用RegisterClassEx()函數進(jìn)行注冊,它們的原型分別如下:
ATOM RegisterClass( CONST WNDCLASS *lpWndClass );
ATOM RegisterClassEx( CONST WNDCLASSEX *lpwcx );
該函數如調用成功,則返回一個(gè)非0值,表明系統中已經(jīng)注冊了一個(gè)名為EasyWin的窗口類(lèi)。如果失敗,則返回0。
創(chuàng )建窗口
當窗口類(lèi)注冊完畢之后,并不會(huì )有窗口顯示出來(lái),因為注冊的過(guò)程僅僅是為創(chuàng )建窗口所做的準備工作。實(shí)際創(chuàng )建一個(gè)窗口的是通過(guò)調用CreateWindow()函數完成的。窗口類(lèi)中已經(jīng)預先定義了窗口的一般屬性,而CreateWindow()中的參數可以進(jìn)一步指定一個(gè)窗口的更具體的屬性,在EasyWin程序中,是如下調用CreateWindow()函數來(lái)創(chuàng )建窗口的:
hwnd = CreateWindow(
"EasyWin", //創(chuàng )建窗口所用的窗口類(lèi)的名稱(chēng)*
"一個(gè)基本的Win32程序", //窗口標題
WS_OVERLAPPEDWINDOW, //窗口風(fēng)格,定義為普通型*
100, //窗口位置的x坐標
100, //窗口位置的y坐標
400, //窗口的寬度
300, //窗口的高度
NULL, //父窗口句柄
NULL, //菜單句柄
hInstance, //應用程序實(shí)例句柄*
NULL ); //一般都為NULL
CreateWindow()函數的參數的含義在上面的注釋中已有介紹,注釋后打了星號標記的參數應該著(zhù)重注意,其它的參數都很簡(jiǎn)單,不多做介紹,可參看VC的幫助。
第一個(gè)參數是創(chuàng )建該窗口所使用的窗口類(lèi)的名稱(chēng),注意這個(gè)名稱(chēng)應與前面所注冊的窗口類(lèi)的名稱(chēng)一致。
第三個(gè)參數為創(chuàng )建的窗口的風(fēng)格,下表列出了常用的窗口風(fēng)格:
風(fēng)格 含義
WS_OVERLAPPEDWINDOW 創(chuàng )建一個(gè)層疊式窗口,有邊框、標題欄、系統菜單、最大最小化按鈕,是以下幾種風(fēng)格的集合:WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX, WS_MAXIMIZEBOX
WS_POPUPWINDOW 創(chuàng )建一個(gè)彈出式窗口,是以下幾種風(fēng)格的集合: WS_BORDER,WS_POPUP,WS_SYSMENU。WS_CAPTION與WS_POPUPWINDOW風(fēng)格必須一起使用才能使窗口菜單可見(jiàn)
WS_OVERLAPPED 創(chuàng )建一個(gè)層疊式窗口,它有標題欄和邊框,與WS_TILED風(fēng)格一樣
WS_POPUP 該窗口為彈出式窗口,不能與WS_CHILD同時(shí)使用
WS_BORDER 窗口有單線(xiàn)邊框
WS_CAPTION 窗口有標題欄
WS_CHILD 該窗口為子窗口,不能與WS_POPUP同時(shí)使用
WS_DISABLED 該窗口為無(wú)效,即對用戶(hù)操作不產(chǎn)生任何反應
WS_HSCROLL 窗口有水平滾動(dòng)條
WS_ICONIC 窗口初始化為最小化
WS_MAXIMIZE 窗口初始化為最大化
WS_MAXIMIZEBOX 窗口有最大化按鈕
WS_MINIMIZE 與WS_MAXIMIZE一樣
WS_MINIMIZEBOX 窗口有最小化按鈕
WS_SIZEBOX 邊框可進(jìn)行大小控制的窗口
WS_SYSMENU 創(chuàng )建一個(gè)有系統菜單的窗口,必須與WS_CAPTION風(fēng)格同時(shí)使用
WS_THICKFRAME 創(chuàng )建一個(gè)大小可控制的窗口,與WS_SIZEBOX 風(fēng)格一樣.
WS_TILED 創(chuàng )建一個(gè)層疊式窗口,有標題欄
WS_VISIBLE 窗口為可見(jiàn)
WS_VSCROLL 窗口有垂直滾動(dòng)條
程序中使用了WS_OVERLAPPEDWINDOW標志,它是創(chuàng )建一個(gè)普通窗口常用的標志。而在DirectX編程中,我們常用的是WS_POPUP,用這個(gè)標志創(chuàng )建的窗口沒(méi)有標題欄和系統菜單,如果設定窗口為最大化,客戶(hù)區可以占滿(mǎn)整個(gè)屏幕,以滿(mǎn)足DirectX編程的需要。
CreateWindow()函數后面的參數中,仍用到了該應用程序的實(shí)例句柄hInstance。
如果窗口創(chuàng )建成功,返回值是新窗口的句柄,否則返回NULL。
顯示和更新窗口
窗口創(chuàng )建后,并不會(huì )在屏幕上顯示出來(lái),要真正把窗口顯示在屏幕上,還得使用ShowWindow()函數,其原型如下:
BOOL ShowWindow( HWND hWnd, int nCmdShow );
參數hWnd指定要顯示得窗口的句柄,nCmdShow表示窗口的顯示方式,這里指定為從WinMain()函數的nCmdShow所傳遞而來(lái)的值。
由于ShowWindow()函數的執行優(yōu)先級不高,所以當系統正忙著(zhù)執行其它的任務(wù)時(shí),窗口不會(huì )立即顯示出來(lái),此時(shí),調用UpdateWindow()函數以可以立即顯示窗口。其函數原型如下:
BOOL UpdateWindow( HWND hWnd );
消息循環(huán)
在Win32編程中,消息循環(huán)是相當重要的一個(gè)概念,看似很難,但是使用起來(lái)卻是非常簡(jiǎn)單。在WinMain()函數中,調用InitWindow()函數成功的創(chuàng )建了應用程序主窗口之后,就要啟動(dòng)消息循環(huán),其代碼如下:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Windows應用程序可以接收以各種形式輸入的信息,這包括鍵盤(pán)、鼠標動(dòng)作 、記時(shí)器產(chǎn)生的消息,也可以是其它應用程序發(fā)來(lái)的消息等等。Windows系統自動(dòng)監控所有的輸入設備,并將其消息放入該應用程序的消息隊列中。
GetMessage()函數則是用來(lái)從應用程序的消息隊列中按照先進(jìn)先出的原則將這些消息一個(gè)個(gè)的取出來(lái),放進(jìn)一個(gè)MSG結構中去。GetMessage()函數原型如下:
BOOL GetMessage(
LPMSG lpMsg, //指向一個(gè)MSG結構的指針,用來(lái)保存消息
HWND hWnd, //指定哪個(gè)窗口的消息將被獲取
UINT wMsgFilterMin, //指定獲取的主消息值的最小值
UINT wMsgFilterMax //指定獲取的主消息值的最大值
);
GetMessage()將獲取的消息復制到一個(gè)MSG結構中。如果隊列中沒(méi)有任何消息,GetMessage()函數將一直空閑直到隊列中又有消息時(shí)再返回。如果隊列中已有消息,它將取出一個(gè)后返回。MSG結構包含了一條Windows消息的完整信息,其定義如下:
typedef struct tagMSG {
HWND hwnd; //接收消息的窗口句柄
UINT message; //主消息值
WPARAM wParam; //副消息值,其具體含義依賴(lài)于主消息值
LPARAM lParam; //副消息值,其具體含義依賴(lài)于主消息值
DWORD time; //消息被投遞的時(shí)間
POINT pt; //鼠標的位置
} MSG;
該結構中的主消息表明了消息的類(lèi)型,例如是鍵盤(pán)消息還是鼠標消息等,副消息的含義則依賴(lài)于主消息值,例如:如果主消息是鍵盤(pán)消息,那么副消息中則存儲了是鍵盤(pán)的哪個(gè)具體鍵的信息。
GetMessage()函數還可以過(guò)濾消息,它的第二個(gè)參數是用來(lái)指定從哪個(gè)窗口的消息隊列中獲取消息,其它窗口的消息將被過(guò)濾掉。如果該參數為NULL,則GetMessage()從該應用程序線(xiàn)程的所有窗口的消息隊列中獲取消息。
第三個(gè)和第四個(gè)參數是用來(lái)過(guò)濾MSG結構中主消息值的,主消息值在wMsgFilterMin和wMsgFilterMax之外的消息將被過(guò)濾掉。如果這兩個(gè)參數為0,則表示接收所有消息。
當且僅當GetMessage()函數在獲取到WM_QUIT消息后,將返回0值,于是程序退出消息循環(huán)。
TranslateMessage()函數的作用是把虛擬鍵消息轉換到字符消息,以滿(mǎn)足鍵盤(pán)輸入的需要。DispatchMessage()函數所完成的工作是把當前的消息發(fā)送到對應的窗口過(guò)程中去。
開(kāi)啟消息循環(huán)其實(shí)是很簡(jiǎn)單的一個(gè)步驟,幾乎所有的程序都是按照EasyWin的這個(gè)方法。你完全不必去深究這些函數的作用,只是簡(jiǎn)單的照抄就可以了。
消息處理函數
消息處理函數又叫窗口過(guò)程,在這個(gè)函數中,不同的消息將用switch語(yǔ)句分配到不同的處理程序中去。Windows的消息處理函數都有一個(gè)確定的樣式,即這種函數的參數個(gè)數和類(lèi)型以及其返回值的類(lèi)型都有明確的規定。在VC的說(shuō)明書(shū)中,消息處理函數的原型是這樣定義的:
LRESULT CALLBACK WindowProc(
HWND hwnd, //接收消息窗口的句柄
UINT uMsg, //主消息值
WPARAM wParam, //副消息值
LPARAM lParam //副消息值
);
如果你的程序中還有其它的消息處理函數,也都必須按照上面的這個(gè)樣式來(lái)定義,但函數名稱(chēng)可以隨便取。EasyWin中的WinProc()函數就是這樣一個(gè)典型的消息處理函數。
消息處理函數的四個(gè)參數是由GetMessage()函數從消息隊列中獲得MSG結構,然后分解后得到的。第二個(gè)參數uMsg和MSG結構中的message值是一致的,代表了主消息值。程序中用switch語(yǔ)句來(lái)將不同類(lèi)型的消息分配到不同的處理程序中去。
WinProc()函數明確的處理了4個(gè)消息,分別是WM_KEYDOWN(擊鍵消息)、WM_RBUTTONDOWN(鼠標右鍵按下消息)、WM_PAINT(窗口重畫(huà)消息)、WM_DESTROY(銷(xiāo)毀窗口消息)。
值得注意的是,應用程序發(fā)送到窗口的消息遠遠不止以上這幾條,象WM_SIZE、WM_MINIMIZE、WM_CREATE、WM_MOVE等這樣頻頻使用的消息就有幾十條。為了減輕編程的負擔,Windows的API提供了DefWindowProc()函數來(lái)處理這些最常用的消息,調用了這個(gè)函數后,這些消息將按照系統默認的方式得到處理。
因此,在switch_case語(yǔ)句中,只須明確的處理那些有必要進(jìn)行特別響應的消息,把其余的消息交給DefWindowProc()函數來(lái)處理,是一種明智的選擇,也是你必須做的一件事。
結束消息循環(huán)
當用戶(hù)按Alt+F4或單擊窗口右上角的退出按鈕,系統就向應用程序發(fā)送一條WM_DESTROY的消息。在處理此消息時(shí),調用了PostQuitMessage()函數,該函數會(huì )給窗口的消息隊列中發(fā)送一條WM_QUIT的消息。在消息循環(huán)中,GetMessage()函數一旦檢索到這條消息,就會(huì )返回FALSE,從而結束消息循環(huán),隨后,程序也結束。
小結
本章介紹的是Win32編程的基礎知識,在進(jìn)行DirectX編程之前,掌握它們是十分必要的。
通過(guò)本文的學(xué)習,你應該學(xué)到以下知識:
如何創(chuàng )建一個(gè)Win32應用程序工程
用RegisterClass()函數注冊一個(gè)窗口類(lèi),再立即調用CreateWindow()函數創(chuàng )建一個(gè)窗口的實(shí)例
設置窗口的類(lèi)型以及將一個(gè)消息處理函數與窗口聯(lián)系上
用一固定的模式開(kāi)啟消息循環(huán)
了解消息處理函數的定義規則,如何自己定義一個(gè)窗口消息處理函數
在消息處理函數中,最后必須調用DefWindowProc()函數以處理那些缺省的消息
調用PostQuitMessage()函數以結束消息循環(huán)