對窗口創建的詳細解釋
============
在昨天的學習中, 我們已經初步完成了一個窗口的創建, 雖然我們對於代碼中的許多語句可能還不理解, 但這暫時並不會影響到我們的學習。再次來回顧昨天那個窗口的代碼部分:
Create myWindow - View Code
1 #include <windows.h> 2 3 LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ; //聲明用來處理消息的函數 4 5 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow ) 6 { 7 static TCHAR szAppName[] = TEXT("MyWindow") ; 8 HWND hwnd ; 9 MSG msg ; 10 WNDCLASS wndclass ; //聲明一個窗口類對象 11 12 //以下為窗口類對象wndclass的屬性 13 wndclass.style = CS_HREDRAW | CS_VREDRAW ; //窗口樣式 14 wndclass.lpszClassName = szAppName ; //窗口類名 15 wndclass.lpszMenuName = NULL ; //窗口菜單:無 16 wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH) ; //窗口背景顏色 17 wndclass.lpfnWndProc = WndProc ; //窗口處理函數 18 wndclass.cbWndExtra = 0 ; //窗口實例擴展:無 19 wndclass.cbClsExtra = 0 ; //窗口類擴展:無 20 wndclass.hInstance = hInstance ; //窗口實例句柄 21 wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION ) ; //窗口最小化圖標:使用缺省圖標 22 wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ) ; //窗口采用箭頭光標 23 24 if( !RegisterClass( &wndclass ) ) 25 { //注冊窗口類, 如果注冊失敗彈出錯誤提示 26 MessageBox( NULL, TEXT("窗口注冊失敗!"), TEXT("錯誤"), MB_OK | MB_ICONERROR ) ; 27 return 0 ; 28 } 29 30 hwnd = CreateWindow( //創建窗口 31 szAppName, //窗口類名 32 TEXT("我的窗口"), //窗口標題 33 WS_OVERLAPPEDWINDOW, //窗口的風格 34 CW_USEDEFAULT, //窗口初始顯示位置x:使用缺省值 35 CW_USEDEFAULT, //窗口初始顯示位置y:使用缺省值 36 CW_USEDEFAULT, //窗口的寬度:使用缺省值 37 CW_USEDEFAULT, //窗口的高度:使用缺省值 38 NULL, //父窗口:無 39 NULL, //子菜單:無 40 hInstance, //該窗口應用程序的實例句柄 41 NULL // 42 ) ; 43 44 ShowWindow( hwnd, iCmdShow ) ; //顯示窗口 45 UpdateWindow( &msg ); //更新窗口 46 47 while( GetMessage( &msg, NULL, 0, 0 ) ) //從消息隊列中獲取消息 48 { 49 TranslateMessage( &msg ) ; //將虛擬鍵消息轉換為字符消息 50 DispatchMessage( &msg ) ; //分發到回調函數(過程函數) 51 } 52 return msg.wParam ; 53 } 54 55 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) 56 { 57 HDC hdc ; //設備環境句柄 58 PAINTSTRUCT ps ; //繪制結構 59 RECT rect; //矩形結構 60 61 switch( message ) //處理得到的消息 62 { 63 case WM_CREATE: //窗口創建完成時發來的消息 64 MessageBox( hwnd, TEXT("窗口已創建完成!"), TEXT("我的窗口"), MB_OK | MB_ICONINFORMATION ) ; 65 return 0; 66 67 case WM_PAINT: //處理窗口區域無效時發來的消息 68 hdc = BeginPaint( hwnd, &ps ) ; 69 GetClientRect( hwnd, &rect ) ; 70 DrawText( hdc, TEXT( "Hello, 這是我自己的窗口!" ), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER ) ; 71 EndPaint( hwnd, &ps ) ; 72 return 0 ; 73 74 case WM_LBUTTONDOWN: //處理鼠標左鍵被按下的消息 75 MessageBox( hwnd, TEXT("鼠標左鍵被按下。"), TEXT("單擊"), MB_OK | MB_ICONINFORMATION ) ; 76 return 0; 77 78 case WM_DESTROY: //處理窗口關閉時的消息 79 MessageBox( hwnd, TEXT("關閉程序!"), TEXT("結束"), MB_OK | MB_ICONINFORMATION ) ; 80 PostQuitMessage( 0 ) ; 81 return 0; 82 } 83 return DefWindowProc( hwnd, message, wParam, lParam ) ; //DefWindowProc處理我們自定義的消息處理函數沒有處理到的消息 84 }
·調用了哪些Windows API
通過這段代碼, 我們完成了在客戶區(窗口的白色區域)中心顯示一段字符串, 此外, 我們還處理了系統發來的部分消息, 在這個過程中我們使用了Windows的諸多函數, 這些函數分別為:
| 函數名 | 函數描述 |
| GetStockObject |
獲取一個圖形對象 |
| LoadIcon |
為程序加載圖標 |
| LoadCursor |
為程序加載光標 |
| RegisterClass |
為程序窗口注冊一個窗口類 |
| MessageBox |
顯示消息對話框 |
| CreateWindow |
創建一個窗口 |
| ShowWindow |
在屏幕上將窗口顯示出來 |
| UpdateWindow |
重繪窗口客戶區 |
| GetMessage |
從消息隊列獲取消息 |
| TranslateMessage |
將虛擬鍵消息轉換為字符消息 |
| DispatchMessage |
將消息發送給消息處理函數 |
| BeginPaint |
准備對窗口進行繪圖 |
| GetClientRect |
獲取窗口客戶區尺寸 |
| DrawText |
繪制一個文本字符串 |
| EndPaint |
結束對窗口的繪圖 |
| PostQuitMessage |
向消息隊列插入"退出"消息 |
| DefWindowProc |
執行系統默認的消息處理 |
對於這些函數的詳細說明, 可以到百科查詢, 或者到MSDN Library文檔中查詢(推薦)。
·大寫標識符說明
在上面的程序中出現了許多類似於XX_XXXX的大寫標識符, 如:
| CS_HREDRAW |
CS_VREDRAW |
IDI_APPLICATION |
IDC_ARROW |
MB_OK |
| MB_ICONERROR |
WS_OVERLAPPEDWINDOW |
CW_USEDEFAULT |
DT_SINGLELINE |
DT_CENTER |
| DT_VCENTER |
WM_CREATE |
WM_PAINT |
WM_LBUTTONDOWN |
WM_DESTROY |
這些標識符均為數值常量, 在WINUSER.H頭文件中有定義, 該程序中用到的一些標識符前綴含義為:
| 前綴 |
含義 |
| CS_ |
類風格選項 |
| CW_ |
創建窗口選項 |
| DT_ |
文本繪制選項 |
| IDI_ |
圖標的ID號 |
| IDC_ |
光標的ID號 |
| MB_ |
消息框選項 |
| WM_ |
窗口消息 |
| WS_ |
窗口風格 |
對於這些大寫標識符暫時沒必要強記下來, 只需要對其有個大致的印象即可, 不會影響到我們的學習。
·"新"數據類型
在調用的函數中, 一些函數的參數類型是我們以往在C控制台編程中所沒有見到過的, 例如在代碼的第三行對回調函數的聲明中:
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ; //聲明用來處理消息的函數
其中的"LRESULT"、"UINT"、"WPARAM"、"LPWARAM", 以及主函數WinMain中的"PSTR"之類的類型都是我們所沒有見過的, 這些"新"數據類型都是什么呢?
其實這些"新"數據類型是為了程序書寫時的簡便而使用typedef或#define對C已有的數據類型重新定義的, 在WINDEF.H/WINNT.H頭文件中有定義, 如:
typedef unsigned int UINT; typedef char CHAR; typedef CHAR *LPSTR, *PSTR; typedef UINT WPARAM; typedef long LONG; typedef LONG LPARAM; typedef LONG LRESULT;
可以看出, UINT實際上就是unsigned int型; PSTR就是一個char *型, WPARAM被定義為UINT型, 也就是unsigned int型; LPARAM和LRESULT被定義為long型;
由此觀之, "新"數據類型, 並不新。
在聲明消息處理函數WndProc時, 在函數名前面我們還是使用了一個"CALLBACK"標識符, 這個標識符其實就是函數的調用規則, 在WINDEF.H中有定義:
#define CALLBACK __stdcall
在前些天的學習中, 我們還知道在WinMain主函數的前面有個WINAPI調用規則, 在WINDEF.H中也被定義為 __stdcall。
·結構類型
結構體, 在C語言的學習中我們已經對這種數據類型十分熟悉了, 在上述創建窗口的過程中我們使用了四種結構類型來創建結構體的對象, 這些結構體在WINUSER.H/WINDEF.H中有定義, 現在我們只需對其進行一下印象中的認識, 對於結構體的成員暫時不去討論, 這四種結構體分別為:
1>. MSG消息結構
MSG在WINUSER.H中的定義:
typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; #ifdef _MAC DWORD lPrivate; #endif } MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
2>. WNDCLASS窗口類結構
WNDCLASS在WINUSER.H中的定義:
typedef struct tagWNDCLASSW { UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCWSTR lpszMenuName; LPCWSTR lpszClassName; } WNDCLASSW, *PWNDCLASSW, NEAR *NPWNDCLASSW, FAR *LPWNDCLASSW; #ifdef UNICODE typedef WNDCLASSW WNDCLASS;
3>. PAINTSTRUCT繪制結構
PAINTSTRUCT在WINUSER.H中的定義:
typedef struct tagPAINTSTRUCT { HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[32]; } PAINTSTRUCT, *PPAINTSTRUCT, *NPPAINTSTRUCT, *LPPAINTSTRUCT;
4>. RECT矩形結構
RECT在WINDEF.H中的定義:
typedef struct tagRECT { LONG left; LONG top; LONG right; LONG bottom; } RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT;
·再談句柄
不同類型的句柄有不同的標識符, 在我們嘗試創建窗口的代碼中用到的句柄有:
| 標識符 |
含義 |
| HINSTANCE |
實例句柄, 指程序本身 |
| HWND |
窗口句柄 |
| HDC |
設備環境句柄 |
| HBRUSH |
圖形畫刷句柄 |
句柄是一個標識符,用來來標識對象, 一個句柄使用四個字節長的整數來存儲一個整數值, 這個具體的整數值實際上我們並不需要知道是多少, 我們要做的就是傳遞句柄, Windows會知道如果用過這個句柄找到並以引用相應的對象。
·匈牙利命名法
匈牙利命名法是一種編程時的命名規范, 在一開始我們接觸的MessageBox對話框時我們就已經見到了匈牙利命名的變量:
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow )
這里的 hInstance、hPrevInstance、szCmdLine、iCmdShow的命名都遵循匈牙利命名法, 匈牙利命名法的目的和作用是使變量名非常清晰易懂,增強了代碼的可讀性,方便各程序員之間相互交流代碼, 減少代碼中因數據類型不匹配的錯誤。
例如:
szCmdLine, 通過變量名我們就可以知道這是一個"以0結束的字符串";
hInstance, 以h開頭, 代表一個句柄;
iCmdShow, 以i開頭, 代表一個int整形。
這樣, 就可以不用再去找到變量聲明的位置去看它的變量類型, 避免了在使用變量時由於數據類型不匹配引起的錯誤。
經常使用的匈牙利命名的前綴如下:
| 前綴 | 含義描述 |
| p | 指針 |
| fn | 函數 |
| v | 無效 |
| h | 句柄 |
| l | 長整形 |
| b | 布爾型 |
| f | 浮點型 |
| dw | 雙字 |
| sz | 字符串 |
| n | 短整型 |
| d | 雙精度浮點型 |
| c | 計數, 通常寫為cnt |
| ch | 字符, 通常寫為c |
| i | 整型 |
| by | 字節 |
| w | 字型 |
| r | 實型 |
| u | 無符號型 |
| g_ | 全局變量 |
| c_ | 常量 |
| m_ | 成員變量 |
| s_ | 靜態變量 |
| Max | 最大 |
| Min | 最小 |
| Init | 初始化 |
| T 或 Temp | 臨時變量 |
| Src | 源對象 |
| Dest | 目標對象 |
-------------------
