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