預備知識
1.什么是句柄? (HANDLE)
在win32編程中有各種句柄,那么什么是句柄呢?
#define DECLARE_HANDLE(name)
struct name##_
{
int unused;
};
typedef struct name_* name;
例如HDC的定義
#define DECLARE_HANDLE(HDC)
struct HDC_
{
int unused;
};
typedef struct HDC_ * HDC
當一個函數需要HWND類型參數的時候,你就不能傳遞HDC,因為類型不匹配。
總結:1.一個窗口句柄本質上是一個void * 2.win32編程中有特別多不同類型的窗口句柄,所以我們就把他們定義成不同的類型。例如HDC就是 HDC_*類型,HWND就是HWND_*類型。這樣能避免參數類型錯誤。
2.calling convention 函數調用約定
這些現象通常是出現在C和C++的代碼混合使用的情況下或在C++程序中使用第三方的庫的情況下(不是用C++語言開發的),其實這都是函數調用約定(Calling Convention)和函數名修飾(Decorated Name)規則惹的禍。函數調用方式決定了函數參數入棧的順序,是由調用者函數還是被調用函數負責清除棧中的參數等問題,而函數名修飾規則決定了編譯器使用何種名字修飾方式來區分不同的函數,如果函數之間的調用約定不匹配或者名字修飾不匹配就會產生以上的問題。
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_ASTERISK);
wc.hInstance = hInstance;
wc.lpfnWndProc = (WNDPROC)WndProc;
wc.lpszClassName = szClassName;
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW;
3.窗口函數WNDPROC。
窗口類初始化中有一個(WNDPROC)WndProc窗口函數。窗口函數就是窗口對各種消息的處理函數(鼠標點擊消息,鍵盤消息)。
我們使用switch case 結構來處理消息。
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
//The PAINTSTRUCT structure contains information for an application.
//This information can be used to paint the client area of a window owned by that application.
PAINTSTRUCT ps;
//The RECT structure defines the coordinates of the upper-left and lower-right corners of a rectangle.
RECT rect;
switch(msg)
{
case WM_CREATE:
PlaySound (L"hello.wav", NULL, SND_FILENAME | SND_ASYNC) ;
break;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps);
GetClientRect (hwnd, &rect) ;
DrawText (hdc, TEXT ("Hello, Windows 98!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
EndPaint(hwnd, &ps);
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
4.創建窗口
HWND hWnd; //創建窗口返回的句柄。如果不是NULL則創建成功。
hWnd = CreateWindow(szClassName, L"HelloWorld", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 650, 400, NULL, NULL, hInstance, NULL);
5.顯示窗口
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
6.消息循環
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
什么是消息循環,它如何工作?
1.消息循環調用while循環中的GetMessage()函數,GetMessage()函數在消息隊列中尋找消息。如果沒有消息,程序就一直“停”在while循環中。
2.當一個消息進入消息隊列時,比如你點擊鼠標觸發了一個消息。GetMessage()函數返回一個大於0的值,表示這個消息正在被處理,並給msg結構體賦值。
(WM_QUIT消息 GetMessage()函數返回0,如果產生錯誤 GetMessage()函數負值)。
3.我們獲得msg這個消息結構體,傳遞給TranslateMessage()函數,TranslateMessage()函數將虛擬的鼠標,鍵盤消息轉化成WM_開頭的字符串消息。
4.我們將字符串消息傳遞給DispatchMessage()函數。DispatchMessage()函數將會查找是那個窗口產生的消息,並且調用該窗口的窗口函數來處理。
我們將傳遞窗口的句柄,msg,wParam,lParam給窗口函數。
5.在窗口處理函數中,將檢查消息(該消息是WM_那種消息?),並在特定的case中處理。如果沒有這個消息的分類,則在DefWindowProc()函數中,默認處理。
6.一旦處理完消息,窗口處理函數返回,DispatchMessage()函數返回。程序又去消息隊列中尋找下一個消息(返回最初始的狀態)。
更多win32學習: http://www.winprog.org/tutorial/
完整代碼: https://github.com/Superxy/Win32/blob/master/SimpleWindow/SimpleWindow/SimpleWindow.cpp