Windows應用程序

 

Windows應用程序是由一系列的消息處理

代碼來實現的。這和傳統的過程式編程方法很不一樣,編程者只能夠

預測用戶所利用應用程序用戶界面對象所進行的操作以及爲這些操作

編寫處理代碼,卻不可以這些操作在什麼時候發生或者是以什麼順序

來發生,也就是說,我們不可能知道什麼消息會在什麼時候以什麼順

序來臨。

Windows應用程序基本流程:

 

Windows API:(windowsAppliacation Programming Interface. Windows 應用程序編程接口),

API就是一系列的例程,應用程序通過調用這些例程來請求操作系統完成一些低級服務。在Windows這樣的圖形用戶界面中,應用程序的窗口、圖標、菜單和對話框等就是由API來管理和維護的。

 

Windows應用程序中,main()函數被、WinMain()函數取代,WinMain()函數原型如下:

Int WINAPIWinMain( HINSTANCE hInstance //當前實例句柄

                  HINSTANCE hPrevInstance //前一實例句柄

                  LPSTR lpCmdLine //指向命令行參數的指針

                  Int  nCmdShow)//窗口的顯示狀態

“句柄”(handle),所謂的句柄是一個標識

對象的變量,或者是一個對操作系統資源的間接引用。

WinMain()函數中一般需要完成以下操作:

1 註冊窗口類

2 創建應用程序主窗口

3 進入應用程序消息循環

 

在註冊窗口類前,我們先創建一個類型爲

WNDCLASS的結構,然後在該結構對象中填入窗口類的信息,最後將它

傳遞給函數RegisterClass,整個過程如下面的代碼所示:

WNDCLASS wc;

// 填充窗口類信息

wc.style=CS_HREDRAW|CS_VREDRAW;//

wc.lpfnWndProc=WndProc;

wc.cbClsExtra=0;

wc.cbWndExtra=0;

wc.hInstance=hInstance;

wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);

wc.hCursor=LoadCursor(NULL,IDC_ARROW);

wc.hbrBackground=GetStockObject(WHITE_BRUSH);

wc.lpszMenuName=NULL;

wc.lpszClassName="SdkDemo1";

// 註冊窗口類

RegisterClass(&wc);

在使用RegisterClass註冊窗口類成功之後,即可以使用該窗口類創

建並顯示應用程序的窗口。這個過程如下面的代碼所示:

// 創建應用程序主窗口

hWnd=CreateWindow("SdkDemo1", // 窗口類名

"第一個Win32 SDK應用程序",// 窗口標題

WS_OVERLAPPEDWINDOW,// 窗口樣式

CW_USEDEFAULT,// 初始化 x 座標

CW_USEDEFAULT,// 初始化 y 座標

CW_USEDEFAULT,// 初始化窗口寬度

CW_USEDEFAULT,// 初始化窗口高度

NULL, // 父窗口句柄

NULL, // 窗口菜單句柄

hInstance,// 程序實例句柄

NULL); // 創建參數

// 顯示窗口

ShowWindow(hWnd,SW_SHOW);

// 更新主窗口客戶區

UpdateWindow(hWnd);

創建窗口完成之後,ShowWindows顯示該窗口,第二個參數SW_SHOW表

示在當前位置以當前大小激活並顯示由第一個參數標識的窗口。然

後,函數UpdateWindows向窗口發送一條WM_PAINT消息,以通知窗口

更新其客戶區。

////////////////////////////////////////////////////////////////////////////////////////////

CreateWindow()原型:

HWNDCreateWindow(LPCTSTR lpClassName, // 指向已註冊的類名

LPCTSTR lpWindowName,// 指向窗口名稱

DWORDdwStyle, // 窗口樣式

int x, // 窗口的水平位置

int y, // 窗口的垂直位置

int nWidth,// 窗口寬度

int nHeight,// 窗口高度

HWNDhWndParent, // 父窗口或所有者窗口句柄

HMENU hMenu,// 菜單句柄或子窗口標識符

HANDLEhInstance, // 應用程序實例句柄

LPVOIDlpParam, // 指向窗口創建數據的指針

);

////////////////////////////////////////////////////////////////////////////////////////////

 

在完成上面的步驟之後,進入應用程序的主消息循環。一般情況下,

主消息循環具有下面的格式:

while(GetMessage(&msg,NULL,0,0)) {

TranslateMessage(&msg);

DispatchMessage(&msg);

}

下面我們來看一下程序主窗口的窗口過程WndProc。窗口過程名是可

以由用戶自行定義,然後在註冊窗口類時在WNDCLASS結構中指定。但

是,一般來說,程序都把窗口過程命令爲WndProc來類似的名稱,如

MainWndProc等,並不是一定要這樣做,但是這樣明顯的有利於閱

讀,因此也是我們推薦的做法。窗口過程具有如下的原型:

LRESULTWINAPI WndProc(HWND,UINT,WPARAM,LPARAM);

LRESULTCALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

對於編譯器而言,兩種書寫形式都是一樣的,它們都等價於 long __stdcall WndProc(void *,unsigned int,unsigned int,long)

窗口過程使用了四個參數,在它被調用時(再強調一點,一般情況

下,窗口過程是由操作系統調用,而不是由應用程序調用的,這就是

我們爲什麼將它們稱爲回調函數的道理),這四個參數對應於所發送

消息結構的前四個成員。下面給出了一個窗口過程的例子:

// WndProc 主窗口過程

LRESULTWINAPI WndProc (HWND hWnd,

UINT msg,

WPARAMwParam,

LPARAMlParam)

{

HDC hdc;

RECT rc;

HPENhPen,hPenOld;

HBRUSHhBrush,hBrushOld;

switch (msg)

{

caseWM_PAINT:

hdc=GetDC(hWnd);

GetClientRect(hWnd,&rc);

hPen=CreatePen(PS_SOLID,0,RGB(0,0,0));

hBrush=CreateHatchBrush(HS_DIAGCROSS,RGB(0,0,0));

hPenOld=SelectObject(hdc,hPen);

hBrushOld=SelectObject(hdc,hBrush);

Ellipse(hdc,rc.left,rc.top,rc.right,rc.bottom);

SelectObject(hdc,hPenOld);

SelectObject(hdc,hBrushOld);ReleaseDC(hWnd,hdc);

break;

caseWM_DESTROY:

PostQuitMessage(0);

break;

default:

break;

}

returnDefWindowProc(hWnd,msg,wParam,lParam);

}

在該窗口過程中,我們處理了最基本兩條消息。

第一條消息是WM_PAINT,當窗口客戶區的全部或一部分需要重繪時,

系統向該窗口發送該消息。在前面的過程中我們已經提到過,在使用

ShowWindow函數顯示窗口之後,通常隨即調用函數UpdateWindow,該

函數直接向窗口過程發送一個WM_PAINT消息,以通知窗口繪製其客戶

區。在該消息的處理函數中,我們先使用GetDC獲得窗口的設備句

柄,關於設備句柄本書後面將要專門涉及,這裏我們只需知道它是用

來調用各種繪圖方法的。然後調用GetClientRect獲得當前窗口的客

戶區矩形。接着調用CreatePen創建一個黑色畫筆,調用

CreateHatchBrush創建一個45度交叉線的填充畫刷,並且

SelectObject函數將它們選入設備描述表中,原有的畫筆和畫刷被保

存到hPenOld和hBrushOld中,以便以後恢復。完成以上步驟之後,調

用Ellipse函數以當前客戶區大小繪製一個橢圓。最後,再一次調用

SelectObject函數恢復原有的畫筆和畫刷,並調用ReleaseDC釋放設

備描述表句柄。在這個消息的處理代碼中,我們涉及到了一些新的概

念、數據類型和API函數,然而本章並不着意於講述這些內容,讀者

也不必深究它們,這些代碼只是爲了完整該示例程序才使用的。對於

窗口來說,除了客戶區以外的其它內容將由系統進行重繪,這些內容

包括窗口標題條、邊框、菜單條、工具條以及其它控件,如果包含了

它們的話。這種重繪往往發生在覆蓋於窗口上方的其它窗口被移走,

或者是窗口被移動或改變大小時。因此,對於大多數窗口過程來說,

WM_PAINT消息是必須處理的。

另一個對於絕大多數窗口過程都必須處理的消息是WM_DESTROY,當窗口被撤消時(比如用戶從窗口的系統菜單中選擇了“關閉”,或者單

擊了右邊的小叉,對於這些事件,Windows的默認處理是調用

DestroyWindow函數撤銷相應的窗口),將會接收到該消息。由於本程

序僅在一個窗口,因此在這種情況下應該終止應用程序的執行,因此

我們調用了PostQuitMessage函數,該函數向線程的消息隊列中放入

一個WM_QUIT消息,傳遞給PostQuitMessage函數的參數將成爲

WM_QUIT消息的wParam參數,在上面的例子中,該值爲0。

對於其它情況,在上面的示例程序中我們沒有必要進行處理,

Windows專門爲此提供了一個默認的窗口過程,稱爲DefWindowProc,

我們只需要以WndProc的參數原封不動的調用默認窗口過程

DefWindowProc,並將其返回值作爲WndProc的返回值即可。

 

這個程序的完整代碼給出如下:

#include<windows.h>

// 函數原型

int WINAPIWinMain(HINSTANCE,HINSTANCE,LPSTR,int);

LRESULTWINAPI WndProc(HWND,UINT,WPARAM,LPARAM);

// WinMain 函數

int WINAPIWinMain (HINSTANCE hInstance,

HINSTANCEhPrevInstance,

LPSTRlpCmdLine,

intnCmdShow) HWND hWnd; // 主窗口句柄

MSG msg; // 窗口消息

