windows編程——窗口與消息1

1.WinMain函數

WinMain( )函數與DOS程序的main( )函數基本起同樣的作用,但有一點不同的是WinMain( )函數必須帶有四個系統傳遞給它的參數。WinMain()函數的原型如下:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

第一個參數hInstance是標識該應用程序的句柄。不過句柄又是什麼呢?其實就是一個對象或項目的標識符。hIstance唯一地代表了該應用程序,Windows使用它管理內存中的各種對象。它十分重要。在後面的初始化程序主窗口的過程中就需要使用它作爲參數。
       第二個參數是hPrevInstance,給它NULL吧,這個參數只是爲了保持與16Windows的應用程序的兼容性。
       
第三個參數是lpCmdLine,是指向應用程序命令行參數字符串的指針。比如說,如果我們執行"test hello",則此參數指向的字符串爲"hello"
       
最後一個參數是nCmdShow,是用來指定窗口如何顯示的整數。關於窗口顯示方式的種類,將在下面說明。

2.註冊窗口類

一個程序可以有許多窗口,但只有一個是主窗口,它是與該應用程序唯一對應的。

創建窗口前通常要填充一個窗口類WNDCLASS,並調用RegisterClass( )對該窗口類進行註冊。每個窗口都有一些基本的屬性,如窗口標題欄文字、窗口大小和位置、鼠標、背景色,窗口消息處理函數(後面會講這個函數)的名稱等等。註冊的過程就是將這些屬性告訴系統,然後再調用CreateWindow( )函數創建出窗口。

  下面列出了WNDCLASS的成員:

  UINT style; //窗口的風格
 
WNDPROC lpfnWndProc; //窗口消息處理函數的指針
 
int cbClsExtra; //分配給窗口類結構之後的額外字節數
 
int cbWndExtra; //分配給窗口實例之後的額外字節數
 
HANDLE hInstance; //窗口所對應的應用程序的句柄
 
HICON hIcon; //窗口的圖標
 
HCURSOR hCursor; //窗口的鼠標
 
HBRUSH hbrBackground; //窗口的背景
 
LPCTSTR lpszMenuName; //窗口的菜單資源名稱
 
LPCTSTR lpszClassName; //窗口類的名稱

WNDCLASS的第一個成員style表示窗口類的風格,它往往是由一些基本的風格通過位的"或"操作(操作符"|")組合而成。下表列出了一些常用的基本窗口風格:

風格

含義

CS_HREDRAW

如果窗口寬度發生改變,重繪整個窗口

CS_VREDRAW

如果窗口高度發生改變,重繪整個窗口

CS_DBLCLKS

能感受用戶在窗口中的雙擊消息

CS_NOCLOSE

禁用系統菜單中的"關閉"命令

CS_SAVEBITS

把被窗口遮掩的屏幕圖像部分作爲位圖保存起來。當該窗口被移動時,Windows使用被保存的位圖來重建屏幕圖像

第二個成員是lpfnWndProc,給它消息處理函數的函數名稱即可,必要時應該進行強制類型轉換,將其轉換成WNDPROC型。

接下來的cbClsExtra和wc.cbWndExtra一般都可以設爲0。

然後的hInstance成員,給它的值是窗口所對應的應用程序的句柄,表明該窗口與此應用程序是相關聯的。

下面的hIcon是讓我們給這個窗口指定一個圖標,這個程序沒有設置。

鼠標也沒有設置,因爲編遊戲時的鼠標都是在刷新屏幕時自己畫上去的。

hbrBackground成員用來定義窗口的背景色。這裏設爲CreateSolidBrush (RGB(100, 0, 0)),即暗紅色。關於CreateSolidBrush函數,請參閱4.10節。

lpszMenuName成員的值我們給它NULL,表示該窗口沒有菜單。

WNDCLASS的最後一個成員lpszClassName是讓我們給這個窗口類起一個獨一無二的名稱,因爲Windows操作系統中有許許多多的窗口類。通常,我們可以用程序名來命名這個窗口類的名稱。在調用CreateWindow( )函數時將要用到這個名稱。

