Windows程序的基本結構(轉)

一、概述
  Windows程序具有相對固定的結構,對編寫者而言,不需要書寫整個過程,大部分過程由系統完成。
  程序中只要按一定的格式填寫系統留給客戶的那一小部分。
  所需要完成的有:
  窗口類的定義、窗口的建立、消息函數的書寫、消息循環。
二、消息處理函數
  Windows程序是事件驅動的,對於一個窗口,它的大部分例行維護是由系統維護的。沒個窗口都有一個消息處理函數。
  在消息處理函數中,對傳入的消息進行處理。系統內還有它自己的缺省消息處理函數。
  客戶寫一個消息處理函數,在窗口建立前,將消息處理函數與窗口關聯。這樣,每當有消息產生時,就會去調用這個消息處理函數。
  通常情況下,客戶都不會處理全部的消息,而是隻處理自己感興趣的消息,其他的,則送回到系統的缺省消息處理函數中去。
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case ...
...
case ...
...
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
三、窗口的建立
  客戶需要自己建立窗口,建立後會得到系統返回的窗口句柄(HWND),後繼的針對窗口的操作都針對句柄進行。
1.註冊窗口類
  建立窗口前,需要制定好這個窗口的相關屬性,最主要的就是將自己定義的消息處理函數與窗口關聯,其他的屬性還包括:菜單、圖標等等。
  這個屬性指定步驟是通過指定"窗口類"來完成的。
  對於自己建立的窗口,這個"窗口類"需要自己制定,也即自己填充一個WNDCLASS結構,然後向系統註冊。
對於一些特殊窗口,如按鈕等控件,他們的行爲是系統制定好了的,所以不需要自己註冊,直接使用對應的“窗口類”名稱就行了。
2.建立窗口
  建立窗口時,註冊的"窗口類"名稱作爲參數傳入。
  這樣,當有針對該窗口的消息時,將調用“窗口類”中指定的消息處理函數,在其中得到處理。
四、消息循環
  系統會將針對這個程序的消息依次放到程序的“消息隊列”中,由程序自己依次取出消息,在分發到對應的窗口中去。
  因此,建立窗口後,將進入一個循環。
  在循環中,取出消息、派發消息,循環往復,直到取得的消息是退出消息。
  循環退出後,程序即結束。
#include "stdafx.h"
#include <windows.h>
//一、消息處理函數
//參數:窗口句柄,消息,消息參數,消息參數
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) ;
}
//二、應用程序主函數
//參數:實例句柄、前一個實例的句柄、命令行參數、窗口顯示方式
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
 //1.註冊窗口類
 static TCHAR szAppName[] = TEXT ("HelloWin") ; //窗口類名稱
 //定製"窗口類"結構
 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 (WHITE_BRUSH); //畫刷
 wndclass.lpszMenuName = NULL ;
 wndclass.lpszClassName = szAppName; //類名稱
 //註冊
 if (!ReGISterClass (&wndclass))
 {
  MessageBox (NULL, TEXT ("RegisterClass Fail!"),
  szAppName, MB_ICONERROR) ;
  return 0 ;
 }
 //建立窗口
 HWND hwnd ;
 hwnd = CreateWindow (szAppName, //窗口類名稱
  TEXT ("The Hello Program"), //窗口標題
  WS_OVERLAPPEDWINDOW, //窗口風格
  CW_USEDEFAULT,
  CW_USEDEFAULT,
  CW_USEDEFAULT,
  CW_USEDEFAULT,
  NULL,
  NULL,
  hInstance, //實例句柄
  NULL);
 ShowWindow (hwnd, iCmdShow) ;
 UpdateWindow (hwnd) ;
 
 //消息循環
 MSG msg ;
 while (GetMessage (&msg, NULL, 0, 0)) //從消息隊列中取消息
 {
  TranslateMessage (&msg) ; //轉換消息
  DispatchMessage (&msg) ; //派發消息
 }
 return msg.wParam ;
 

在EasyWin.cpp文件中輸入以下源程序代碼。  
 
