關於Windows應用程式的基礎,基本內容就是那幾項,以前學MFC的時候看孫鑫的視訊講過一遍,學習DirectX的時候學過一遍。現在回來想做MFC的程式,發現又忘了,這種學了忘忘了學的感覺真煩惱,每次都要從頭學起,每次都是新手。所以我決定一定要自己寫個總結,可能還是會忘記,但是檢視自己的總結感覺踏實一些。
一. WinMain函式
Windows程式的入口函式:
int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance // 應用程式例項的控制程式碼。同一程式程式碼也有可能有多個例項,因為應用程式例項也是一種資源,所以需要對應用程式例項進行標識。 HINSTANCE hPrevInstance, // handle to previous instance // 先前例項的標識,之前沒有則為空。Win32下總是為空。 LPSTR lpCmdLine, // command line //LP表示長指標,STR表示字串,所以LPSTR表示指向字串的指標。lpCmdLine接受命令列引數。 int nCmdShow // show state // 顯示狀態,指定程式執行的時候應該如何顯示 );
WinMain函式由作業系統呼叫,因此WinMain函式的引數都是由作業系統進行賦值。
二.視窗
視窗一般需要進行以下幾個步驟:
1.設計視窗
2.註冊視窗
3.建立視窗
4.顯示及更新視窗
視窗類是一個結構體
typedef struct WNDCLASSEX { UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS;
1.設計視窗
設計視窗類。
WNDCLASSEX wc; //視窗類的型別 wc.style = CS_HREDRAW | CS_VREDRAW; //水平或垂直位置變化的時候要求視窗重畫 //視窗過程函式,一個指標,指向一個視窗過程,用來接收一個函式指標,用來指定視窗對應的過程函式/回撥函式 wc.lpfnWndProc = (WNDPROC) WndProc; //函式名可以代表程式碼的首地址 //類的附加記憶體 wc.cbClsExtra = 0; //通常情況下設定為0 //視窗的附加記憶體空間 wc.cbWndExtra = 0; wc.cbSize = sizeof(WNDCLASSEX); //應用程式例項號,來自WinMain函式引數 wc.hInstance = hInstance; //圖示控制程式碼 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); //標準圖示被載入的時候第一個引數必須為NULL //如果要自定義的圖示的話 //wc.hIcon = LoadIcon(hInstance, //(LPCTSTR)IDI_DIRECTX_ICON); //游標控制程式碼 wc.hCursor = LoadCursor(NULL, IDC_ARROW); //畫刷的控制程式碼 wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); //選單的名字 wc.lpszMenuName = 0; //視窗類的名字 wc.lpszClassName = "Class Name";
2.註冊視窗
視窗設計完之後,需要向作業系統進行註冊,之後才可以建立基於這種視窗型別的視窗。
//函式原型: //ATOM RegisterClass (CONST WNDCLASS* lpWndClass) ; //向作業系統註冊視窗 RegisterClassEx(&wc) ) //傳入視窗類結構體的地址 //函式的返回值為布林值,註冊成功則返回真
3.建立視窗
註冊完成之後,就可以建立視窗類例項了。首先需要定義一個控制程式碼,作為一個標識,儲存下來。
HWND CreateWindow( LPCTSTR lpszClassName, //視窗類名 LPCTSTR lpszTitle, //視窗標題名 DWORD dwStyle, //建立視窗的樣式 int x,y, //視窗左上角座標 int nWidth,nHeight, //視窗寬度和高度 HWND hwndParent,//該視窗的父視窗控制程式碼 HWENU hMenu, //視窗主選單控制程式碼 HINSTANCE hInstance, //建立視窗的應用程式當前控制程式碼 LPVOID lpParam //指向一個傳遞給視窗的引數值的指標 ); //建立視窗 HWND hwnd = 0; //控制程式碼,用以儲存新建立的視窗的標識 hwnd = CreateWindow( " Class Name ", //註冊的類名,設計視窗的時候指定的名字 "視窗標題", WS_OVERLAPPEDWINDOW,//層疊的視窗,具有標題欄和邊框 0, 0, width, height, 0 /*parent hwnd*/, 0 /* menu */, hInstance, 0 /*extra*/); //第一個引數是註冊的類名,一定要用註冊之後的視窗類名 //第二個引數是視窗的名字,即視窗標題名 //第三個引數是視窗的型別 //之後的四個參數列示視窗的左上角水平座標、垂直座標、寬度、高度 //之後是一個視窗的控制程式碼HWND,父視窗控制程式碼,沒有父視窗可設定為NULL //之後是選單控制程式碼 //之後是當前應用程式例項的控制程式碼,WinMain形參之一 //之後是一個資料指標,多文件時用,主要作為WM_CREATE訊息的附加引數
4.顯示及更新視窗
//顯示視窗 ShowWindow(hwnd, SW_SHOW);//視窗控制程式碼及顯示狀態 //更新視窗,重新整理一次視窗,可有可無 UpdateWindow(hwnd);
三.訊息及訊息迴圈
1.訊息
作業系統將每個事件(輸入裝置上的變化)都包裝成一個稱為訊息的結構體MSG來傳遞給應用程式,然後由應用程式來決定對此事件做出何種響應,應用程式通過呼叫函式的形式來通知作業系統執行相應的功能。應用程式對這些函式的呼叫就叫做系統呼叫,這些函式的集合就是Windows作業系統提供給應用程式程式設計的介面(Application Programming Interface),簡稱Windows API。
訊息結構體:
typedef struct tagMSG { HWND hwnd; //接受訊息的視窗的控制程式碼; //控制程式碼是資源的標識,視窗控制程式碼的型別是HWND UINT message; //訊息識別符號,表示具體的訊息資訊; WPARAM wParam; //32位的附加訊息引數。根據訊息的不同而不同。 LPARAM lParam; //32位的附加訊息引數,其值與訊息有關; DWORD time; //雙16位,即32位的整數,訊息放入訊息佇列的時間 POINT pt; //訊息放入訊息佇列時滑鼠的座標 } MSG, *PMSG;
UINT是一個無符號整數,因為數值一般不好記憶,所以微軟定義了一些巨集來幫助記憶,巨集以WM作為開頭(Window Message),如WM_LBUTTONDOWN,WM_CHAR等。
當按下鍵盤之後,無論按下哪個鍵,收到的都是WM_CHAR訊息,並不知道按下的是哪個字母,可以通過wParam獲取到相對應的字母的ASCII碼。
2.訊息迴圈
訊息佇列是一個先進先出的緩衝區,作業系統為每一個應用程式都會建立一個訊息佇列,應用程式從訊息佇列中取出訊息,進行相應的相應。
訊息迴圈的常見寫法如下:
MSG msg; while(GetMessage(&msg,NULL,0,0)) { TranslateMessage (&msg); DispatchMessage (&msg); }
GetMessage從訊息佇列中獲取訊息。檢索到WM_QUIT訊息時返回零值。
GetMessage( lpMSG,//指向MSGMSG結構的指標 hwnd, //視窗控制程式碼 nMsgFilteMin, //用於訊息過濾的最小訊息號值 nMsgFilterMax//用於訊息過濾的最大訊息號值 );
TranslateMessage轉換訊息。比如將鍵盤的WM_KEYDOWN和WM_KEYUP訊息轉換成WM_CHAR訊息,放在訊息佇列當中。
DispatchMessage將收到的訊息傳到視窗的回撥函式(視窗過程函式)中。
3.視窗過程函式(回撥函式)
視窗過程函式用來處理和響應訊息。
LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
函式名可以更改,但是引數的型別不能變,引數名可以隨便取。
接收一個訊息,DispatchMessage呼叫的時候,作業系統呼叫這個視窗過程函式,並且將相關的引數傳遞給這個函式。直到視窗過程函式將控制返回給Windows作業系統,DispatchMessage才能返回。
函式內部一般用Switch case語句判斷具體是什麼訊息,然後採取相應的措施。
一個回撥函式的例子如下:
// // WndProc // //視窗過程函式,回撥函式 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) //視窗控制程式碼、訊息標識、訊息的附加引數 { //LRESULT是個Long型,返回的結果碼 switch( msg ) { case WM_DESTROY: //在訊息處理程式段中一般都有對//WM_DESTROY的處理,該訊息是關閉視窗時發出的 PostQuitMessage(0); break; case WM_KEYDOWN: if( wParam == VK_ESCAPE ) DestroyWindow(hwnd); break; default: //提供預設處理,讓系統對不感興趣的訊息進行一些預設處理 return DefWindowProc(hwnd, msg, wParam, lParam); //按鍵: WM_CHAR //滑鼠左鍵: WM_LBUTTONDOWN //視窗重繪: WM_PAINT //關閉: WM_CLOSE //DestroyWindow函式銷燬視窗,併傳送以下訊息 //WM_DESTROY 然後PostQuitMessage(0) 退出程式,傳送WM_QUIT訊息,訊息迴圈返回值,結束訊息迴圈,退出WinMain函式 } return 0; }