填充完WNDCLASS後,我們需要調用RegisterClass( )函數進行註冊;該函數如調用成功,則返回一個非0值,表明系統中已經註冊了這個窗口類。如果失敗,則返回0 

3.創建窗口

當窗口類註冊完畢之後,我們就可以創建一個窗口,這是通過調用CreateWindow( )函數完成的。窗口類中已經預先定義了窗口的一般屬性,而在CreateWindow( )中的參數中可以進一步指定窗口更具體的屬性。下面舉一個例子來說明CreatWindow( )的用法:
 
hwnd= CreateWindow(
 
"Simple_Program",//創建窗口所用的窗口類的名稱
 
"ASimple Windows Program", //窗口標題
 
WS_OVERLAPPEDWINDOW,//窗口風格,定義爲普通型
 
100,//窗口位置的x座標
 
100,//窗口位置的y座標
 
400,//窗口的寬度
 
300,//窗口的高度
 
NULL,//父窗口句柄
 
NULL,//菜單句柄
 
hInstance,//應用程序句柄
 
NULL); //一般都爲NULL

第一個參數是創建該窗口所使用的窗口類的名稱,注意這個名稱應與前面所註冊的窗口類的名稱一致。
    
第三個參數爲創建的窗口的風格,下表列出了常用的窗口風格:  

風格

含義

WS_OVERLAPPEDWINDOW

創建一個層疊式窗口,有邊框、標題欄、系統菜單、最大最小化按鈕,是以下幾種風格的集合:WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX, WS_MAXIMIZEBOX

WS_POPUPWINDOW

創建一個彈出式窗口,是以下幾種風格的集合: WS_BORDER, WS_POPUP, WS_SYSMENU。必須再加上WS_CAPTION與才能使窗口菜單可見。

WS_OVERLAPPED & WS_TILED

創建一個層疊式窗口,它有標題欄和邊框。

WS_POPUP

該窗口爲彈出式窗口,不能與WS_CHILD同時使用。

WS_BORDER

窗口有單線邊框。

WS_CAPTION

窗口有標題欄。

WS_CHILD

該窗口爲子窗口,不能與WS_POPUP同時使用。

WS_DISABLED

該窗口爲無效,即對用戶操作不產生任何反應。

WS_HSCROLL / WS_VSCROLL

窗口有水平滾動條 / 垂直滾動條。

WS_MAXIMIZE / WS_MINIMIZE

窗口初始化爲最大化 / 最小化。

WS_MAXIMIZEBOX / WS_MINIMIZEBOX

窗口有最大化按鈕 / 最小化按鈕

WS_SIZEBOX & WS_THICKFRAME

邊框可進行大小控制的窗口

WS_SYSMENU

創建一個有系統菜單的窗口,必須與WS_CAPTION風格同時使用

WS_TILED

創建一個層疊式窗口,有標題欄

WS_VISIBLE

窗口爲可見

如果窗口創建成功,CreateWindow( )返回新窗口的句柄,否則返回NULL

4.顯示和更新窗口

    窗口創建後,並不會在屏幕上顯示出來,要真正把窗口顯示在屏幕上,還得使用ShowWindow( )函數,其原型如下:
    BOOL ShowWindow( HWND hWnd, int nCmdShow );
      參數hWnd就是要顯示的窗口的句柄。
           nCmdShow是窗口的顯示方式,一般給它WinMain()函數得到的nCmdShow的值就可以了。常用的窗口顯示方式有:

方式

含義

SW_HIDE

隱藏窗口

SW_MINIMIZE

最小化窗口

SW_RESTORE

恢復並激活窗口

SW_SHOW

顯示並激活窗口

SW_SHOWMAXIMIZED

最大化並激活窗口

SW_SHOWMINIMIZED

最小化並激活窗口

ShowWindow( )函數的執行優先級不高,當系統正忙着執行其它的任務時窗口不會立即顯示出來。所以我們使用ShowWindow( )函數後還要再調用UpdateWindow(HWND hWnd); 函數以保證立即顯示窗口。 