WNDCLASS wc;// 窗口類

if(!hPrevInstance)

{

// 填充窗口類信息

wc.style=CS_HREDRAW|CS_VREDRAW;

wc.lpfnWndProc=WndProc;

wc.cbClsExtra=0;

wc.cbWndExtra=0;

wc.hInstance=hInstance;

wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);

wc.hCursor=LoadCursor(NULL,IDC_ARROW);

wc.hbrBackground=GetStockObject(WHITE_BRUSH);

wc.lpszMenuName=NULL;

wc.lpszClassName="SdkDemo1";

// 註冊窗口類

RegisterClass(&wc);

}

// 創建應用程序主窗口

hWnd=CreateWindow("SdkDemo1", // 窗口類名

"第一個Win32 SDK應用程序",// 窗口標題

WS_OVERLAPPEDWINDOW,// 窗口樣式

CW_USEDEFAULT,// 初始化 x 座標

CW_USEDEFAULT,// 初始化 y 座標

CW_USEDEFAULT,// 初始化窗口寬度 CW_USEDEFAULT, // 初始化窗口高度

NULL, // 父窗口句柄

NULL, // 窗口菜單句柄

hInstance,// 程序實例句柄

NULL); // 創建參數

// 顯示窗口

ShowWindow(hWnd,SW_SHOW);

// 更新主窗口客戶區

UpdateWindow(hWnd);

// 開始消息循環

while(GetMessage(&msg,NULL,0,0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

returnmsg.wParam;

}

// WndProc 主窗口過程

LRESULTWINAPI WndProc (HWND hWnd,

UINT msg,

WPARAMwParam,

LPARAMlParam)

{

HDC hdc;

RECT rc;

HPENhPen,hPenOld; HBRUSH hBrush,hBrushOld;

switch (msg)

{

caseWM_PAINT:

hdc=GetDC(hWnd);

GetClientRect(hWnd,&rc);

hPen=CreatePen(PS_SOLID,0,RGB(0,0,0));

hBrush=CreateHatchBrush(HS_DIAGCROSS,RGB(0,0,0));

hPenOld=SelectObject(hdc,hPen);

hBrushOld=SelectObject(hdc,hBrush);

Ellipse(hdc,rc.left,rc.top,rc.right,rc.bottom);

SelectObject(hdc,hPenOld);

SelectObject(hdc,hBrushOld);

ReleaseDC(hWnd,hdc);

break;

caseWM_DESTROY:

PostQuitMessage(0);

break;

default:

break;

}

returnDefWindowProc(hWnd,msg,wParam,lParam);

}

 

Windows中

所用的數

據類型

對應的基本數據

類型

說明

BOOL  int  布爾值

BSTR  unsigned short *  32位字符指針

BYTE  unsigned char 8位無符號整數

COLORREF  unsigned long 用作顏色值的32位值

DWORD  unsigned long 32位無符號整數,段地址和相關的偏移地

 

LONG  long 32位帶符號整數

LPARAM  long  作爲參數傳遞給窗口過程或回調函數的32

位值

LPCSTR  const char * 指向字符串常量的32位指針

LPSTR  char * 指向字符串的32位指針

LPCTSTR  const char  *

(注1)

指向可移植的Unicode和DBCS字符串常量

的32位指針

LPTSTR  char *(注1)  指向可移植爲Unicode和DBCS字符串的32

位指針

LPVOID  void * 指向未定義類型的32位指針

LRESULT  long  來自窗口過程或回調函數的32位返回值

UINT  unsigned int 32位無符號整數

WNDPROC  long

(__stdcall  *)

(void

*,unsigned

int,unsigned

int,long)(注2)

指向窗口過程的32位指針

WORD  unsigned short  16位無符號整數

WPARAM  unsigned int 當作參數傳遞給窗口過程或回調函數的32

位值

 

注1:  這是在DBCS版本下的情況,在Unicode版本下LPCTSTR和LPTSTR將代表

其它的數據類型。

注2:  事實上,WNDPROC被定義爲LRESULT  (CALLBACK*)(HWND,  UINT,

WPARAM,LPARAM),這個定義最終被編譯器解釋爲long (__stdcall *)(void

*,unsignedint,unsigned int,long)。

 

表3. 3 Windows公用句柄類型  (void*)

句柄類型  說明

HBITMAP  保存位圖信息的內存域的句柄

HBRUSH  畫刷句柄

HCTR  子窗口控件句柄

HCURSOR  鼠標光標句柄 HDC  設備描述表句柄

HDLG  對話框句柄

HFONT  字體句柄

HICON  圖標句柄

HINSTANCE  應用程序的實例句柄

HMENU  菜單句柄

HMODULE  模塊句柄

HPALETTE  顏色調色板句柄

HPEN  在設備上畫圖時用於指明線型

的筆的句柄

HRGN  剪貼區域句柄

HTASK  獨立於已執行任務的句柄

HWND  窗口句柄

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