//*******************************************************************
// 工程:easywin
// 文件:easywin.cpp
// 內容:一個基本的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應用程序入口函數。創建主窗口,處理消息循環
//*******************************************************************
int PASCAL WinMain( HINSTANCE hInstance, //當前實例句柄
HINSTANCE hPrevInstance, //前一個實例句柄
LPSTR lpCmdLine, //命令行字符
int nCmdShow) //窗口顯示方式
{
MSG msg;
//創建主窗口
if ( !InitWindow( hInstance, nCmdShow ) )
return FALSE;
//進入消息循環:
//從該應用程序的消息隊列中檢取消息,送到消息處理過程,
//當檢取到WM_QUIT消息時,退出消息循環。
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//程序結束
return msg.wParam;
}
//******************************************************************
//函數:InitWindow()
//功能:創建窗口。
//******************************************************************
static BOOL InitWindow( HINSTANCE hInstance, int nCmdShow )
{
HWND hwnd; //窗口句柄
WNDCLASS wc; //窗口類結構
//填充窗口類結構
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 );
 
//創建主窗口
hwnd = CreateWindow(
"EasyWin", //窗口類名稱
"一個基本的Win32程序", //窗口標題
WS_OVERLAPPEDWINDOW, //窗口風格,定義爲普通型
100, //窗口位置的x座標
100, //窗口位置的y座標
400, //窗口的寬度
300, //窗口的高度
NULL, //父窗口句柄
NULL, //菜單句柄
hInstance, //應用程序實例句柄
NULL ); //窗口創建數據指針
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://窗口重畫消息
{
char hello[]="你好,我是EasyWin !";
HDC hdc;
PAINTSTRUCT ps;
hdc=BeginPaint( hWnd,&ps ); //取得設備環境句柄
SetTextColor(hdc, RGB(0,0,255)); //設置文字顏色
TextOut( hdc, 20, 10, hello, strlen(hello) );//輸出文字
EndPaint( hWnd, &ps ); //釋放資源
break;
}
case WM_DESTROY://退出消息
PostQuitMessage( 0 );//調用退出函數
break;
}
//調用缺省消息處理過程
return DefWindowProc(hWnd, message, wParam, lParam);
}
  程序輸入完畢,即可編譯執行。在窗口中擊鼠標鍵或按ESC鍵時,會彈出一個對話框以表示你的操作。
  其實,這個程序可以看成是所有Win32應用程序的框架,在以後所有的程序中,你會發現它們都是在這個程序的基礎之上再添加代碼。
 

在EasyWin.cpp文件中輸入以下源程序代碼。  
 
//*******************************************************************
// 工程:easywin
// 文件:easywin.cpp
// 內容:一個基本的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應用程序入口函數。創建主窗口,處理消息循環
//*******************************************************************
int PASCAL WinMain( HINSTANCE hInstance, //當前實例句柄
HINSTANCE hPrevInstance, //前一個實例句柄
LPSTR lpCmdLine, //命令行字符
int nCmdShow) //窗口顯示方式
{
MSG msg;
//創建主窗口
if ( !InitWindow( hInstance, nCmdShow ) )
return FALSE;
//進入消息循環:
//從該應用程序的消息隊列中檢取消息,送到消息處理過程,
//當檢取到WM_QUIT消息時,退出消息循環。
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//程序結束
return msg.wParam;
}
//******************************************************************
//函數:InitWindow()
//功能:創建窗口。
//******************************************************************
static BOOL InitWindow( HINSTANCE hInstance, int nCmdShow )
{
HWND hwnd; //窗口句柄
WNDCLASS wc; //窗口類結構
//填充窗口類結構
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 );
 
