上篇文章講了如何編寫一個彈出對話框的程序,這篇文章就來講講如何在C\C++下創建一個窗口。
來簡單介紹下 Windows窗口程序 的創建過程:
首先我們需要注冊一個窗口類,可以使用RegisterClass函數,接着使用CreateWindow創建一個窗口。下一步需要讓窗口在屏幕上面顯示出來,可以使用ShowWindow來完成,接着是更新窗口,注意一定不能漏掉這個步驟!這個闊以使用UpdateWindow來完成。接着就是建立消息循環隊列啦,如果沒有這個,程序就不能響應Windows發來的信息啦。
來康康代碼:
#include <windows.h>
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ; //聲明用來處理消息的函數
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow )
{
static TCHAR szAppName[] = TEXT("MyWindow") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ; //聲明一個窗口類對象
//以下為窗口類對象wndclass的屬性
wndclass.style = CS_HREDRAW | CS_VREDRAW ; //窗口樣式
wndclass.lpszClassName = szAppName ; //窗口類名
wndclass.lpszMenuName = NULL ; //窗口菜單:無
wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH) ; //窗口背景顏色
wndclass.lpfnWndProc = WndProc ; //窗口處理函數
wndclass.cbWndExtra = 0 ; //窗口實例擴展:無
wndclass.cbClsExtra = 0 ; //窗口類擴展:無
wndclass.hInstance = hInstance ; //窗口實例句柄
wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION ) ; //窗口最小化圖標:使用缺省圖標
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ) ; //窗口采用箭頭光標
if( !RegisterClass( &wndclass ) )
{ //注冊窗口類, 如果注冊失敗彈出錯誤提示
MessageBox( NULL, TEXT("窗口注冊失敗"), TEXT("錯誤"), MB_OK | MB_ICONERROR ) ;
return 0 ;
}
hwnd = CreateWindow( //創建窗口
szAppName, //窗口類名
TEXT("我的第一個Windows窗口"), //窗口標題
WS_OVERLAPPEDWINDOW, //窗口的風格
CW_USEDEFAULT, //窗口初始顯示位置x:使用缺省值
CW_USEDEFAULT, //窗口初始顯示位置y:使用缺省值
CW_USEDEFAULT, //窗口的寬度:使用缺省值
CW_USEDEFAULT, //窗口的高度:使用缺省值
NULL, //父窗口:無
NULL, //子菜單:無
hInstance, //該窗口應用程序的實例句柄
NULL
) ;
ShowWindow( hwnd, iCmdShow ) ; //顯示窗口
UpdateWindow( hwnd ) ; //更新窗口
while( GetMessage( &msg, NULL, 0, 0 ) ) //從消息隊列中獲取消息
{
TranslateMessage( &msg ) ; //將虛擬鍵消息轉換為字符消息
DispatchMessage( &msg ) ; //分發到回調函數(過程函數)
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
HDC hdc ; //設備環境句柄
PAINTSTRUCT ps ; //繪制結構
RECT rect; //矩形結構
switch( message ) //處理得到的消息
{
case WM_PAINT: //處理窗口區域無效時發來的消息
hdc = BeginPaint( hwnd, &ps ) ;
GetClientRect( hwnd, &rect ) ;
DrawText( hdc, TEXT("Hello World"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER ) ; //文字
EndPaint( hwnd, &ps ) ;
return 0 ;
case WM_DESTROY: //處理窗口關閉時的消息
MessageBox( hwnd, TEXT("關閉程序!"), TEXT("結束"), MB_OK | MB_ICONINFORMATION ) ;
PostQuitMessage( 0 ) ;
return 0;
}
return DefWindowProc( hwnd, message, wParam, lParam ) ; //DefWindowProc處理我們自定義的消息處理函數沒有處理到的消息
}
編譯(方式同上篇文章),你會看到個Windows窗口,啊恭喜你!你已經編譯出了你人生第一個Windows窗口程序(滑稽)。
由上面的代碼可知Windows窗口的代碼框架:
#include <windows.h>
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ; //聲明用來處理消息的函數
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow ) //Windows窗口程序的入口函數
{
//創建窗口、消息循環等
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ){
//處理消息
return DefWindowProc( hwnd, message, wParam, lParam ) ;
}
觀察上面的代碼,你會發現,哎,main不見了,取而代之的是WinMain,還有一個WndProc!是的你沒看錯,Windows窗口程序不再使用main作為入口函數了,而是使用WinMain來作為主函數。而且WinMain的參數也變了: ```cpp int WINAPI WinMain( //Windows窗口程序入口函數 HINSTANCE hInstance, //實例句柄 HINSTANCE hPrevInstance, //無用參數,總是為NULL(空,即“0”) PSTR szCmdLine, //傳入的命令 int iCmdShow //窗口顯示類型 ) ```
再仔細觀察,你會發現,WinMain旁邊還有個WINAPI,這是什么意思呢?其實,WINAPI是個在windef.h里面定義的標識符: ```cpp #define WINAPI __stdcall ``` 這條語句規定了一種函數調用約定,表明如何生成在堆棧中放置函數調用參數的機器代碼,絕大部分 Windows 函數調用都定義成 WINAPI。
Windows窗口程序工作原理
運行剛才編譯的程序,你會發現不管怎么拖動、縮放來改變 程序 的大小,那段Hello World的文字永遠是在屏幕正中央,這是怎么做到的呢?這就涉及到Windows窗口程序的工作原理了。 其實,用戶調整窗口大小所產生的瑣碎代碼都是 Windows 實現的而並非應用程序,那么應用程序如何知道自己的大小以及被改變了?每當用戶進行拖動窗口或者點擊窗口之類的操作時,Windows都會向程序發送一條消息,這就是上面代碼中的WndProc函數的作用。每當Windows向應用程序發送一條消息,都會觸發WndProc函數,現在來康康WndProc的參數:
LRESULT CALLBACK WndProc( //處理Windows發送來的消息
HWND hwnd, //窗口句柄(后面會講到)
UINT message, //Windows發來的消息
WPARAM wParam, //傳入的參數,暫時不需要理解
LPARAM lParam //傳入的參數,暫時不需要理解
)
顯然,WndProc是對message這個參數進行處理。
現在來一個個解析調用到的Windows API:
函數 | 意義 |
---|---|
LoadIcon | 加載圖標 |
LoadCursor | 加載鼠標光標 |
GetStockObject | 獲取圖形對象 |
RegisterClass | 注冊一個窗口類 |
MessageBox | 彈出對話框 |
CreateWindow | 創建一個窗口 |
ShowWindow | 顯示窗口 |
UpdateWindow | 更新窗口 |
GetMessage | 從消息隊列中獲取消息 |
TranslateMessage | 轉換消息 |
DispatchMessage | 將消息分發到回調函數處理 |
BeginDraw | 開始繪圖 |
GetClientRect | 獲取窗口大小 |
DrawText | 繪制文字 |
EndPaint | 結束繪制 |
PostQuitMessage | 插入"退出"消息到消息隊列 |
DefWindowProc | 執行默認的消息處理 |