5.消息循環

 WinMain()函數中,調用InitWindow( )函數成功地創建了應用程序主窗口之後,就要啓動消息循環,其代碼如下:
 
for(;;)
 
{
     
if(PeekMessage(&msg, NULL, 0, 0,PM_REMOVE))  
     
{
         
if ( msg.message==WM_QUIT) break;
         
TranslateMessage(&msg);
         
DispatchMessage(&msg);
 
     }
 
}

Windows應用程序可以接收各種形式的信息,這包括鍵盤和鼠標的動作、記時器消息,其它應用程序發來的消息等等。Windows系統會自動將這些消息放入應用程序的消息隊列中。

PeekMessage( )函數就是用來從應用程序的消息隊列中按照先進先出的原則將這些消息一個個的取出來,放進一個MSG結構中去。如果隊列中沒有任何消息,PeekMessage()函數將立即返回。如果隊列中有消息,它將取出一個後返回。

MSG結構包含了一條Windows消息的完整信息,它由下面的幾部分組成:
      
HWND hwnd; //接收消息的窗口句柄
     
UINT message; //主消息值
     
WPARAM wParam; //副消息值1,其具體含義依賴於主消息值
     
LPARAM lParam; //副消息值2,其具體含義依賴於主消息值
     
DWORD time; //消息被投遞到消息隊列的時間
     
POINT pt; //鼠標的位置

該結構中的主消息表明了消息的類型,例如是鍵盤消息還是鼠標消息等。副消息的含義則依賴於主消息值,比如說如果主消息是鍵盤消息,那麼wParam中存儲了是鍵盤的哪個具體鍵;如果主消息是鼠標消息,那麼LOWORD(lParam)HIWORD(lParam)分別爲鼠標位置的xy座標;如果主消息是WM_ACTIVATEwParam就表示了程序是否處於激活狀態。這裏順便說一下,定義一個POINT類型的變量curpos後,在程序的任意位置使用GetCursorPos(&curpos)都可以將鼠標座標存儲在curpos的成員xy中。

PeekMessage( )函數的原型如下:
 
BOOL PeekMessage (
          
LPMSG lpMsg, //指向一個MSG結構的指針,用來保存消息
     
HWND hWnd, //指定哪個窗口的消息將被獲取
     
UINT wMsgFilterMin, //指定獲取的主消息值的最小值
     
UINT wMsgFilterMax, //指定獲取的主消息值的最大值
     
UINTwRemoveMsg //得到消息後是否移除消息
 
);

PeekMessage( )的第一個參數的意義上面已解釋。

 第二個參數是用來指定從哪個窗口的消息隊列中獲取消息,其它窗口的消息將被過濾掉。如果該參數爲NULL,則PeekMessage( )從該應用程序所有窗口的消息隊列中獲取消息。
     
第三個和第四個參數是用來過濾MSG結構中主消息值的,主消息值在wMsgFilterMinwMsgFilterMax之外的消息將被過濾掉。如果這兩個參數均爲0,表示接收所有消息。
     
第五個參數用來設置分發完消息後是否將消息從隊列中移除,一般設爲PM_REMOVE即移除。
 
TranslateMessage( )函數的作用是把虛擬鍵消息轉換到字符消息,以滿足鍵盤輸入的需要。DispatchMessage( )函數所完成的工作是把當前的消息發送到對應的窗口過程中去。
     
開啓消息循環其實很簡單,幾乎所有的程序都是按照這個方法。我們完全不必去深究這些函數的作用,只是照抄即可。
     
另外,這裏介紹的消息循環開啓方法比用GetMessage( )的方法要好一些,因爲GetMessage()如果得不到消息會一直等待,結果就耗費了許多寶貴的時間,使程序不能及時刷新。 

初學者如果覺得這一些東西理解起來困難,可以先放着,大概理解一下,安裝嚮導或者照抄下來就好,先學後面一些控件的控制、函數的用法,樹立信心和興趣之後再將它完全理解了
嚮導相關具體參考<windows編程——編譯與嚮導安裝>

————2014年1月23日16:00:32

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