//創建主窗口
hwnd = CreateWindow(
"EasyWin", //窗口類名稱
"一個基本的Win32程序", //窗口標題
WS_OVERLAPPEDWINDOW, //窗口風格,定義爲普通型
100, //窗口位置的x座標
100, //窗口位置的y座標
400, //窗口的寬度
300, //窗口的高度
NULL, //父窗口句柄
NULL, //菜單句柄
hInstance, //應用程序實例句柄
NULL ); //窗口創建數據指針
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://窗口重畫消息
{
char hello[]="你好,我是EasyWin !";
HDC hdc;
PAINTSTRUCT ps;
hdc=BeginPaint( hWnd,&ps ); //取得設備環境句柄
SetTextColor(hdc, RGB(0,0,255)); //設置文字顏色
TextOut( hdc, 20, 10, hello, strlen(hello) );//輸出文字
EndPaint( hWnd, &ps ); //釋放資源
break;
}
case WM_DESTROY://退出消息
PostQuitMessage( 0 );//調用退出函數
break;
}
//調用缺省消息處理過程
return DefWindowProc(hWnd, message, wParam, lParam);
}
  程序輸入完畢,即可編譯執行。在窗口中擊鼠標鍵或按ESC鍵時,會彈出一個對話框以表示你的操作。
  其實,這個程序可以看成是所有Win32應用程序的框架,在以後所有的程序中,你會發現它們都是在這個程序的基礎之上再添加代碼。

註冊窗口類
  一個應用程序可以有許多窗口,但只有一個是主窗口,它是與該應用程序的實例句柄唯一關聯的。上面的例程中,創建主窗口的函數是InitWindow()。
  通常要對填充一個窗口類結構WNDCLASS,然後調用ReGISterClass()對該窗口類進行註冊。每個窗口都有一些基本的屬性,如窗口邊框、窗口標題欄文字、窗口大小和位置、鼠標、背景色、處理窗口消息函數的名稱等等。註冊的過程也就是將這些屬性告訴系統,然後再調用CreateWindow()函數創建出窗口。這也就象你去裁縫店訂做一件衣服,先要告訴店老闆你的身材尺寸、布料顏色、以及你想要的款式,然後他才能爲你做出一件讓你滿意的衣服。
  在VC的幫助中,可以看到WNDCLASS結構是這樣定義的:
typedef struct _WNDCLASS {
UINT style; //窗口的風格*
WNDPROC lpfnWndProc; //指定窗口的消息處理函數的遠指針*
int cbClsExtra; //指定分配給窗口類結構之後的額外字節數*
int cbWndExtra; //指定分配給窗口實例之後的額外字節數
HANDLE hInstance; //指定窗口過程所對應的實例句柄*
HICON hIcon; //指定窗口的圖標
HCURSOR hCursor; //指定窗口的鼠標
HBRUSH hbrBackground; //指定窗口的背景畫刷
LPCTSTR lpszMenuName; //窗口的菜單資源名稱
LPCTSTR lpszClassName; //該窗口類的名稱*
} WNDCLASS;
  在Win95和WinNT的具有新界面特性的系統中,爲了支持新的窗口界面特性,還有一種擴展的窗口類型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這兩個結構基本上是一致的,只是WNDCLASSEX結構中多了cbSize和hIconSm這兩個成員。WNDCLASS結構的各成員中,其註釋後打了星號的表示該項應特別注意。
  WNDCLASS結構的第一個成員style表示窗口類的風格,它往往是由一些基本的風格通過位的“或”操作(操作符位“|”)組合而成。下表列出了一些常用的基本窗口風格:
