第2個Wiindows程式講解

尹成發表於2014-09-14

    上次,我們一起寫了一個Windows視窗程式,這個視窗程式雖然非常簡單,但是,程式碼仍然很多,相信,一定會有很多初學者看見這些程式碼而感到頭疼。不用怕,現在,我們就一起來分析一下這些程式碼,相信通過我們共同的努力,一定可以克服這些難題。

     首先,我們要做的第一件事情就是包含windows.h標頭檔案,這個不再解釋了,因為,我們前面已經解釋過。

     之後,我們定義了一個WinMain函式和一個視窗處理函式MainWndProc。

     相信,很多人讀到視窗處理函式,就一定會發暈,不知道它是一個什麼東東,如果大家想要明白視窗處理函式,就必須先明白Windows下的訊息機制,現在,我先簡要介紹一下什麼是訊息機制,如果想要深入研究的人,可以看看《Windows核心程式設計》,windows的訊息機制是藉由訊息系統來完成的。

     Windows的訊息系統是由3個部分組成的:

     1)訊息佇列:Windows為所有的應用程式都維護一個訊息佇列,應用程式必須從訊息佇列中獲取訊息,然後分派給某個視窗處理函式處理。

     2)訊息泵:通過這個訊息泵,應用程式可以從訊息佇列中獲取訊息,然後,再把它分派給適當的視窗,最終由該視窗的視窗處理函式來處理它。

     3)視窗處理函式:每個視窗都有一個視窗處理函式,它的任務就是處理髮給該視窗的訊息,處理完訊息後,它通常要返回一個值給Windows,告訴Windows該訊息處理是否成功。

      相信通過上面的瞭解,大家大概對訊息機制會有一個整體的把握,下面,我將以一個鍵盤的按鍵訊息為例,告訴大家這個訊息是如何產生,流轉,處理。

      當我們按下鍵盤上的一個字元後,比如a,這個時候,鍵盤的硬體會檢測到這個動作,然後通知系統,系統知道後,會為它產生一個訊息:WM_CHAR,之後,系統會將這個訊息傳送到當前程式的訊息佇列,比如當前程式是記事本,那麼這個時候,記事本程式的訊息佇列中會出現一個WM_CHAR訊息,當記事本程式的訊息泵從訊息佇列提取到這個字元的按鍵訊息後,會呼叫當前視窗的處理函式來處理這個字元訊息,當視窗處理函式處理WM_CHAR時,會在螢幕上顯示這個字元,我們這裡,這個字元是a,這就是整個訊息處理的全過程。

      好了,現在,我們言歸正傳,繼續分析接下來的程式。WinMain程式,這個是所有Windows程式的入口點,它是必須要有的,上次,我們已經講過,這裡不再提及。

     接下來,我們來一起看一下這個WinMain函式都做了什麼?

    為了幫助大家理解WinMain函式,我們這裡先使用自然語言進行概括性的描述。   

int WINAPI WinMain(HINSTANCE hinstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nCmdShow)
{
        1、定義一個WNDCLASS或者WNDCLASSEX變數wcx;
        2、設定WNDCLASSEX變數wcx;
        3、利用wcx建立註冊一個視窗類MainWClass,視窗類就好比一個模版,Windows系統會參考這個模版,為我們建立視窗;
        4、用我們建立的視窗類MainWClass,建立一個視窗;
        5、呼叫ShowWindow和UpdateWindow顯示和更新視窗;
        6、建立一個訊息泵,這個訊息泵的任務就是不斷從訊息佇列中提取訊息,並將訊息傳遞給當前視窗的處理函式,
        當訊息泵接收到WM_QUIT退出訊息後,訊息泵就會退出;
        7、程式返回。       
 }
    好了,現在,我們繼續分析WinMain函式的內容:

    1)定義一個WNDCLASS或者WNDCLASSEX變數wcx,這個目標是通過如下的程式碼來完成的:

WNDCLASSEX wcx;
    2)設定視窗類,具體實現是通過如下的程式碼:

wcx.cbSize = sizeof(wcx);           // 這個引數設定wcx的大小;
wcx.style = CS_HREDRAW |CS_VREDRAW; // 告訴系統,當視窗大小改變時,重繪整個視窗;
wcx.lpfnWndProc = MainWndProc;      // 告訴系統,當前視窗類的訊息處理函式是MainWndProc;
wcx.cbClsExtra = 0;                 // 這個大家先不用管,設定為0就可以;
wcx.cbWndExtra = 0;                 // 這個大家也先不用管,設定為0就可以;
wcx.hInstance = hinstance;          // 告訴系統當前這個視窗屬於哪個程式,系統會根據這個引數,將該視窗的訊息傳送到這個程式的訊息佇列;
wcx.hIcon = LoadIcon(NULL,IDI_APPLICATION); // 視窗圖示:大家也先不用管,使用這個IDI_APPLICATION值,就可以;
wcx.hCursor = LoadCursor(NULL,IDC_ARROW); //設定這個視窗的游標樣式,大家也先不用管,設定為IDC_ARROW,就可以;
wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);  // 設定當前視窗的背景顏色,這裡是白色WHITE_BRUSH;
wcx.lpszMenuName = NULL; //當前視窗的選單,我們這個視窗沒有選單,所以,只需要設定為NULL,就可以;
wcx.lpszClassName = "MainWClass";  // 告訴系統,我們這個視窗的類名;
wcx.hIconSm = (HICON)LoadImage(hinstance, // 當前視窗的小圖示,大家先這麼寫,先不用管具體什麼意思,後面會介紹。
		MAKEINTRESOURCE(5),
		IMAGE_ICON,
		GetSystemMetrics(SM_CXSMICON),
		GetSystemMetrics(SM_CYSMICON),
		LR_DEFAULTCOLOR);

    3)利用wcx建立註冊一個視窗類MainWClass,具體通過如下的程式碼來完成:

if (!RegisterClassEx(&wcx))
{
	return 1;
}

    4)用我們建立的視窗類MainWClass,建立一個視窗,具體通過如下程式碼來完成:

hwnd = CreateWindow(
		"MainWClass",        // 視窗類名
		"我們的第二個程式",  // 視窗標題 
		WS_OVERLAPPEDWINDOW, // 視窗樣式  
		CW_USEDEFAULT,       // 水平位置X:預設 
		CW_USEDEFAULT,       // 垂直位置Y:預設
		CW_USEDEFAULT,       // 寬度:預設
		CW_USEDEFAULT,       // 高度:預設 
		(HWND)NULL,          // 父視窗:無 
		(HMENU)NULL,         // 選單:使用視窗類的選單 
		hinstance,           // 應用程式例項控制程式碼 
		(LPVOID)NULL);       // 視窗建立時資料:無 
    5)呼叫ShowWindow和UpdateWindow顯示和更新視窗,具體通過如下程式碼來完成:

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
    6)建立一個訊息泵,具體通過如下的程式碼來完成:

while ((fGotMessage = GetMessage(&msg, (HWND)NULL, 0, 0)) != 0 && fGotMessage != -1)
{
	TranslateMessage(&msg);
	DispatchMessage(&msg);
}
   7)程式返回,具體通過如下的程式碼來完成:

return msg.wParam; //這個msg通常是WM_QUIT,這個wParam是這個訊息的退出碼,通常由PostQuitMessage來設定,主要是通知系統,該程式是否正常退出。
    相信,通過上面這段描述,大家一定對這個例子中的WinMain函式有了一個整體的把握,現在,我們趁熱打鐵,再一起分析一下我們這個視窗類的視窗處理函式。

    現在,我先告訴大家視窗處理函式的格式,具體如下:

LRESULT CALLBACK 視窗處理函式名(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
    大家只需要記住,視窗處理函式的格式必須這麼寫,只有函式名可以改變,其它的都不可以改變,這個是系統規定的,記住就可以,其中WPARAM和LPARAM是Windows訊息中的兩個引數,當視窗處理函式被呼叫的時候,hwnd代表了當前的視窗,uMsg代表了當前的訊息,在Windows中,每個訊息都用一個整數來表示。wParam和lParam是這個訊息中的兩個引數。

    下面,我先給出訊息處理函式的一般流程:

LRESULT CALLBACK 視窗訊息處理函式名(
        HWND hwnd,
	UINT uMsg,
	WPARAM wParam,
	LPARAM lParam
	)
{
	switch (uMsg)
	{
	    case 訊息1:
		{
                    訊息處理函式1;
                    break;
                }
            case 訊息2:
                {
                    訊息處理函式2;
                    break;
                }
            ...
            default:
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}
}
    上面描述了訊息處理函式的一般流程,我們按照這個一般流程來講解一下我們的視窗處理函式,具體如下:

    我們的視窗處理函式,只響應了一個訊息WM_DESTROY,這個訊息是在我們點選視窗的關閉按鈕是產生的,當我們的訊息泵接收到這個訊息後,就會將它發給我們的視窗處理函式,然後,我們的視窗處理函式呼叫函式ExitThread,其中傳遞的是執行緒的退出碼,執行緒正常退出,通常都設定為0,當執行完這個函式後,整個執行緒會結束執行,我們這裡的執行緒只有一個,就是WinMian所在的執行緒,當這個執行緒退出後,整個程式就會結束執行。

   對於其它的訊息,我們都使用系統提供的預設訊息處理函式來處理,這個函式的原型如下:  

LRESULT DefWindowProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam);
    在這個函式中,hWnd就是當前視窗的控制程式碼,Msg就是當前要處理的訊息,wParam和lParam就是當前訊息中的兩個引數。

    好了,今天講了很多,相信,大家讀完後,應該會對我們的第2個Windows程式有所理解,希望大家多實踐,如果有暫時不明白的內容,可以先放下,因為主要的內容,我已經告訴你們了,次要的後邊會根據需要講解。 

   

   

相關文章