在上一篇博文《Windows程序設計03:創建窗口類》(http://www.cnblogs.com/menlsh/archive/2013/01/26/2878162.html)中,我們提到創建一個應用程序窗口需要五個步驟,並實現了第一個步驟:創建窗口類。
在本篇博文中,將繼續介紹第二步和第三步的實現方法,調用RegisterClass函數注冊窗口類和調用CreateWindow函數創建窗口。
1窗口類的注冊
當我們創建好了窗口類之后,為了能夠使用該窗口類,就必須先注冊該窗口類。注冊窗口類是通過調用RegisterClass函數來實現的。
在WinUser.h頭文件中定義了RegisterClass函數,其函數原型如下:
1 WINUSERAPI 2 ATOM 3 WINAPI 4 RegisterClassA( 5 __in CONST WNDCLASSA *lpWndClass); 6 WINUSERAPI 7 ATOM 8 WINAPI 9 RegisterClassW( 10 __in CONST WNDCLASSW *lpWndClass); 11 #ifdef UNICODE 12 #define RegisterClass RegisterClassW 13 #else 14 #define RegisterClass RegisterClassA 15 #endif // !UNICODE
可以看出該函數的唯一參數是指向窗口類WNDCLASS的指針。實際上,系統存在兩種不同的用於注冊窗口類的函數:RegisterClassA和RegisterClassW,它們的參數則分別是指向上述定義中的WNDCLASSA的指針和WNDCLASSW的指針。程序利用哪個函數注冊窗口類決定着傳遞給窗口的消息是包含ASCII文本還是UNICODE文本。
當我們定義了UNICODE標識符時,程序顯然會調用RegisterClassW函數來完成窗口類的注冊。在Windows NT下運行程序時,這是沒有問題的。但是在Windows98下運行程序時,RegisterClassW函數實際上並沒有實現,而且會返回零值表示出現了錯誤。在這種情況下,應該通知用戶應用程序發生錯誤並終止程序。所以可以參照如下方法使用RegisterClass函數注冊窗口類。
1 if(!RegisterClass(&wndclass)) 2 { 3 MessageBox(NULL, TEXT("該應用程序只能運行在Windows NT上!"), szAppName, MB_ICONERROR); 4 return 0; 5 }
可以看出,以上代碼添加了容錯處理,當在程序中定義了UNICODE標識符,而用戶又是在Windows98下運行該程序的,就會得到一個錯誤的提示框“該應用程序只能運行在Windows NT上!”,並終止程序的運行。
當然,這種情況是很難發生的了,因為很顯然,現在的Windows操作系統絕大多數都是Windows NT了,從圖1就可以清楚的看出這一點了。
圖1 Windows家族
不過,出於對應用程序的健壯性方面的考慮,增加這樣的容錯處理還是很有必要的。
2窗口的創建
由於窗口類只是定義了窗口的一般特征,而許多與窗口有關的細節信息則可以在創建窗口時定義。
創建窗口是通過調用CreateWindow函數來實現的。在WinUser.h頭文件中定義了CreateWindow的函數原型如下:
1 #define CreateWindowA(lpClassName, lpWindowName, dwStyle, x, y,\ 2 nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)\ 3 CreateWindowExA(0L, lpClassName, lpWindowName, dwStyle, x, y,\ 4 nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam) 5 #define CreateWindowW(lpClassName, lpWindowName, dwStyle, x, y,\ 6 nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)\ 7 CreateWindowExW(0L, lpClassName, lpWindowName, dwStyle, x, y,\ 8 nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam) 9 #ifdef UNICODE 10 #define CreateWindow CreateWindowW 11 #else 12 #define CreateWindow CreateWindowA 13 #endif // !UNICODE
可以看出,CreateWindow函數總共有11項參數,下面對這些參數做個簡單的介紹,這也是使用CreateWindow函數的前提。
2.1窗口類名稱
CreateWindow函數的第一個參數lpClassName指定了該窗口是基於哪個窗口類創建的。可見,我們所要創建的窗口正是通過這種方式與之前創建和注冊的窗口類建立起了聯系。
2.2窗口標題
CreateWindow函數的第二個參數lpWindowName指定了該窗口的標題,當應用程序運行起來之后,該標題將顯示在應用程序的窗口左上角(默認情況下)。
2.3窗口風格
CreateWindow函數的第三個參數dwStyle指定了窗口的風格。在WinUser.h頭文件中定義了窗口風格如下:

1 /* 2 * Window Styles 3 */ 4 #define WS_OVERLAPPED 0x00000000L 5 #define WS_POPUP 0x80000000L 6 #define WS_CHILD 0x40000000L 7 #define WS_MINIMIZE 0x20000000L 8 #define WS_VISIBLE 0x10000000L 9 #define WS_DISABLED 0x08000000L 10 #define WS_CLIPSIBLINGS 0x04000000L 11 #define WS_CLIPCHILDREN 0x02000000L 12 #define WS_MAXIMIZE 0x01000000L 13 #define WS_CAPTION 0x00C00000L /* WS_BORDER | WS_DLGFRAME */ 14 #define WS_BORDER 0x00800000L 15 #define WS_DLGFRAME 0x00400000L 16 #define WS_VSCROLL 0x00200000L 17 #define WS_HSCROLL 0x00100000L 18 #define WS_SYSMENU 0x00080000L 19 #define WS_THICKFRAME 0x00040000L 20 #define WS_GROUP 0x00020000L 21 #define WS_TABSTOP 0x00010000L 22 23 #define WS_MINIMIZEBOX 0x00020000L 24 #define WS_MAXIMIZEBOX 0x00010000L 25 26 #define WS_TILED WS_OVERLAPPED 27 #define WS_ICONIC WS_MINIMIZE 28 #define WS_SIZEBOX WS_THICKFRAME 29 #define WS_TILEDWINDOW WS_OVERLAPPEDWINDOW
這些宏定義標識符都代表着什么式樣的窗口風格呢?可以參考百度百科CreateWindow(http://baike.baidu.com/view/1001690.htm)。其中,比較常用的窗口風格有以下一些:
單邊框的窗口WS_BORDER
帶標題框的窗口WS_CAPTION
帶對話框邊框的窗口WS_DLGFRAME
初始狀態為禁止的窗口WS_DISABLED
初始狀態為最小化的窗口WS_ICONIC
初始狀態為最大化的窗口WS_MAXIMIZE
帶有水平滾動條的窗口WS_HSCROLL
帶有垂直滾動條的窗口WS_VSCROLL
帶有最大化按鈕的窗口WS_MAXIMIZEBOX
帶有最小化按鈕的窗口WS_MINIMIZEBOX
標題條上帶有窗口菜單的窗口WS_SYSMENU
如果需要使創建的窗口同時具有上述的幾種窗口風格,可以通過使用按位或運算(|)來實現。在WinUser.h頭文件中同樣定義了一些窗口風格的組合,可以方便我們直接使用,具體如下:

1 /* 2 * Common Window Styles 3 */ 4 #define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | \ 5 WS_CAPTION | \ 6 WS_SYSMENU | \ 7 WS_THICKFRAME | \ 8 WS_MINIMIZEBOX | \ 9 WS_MAXIMIZEBOX) 10 11 #define WS_POPUPWINDOW (WS_POPUP | \ 12 WS_BORDER | \ 13 WS_SYSMENU) 14 15 #define WS_CHILDWINDOW (WS_CHILD)
可以看出,WS_OVERLAPPEDWINDOW同時組合了WS_OVERLAPPED、WS_CAPTION、WS_SYSMENU、WS_THICKFRAME、WS_MINIMIZEBOX、WS_MAXIMIZEBOX六種窗口風格。而WS_POPUPWINDOW則同時組合了WS_POPUP、WS_BORDER、WS_SYSMENU三種窗口風格。
2.4初始x、y坐標
CreateWindow函數的第四個參數x和第五個參數y指定了窗口左上角相對於屏幕左上角的初始位置。通常可以將其設為默認值CW_USEDEFAULT,那么Windows會將連續新建的窗口的左上角位置沿水平方向和垂直方向分別作步長為1的偏移。
2.5初始x、y方向尺寸
CreateWindow函數的第六個參數nWidth和第七個參數nHeight指定了窗口的初始化寬度和高度。通常可以將其設為默認值CW_USEDEFAULT,表明希望Windows將窗口的尺寸取為默認值。
2.6父窗口句柄
CreateWindow函數的第八個參數hWndParent表示應用程序的父窗口句柄。應用程序窗口總是位於桌面的前方,但不必為了調用CreateWindow函數而去設法獲取桌面窗口的句柄。可以將我們新建的應用程序窗口看作是頂級窗口,直接將hWndParent參數值置為NULL即可。通常,當兩個窗口之間存在父子關系時,子窗口總是位於父窗口的前面。
2.7窗口菜單句柄
CreateWindow函數的第九個參數hMenu表示應用程序窗口的菜單句柄。如果應用程序窗口沒有菜單欄,則直接將該參數設置為NULL即可。
2.8程序實例句柄
CreateWindow函數的第十個參數hInstance表示應用程序的實例句柄。該參數可以直接被設置為WinMain函數的第一個傳入參數即可。
2.9創建參數
CreateWindow函數的最后一個參數lpParam表示創建參數,通常設置為NULL,也可以將該參數指向某些數據,以便在后續的程序中加以引用(這個參數具體怎么使用現在還不清楚,有待以后繼續學習)。
2.10返回值
CreateWindow函數的返回值為一個指向所創建窗口的句柄。Windows系統中,每一個窗口都有一個句柄,在程序中可以使用句柄來對窗口進行調用。很多Windows函數都以窗口句柄作為輸入參數,以便Windows獲知該函數是要對哪一個窗口進行操作。如果一個程序創建了多個窗口,則每個窗口都應該具有不同的窗口句柄,以方便進行區分和調用。
清楚了CreateWindow函數的參數以及返回值的使用方法,便可以實際編寫CreateWindow函數了,下面的代碼給出了一個簡單的示例:
1 HWND hwnd; //窗口句柄 2 hwnd = CreateWindow(szAppName, //窗口類名稱 3 TEXT("博客園-依舊淡然"), //窗口標題 4 WS_OVERLAPPEDWINDOW, //窗口風格 5 CW_USEDEFAULT, //初始x坐標 6 CW_USEDEFAULT, //初始y坐標 7 CW_USEDEFAULT, //初始x方向尺寸 8 CW_USEDEFAULT, //初始y方向尺寸 9 NULL, //父窗口句柄 10 NULL, //窗口菜單句柄 11 hInstance, //程序實例句柄 12 NULL); //創建參數
相關資料:
關於RegisterClass的注冊位置
http://blog.csdn.net/testcs_dn/article/details/7634711