風格 含義 
CS_HREDRAW 如果窗口客戶區寬度發生改變,重繪整個窗口 
CS_VREDRAW 如果窗口客戶區高度發生改變,重繪整個窗口
CS_DBLCLKS 能感受用戶在窗口中的雙擊消息 
CS_NOCLOSE 禁用系統菜單中的“關閉”命令 
CS_OWNDC 爲該窗口類的各窗口分配各自獨立的設備環境 
CS_CLASSDC 爲該窗口類的各窗口分配一個共享的設備環境 
CS_PARENTDC 指定子窗口繼承其父窗口的設備環境
CS_SAVEBITS 把被窗口遮掩的屏幕圖象部分作爲位圖保存起來。當該窗口被移動時,Windows使用被保存的位圖來重建屏幕圖象 
  在EasyWin應用程序中,是按如下方式對WNDCLASS結構進行填充和註冊的:
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,表示只要窗口的高度或寬度發生變化,都會重畫整個窗口。
  第二個成員lpfnWndProc的值爲(WNDPROC)WinProc。表明該窗口類的消息處理函數是WinProc()函數。這裏,要指定窗口的消息處理函數的遠指針,輸入消息處理函數的函數名稱即可,必要時應該進行強制類型轉換,將其轉換成WNDPROC型。
  接下來的cbClsExtra和wc.cbWndExtra在大多數情況下都會設爲0。
  然後的hInstance成員,給它的值是由WinMain()傳來的應用程序的實例句柄,表明該窗口與該實例是相關聯的。事實上,只要是註冊窗口類,該成員的值始終是該程序的實例句柄,你應該象背書一樣記住它。
  下面的hIcon,是讓你給這個窗口指定一個圖標,調用 LoadIcon( hInstance, IDI_APPLICATION ),可以調用系統內部預先定義好的標誌符爲IDC_APPLICATION的圖標作爲該窗口的圖標。
  同樣,調用LoadCursor( NULL, IDC_ARROW )爲該窗口調用系統內部預先定義好的箭頭型鼠標。
  hbrBackground成員用來定義窗口的背景畫刷顏色,也就是該窗口的背景色。調用GetStockObject(WHITE_BRUSH)可以獲得系統內部預先定義好的白色畫刷作爲窗口的背景色。
  上面的LoadIcon()、LoadCursor()、GetStockObject()都是Windows的API函數,它們的用法可以參看VC的幫助,這裏就不多介紹了。
  lpszMenuName成員的值我們給它NULL,表示該窗口將沒有菜單。如果你想讓你的窗口擁有菜單,就把lpszMenuName成員賦值爲標誌菜單資源的字符串。
  WNDCLASS結構的最後一個成員lpszClassName是讓你給這個窗口類起一個唯一的名稱,因爲Windows操作系統中有許許多多的窗口類,必須用一個獨一無二的名稱來代表它們。通常,你可以用你的程序名來命名這個窗口類的名稱。這個名稱將在創建窗口的CreateWindow()函數中用到。
  填充完畢後,對於WNDCLASS結構,調用RegisterClass()函數進行註冊;對於WNDCLASSEX結構,調用RegisterClassEx()函數進行註冊,它們的原型分別如下:
ATOM RegisterClass( CONST WNDCLASS *lpWndClass );
ATOM RegisterClassEx( CONST WNDCLASSEX *lpwcx ); 
  該函數如調用成功,則返回一個非0值,表明系統中已經註冊了一個名爲EasyWin的窗口類。如果失敗,則返回0。
 
創建窗口
  當窗口類註冊完畢之後,並不會有窗口顯示出來,因爲註冊的過程僅僅是爲創建窗口所做的準備工作。實際創建一個窗口的是通過調用CreateWindow()函數完成的。窗口類中已經預先定義了窗口的一般屬性,而CreateWindow()中的參數可以進一步指定一個窗口的更具體的屬性,在EasyWin程序中,是如下調用CreateWindow()函數來創建窗口的:
hwnd = CreateWindow(
"EasyWin", //創建窗口所用的窗口類的名稱*
"一個基本的Win32程序", //窗口標題
WS_OVERLAPPEDWINDOW, //窗口風格,定義爲普通型*
100, //窗口位置的x座標
100, //窗口位置的y座標
400, //窗口的寬度
300, //窗口的高度
NULL, //父窗口句柄
NULL, //菜單句柄
hInstance, //應用程序實例句柄*
NULL ); //一般都爲NULL
  CreateWindow()函數的參數的含義在上面的註釋中已有介紹,註釋後打了星號標記的參數應該着重注意,其它的參數都很簡單,不多做介紹,可參看VC的幫助。
  第一個參數是創建該窗口所使用的窗口類的名稱,注意這個名稱應與前面所註冊的窗口類的名稱一致。
  第三個參數爲創建的窗口的風格,下表列出了常用的窗口風格:
風格 含義 
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_POPUPWINDOW風格必須一起使用才能使窗口菜單可見 
WS_OVERLAPPED 創建一個層疊式窗口,它有標題欄和邊框,與WS_TILED風格一樣 
WS_POPUP 該窗口爲彈出式窗口,不能與WS_CHILD同時使用 
WS_BORDER 窗口有單線邊框 
WS_CAPTION 窗口有標題欄 
WS_CHILD 該窗口爲子窗口,不能與WS_POPUP同時使用 
WS_DISABLED  該窗口爲無效,即對用戶操作不產生任何反應
WS_HSCROLL 窗口有水平滾動條 
WS_ICONIC 窗口初始化爲最小化 
WS_MAXIMIZE  窗口初始化爲最大化 
WS_MAXIMIZEBOX  窗口有最大化按鈕 
WS_MINIMIZE 與WS_MAXIMIZE一樣 
WS_MINIMIZEBOX 窗口有最小化按鈕 
WS_SIZEBOX 邊框可進行大小控制的窗口 
WS_SYSMENU 創建一個有系統菜單的窗口,必須與WS_CAPTION風格同時使用 
WS_THICKFRAME 創建一個大小可控制的窗口,與WS_SIZEBOX 風格一樣.
WS_TILED 創建一個層疊式窗口,有標題欄 
WS_VISIBLE 窗口爲可見 
WS_VSCROLL 窗口有垂直滾動條 

  程序中使用了WS_OVERLAPPEDWINDOW標誌,它是創建一個普通窗口常用的標誌。而在DirectX編程中,我們常用的是WS_POPUP,用這個標誌創建的窗口沒有標題欄和系統菜單,如果設定窗口爲最大化,客戶區可以佔滿整個屏幕,以滿足DirectX編程的需要。
  CreateWindow()函數後面的參數中,仍用到了該應用程序的實例句柄hInstance。
  如果窗口創建成功,返回值是新窗口的句柄,否則返回NULL。
 
顯示和更新窗口
  窗口創建後,並不會在屏幕上顯示出來,要真正把窗口顯示在屏幕上,還得使用ShowWindow()函數,其原型如下:
BOOL ShowWindow( HWND hWnd, int nCmdShow ); 
  參數hWnd指定要顯示得窗口的句柄,nCmdShow表示窗口的顯示方式,這裏指定爲從WinMain()函數的nCmdShow所傳遞而來的值。
  由於ShowWindow()函數的執行優先級不高,所以當系統正忙着執行其它的任務時,窗口不會立即顯示出來,此時,調用UpdateWindow()函數以可以立即顯示窗口。其函數原型如下:
BOOL UpdateWindow( HWND hWnd ); 
  消息循環
  在Win32編程中,消息循環是相當重要的一個概念,看似很難,但是使用起來卻是非常簡單。在WinMain()函數中,調用InitWindow()函數成功的創建了應用程序主窗口之後,就要啓動消息循環,其代碼如下:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
 
  Windows應用程序可以接收以各種形式輸入的信息,這包括鍵盤、鼠標動作 、記時器產生的消息,也可以是其它應用程序發來的消息等等。Windows系統自動監控所有的輸入設備,並將其消息放入該應用程序的消息隊列中。
  GetMessage()函數則是用來從應用程序的消息隊列中按照先進先出的原則將這些消息一個個的取出來,放進一個MSG結構中去。GetMessage()函數原型如下:
BOOL GetMessage(
LPMSG lpMsg, //指向一個MSG結構的指針,用來保存消息
HWND hWnd, //指定哪個窗口的消息將被獲取
UINT wMsgFilterMin, //指定獲取的主消息值的最小值
UINT wMsgFilterMax //指定獲取的主消息值的最大值
);
 
  GetMessage()將獲取的消息複製到一個MSG結構中。如果隊列中沒有任何消息,GetMessage()函數將一直空閒直到隊列中又有消息時再返回。如果隊列中已有消息,它將取出一個後返回。MSG結構包含了一條Windows消息的完整信息,其定義如下:
typedef struct tagMSG {
HWND hwnd; //接收消息的窗口句柄
UINT message; //主消息值
WPARAM wParam; //副消息值,其具體含義依賴於主消息值
LPARAM lParam; //副消息值,其具體含義依賴於主消息值
DWORD time; //消息被投遞的時間
POINT pt; //鼠標的位置
} MSG;
  該結構中的主消息表明了消息的類型,例如是鍵盤消息還是鼠標消息等,副消息的含義則依賴於主消息值,例如:如果主消息是鍵盤消息,那麼副消息中則存儲了是鍵盤的哪個具體鍵的信息。
  GetMessage()函數還可以過濾消息,它的第二個參數是用來指定從哪個窗口的消息隊列中獲取消息,其它窗口的消息將被過濾掉。如果該參數爲NULL,則GetMessage()從該應用程序線程的所有窗口的消息隊列中獲取消息。
  第三個和第四個參數是用來過濾MSG結構中主消息值的,主消息值在wMsgFilterMin和wMsgFilterMax之外的消息將被過濾掉。如果這兩個參數爲0,則表示接收所有消息。
  當且僅當GetMessage()函數在獲取到WM_QUIT消息後,將返回0值,於是程序退出消息循環。
  TranslateMessage()函數的作用是把虛擬鍵消息轉換到字符消息,以滿足鍵盤輸入的需要。DispatchMessage()函數所完成的工作是把當前的消息發送到對應的窗口過程中去。
  開啓消息循環其實是很簡單的一個步驟,幾乎所有的程序都是按照EasyWin的這個方法。你完全不必去深究這些函數的作用,只是簡單的照抄就可以了。

消息處理函數
  消息處理函數又叫窗口過程,在這個函數中,不同的消息將用switch語句分配到不同的處理程序中去。Windows的消息處理函數都有一個確定的樣式,即這種函數的參數個數和類型以及其返回值的類型都有明確的規定。在VC的說明書中,消息處理函數的原型是這樣定義的:
LRESULT CALLBACK WindowProc(
HWND hwnd, //接收消息窗口的句柄
UINT uMsg, //主消息值
WPARAM wParam, //副消息值
LPARAM lParam //副消息值
); 
  如果你的程序中還有其它的消息處理函數,也都必須按照上面的這個樣式來定義,但函數名稱可以隨便取。EasyWin中的WinProc()函數就是這樣一個典型的消息處理函數。
  消息處理函數的四個參數是由GetMessage()函數從消息隊列中獲得MSG結構,然後分解後得到的。第二個參數uMsg和MSG結構中的message值是一致的,代表了主消息值。程序中用switch語句來將不同類型的消息分配到不同的處理程序中去。
  WinProc()函數明確的處理了4個消息,分別是WM_KEYDOWN(擊鍵消息)、WM_RBUTTONDOWN(鼠標右鍵按下消息)、WM_PAINT(窗口重畫消息)、WM_DESTROY(銷燬窗口消息)。
  值得注意的是,應用程序發送到窗口的消息遠遠不止以上這幾條,象WM_SIZE、WM_MINIMIZE、WM_CREATE、WM_MOVE等這樣頻頻使用的消息就有幾十條。爲了減輕編程的負擔,Windows的API提供了DefWindowProc()函數來處理這些最常用的消息,調用了這個函數後,這些消息將按照系統默認的方式得到處理。
  因此,在switch_case語句中,只須明確的處理那些有必要進行特別響應的消息,把其餘的消息交給DefWindowProc()函數來處理,是一種明智的選擇,也是你必須做的一件事。
 
  結束消息循環
  當用戶按Alt+F4或單擊窗口右上角的退出按鈕,系統就嚮應用程序發送一條WM_DESTROY的消息。在處理此消息時,調用了PostQuitMessage()函數,該函數會給窗口的消息隊列中發送一條WM_QUIT的消息。在消息循環中,GetMessage()函數一旦檢索到這條消息,就會返回FALSE,從而結束消息循環,隨後,程序也結束。
  小結
  本章介紹的是Win32編程的基礎知識,在進行DirectX編程之前,掌握它們是十分必要的。
  通過本文的學習,你應該學到以下知識:
   如何創建一個Win32應用程序工程
   用ReGISterClass()函數註冊一個窗口類,再立即調用CreateWindow()函數創建一個窗口的實例
   設置窗口的類型以及將一個消息處理函數與窗口聯繫上
   用一固定的模式開啓消息循環
   瞭解消息處理函數的定義規則,如何自己定義一個窗口消息處理函數
   在消息處理函數中,最後必須調用DefWindowProc()函數以處理那些缺省的消息
   調用PostQuitMessage()函數以結束消息循環
發佈了70 篇原創文章 · 獲贊 6 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章