建立一個視窗
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <tchar.h>
const TCHAR CLSNAME[] = TEXT("helloworldWClass");
LRESULT CALLBACK winproc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp);
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PTSTR cmdline,
int cmdshow)
{
WNDCLASSEX wc = { };
MSG msg;
HWND hwnd;
wc.cbSize = sizeof (wc);
wc.style = 0;
wc.lpfnWndProc = winproc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = CLSNAME;
wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc)) {
MessageBox(NULL, TEXT("Could not register window class"),
NULL, MB_ICONERROR);
return 0;
}
hwnd = CreateWindowEx(WS_EX_LEFT,
CLSNAME,
NULL,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInst,
NULL);
if (!hwnd) {
MessageBox(NULL, TEXT("Could not create window"), NULL, MB_ICONERROR);
return 0;
}
ShowWindow(hwnd, cmdshow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK winproc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
return DefWindowProc(hwnd, wm, wp, lp);
}
我們看到的第一件事是兩個巨集定義,UNICODE
和 _UNICODE
。這些巨集使我們的程式理解寬字串(wchar_t[n]
),而不是簡單的窄字串(char[n]
)。因此,所有字串文字都必須包含在 TEXT(
巨集中。Win32 字串的通用字元型別是 TCHAR
,其定義取決於是否定義了 UNICODE
。包含一個新標題:<tchar.h>
包含 TCHAR
的宣告。
視窗由所謂的視窗類組成。它描述了有關在其例項之間共享的視窗的資訊,如圖示,游標等。視窗類由視窗類名稱標識,在此示例中,CLSNAME
全域性變數中給出了該視窗類名稱。WinMain
的第一個動作是填寫視窗類結構 WNDCLASSEX wc
。成員是:
- cbSize:結構的大小(以位元組為單位)
- style:視窗類樣式。現在這是 0。
- lpfnWndProc:這是一個比較重要的領域。它儲存視窗過程的地址。視窗過程是一個函式,它處理作為此視窗類例項的所有視窗的事件。
- cbClsExtra:為視窗類分配的額外位元組數。在大多數情況下,此成員為 0。
- cbWndExtra:為每個視窗分配的額外位元組數。不要將此與
cbClsExtra
混淆,後者對所有情況都很常見。這通常是 0。 - hInstance:例項控制代碼。只需將
WinMain
中的hInst
引數分配給該欄位即可。 - hIcon:視窗類的圖示控制代碼。
LoadIcon(NULL, IDI_APPLICATION)
載入預設的應用程式圖示。 - hCursor:視窗類的游標控制代碼。
LoadCursor(NULL, IDC_ARROW)
載入預設游標。 - hbrBackground:背景畫筆的控制代碼。
GetStockObject (WHITE_BRUSH)
給出了白色筆刷的控制代碼。必須轉換返回值,因為GetStockObject
返回一個通用物件。 - lpszMenuName:要使用的選單欄的資源名稱。如果不需要選單欄,則此欄位可以為 NULL。
- lpszClassName:標識此視窗類結構的類名。在此示例中,
CLSNAME
全域性變數儲存視窗類名稱。 - hIconSm:小類圖示的控制代碼。
初始化此結構後,將呼叫 RegisterClassEx
函式。這會導致視窗類在 Windows 中註冊,使應用程式知道它。失敗時返回 0。
現在視窗類已經註冊,我們可以使用 CreateWindowEx
顯示視窗。論點是:
- stylesex:擴充套件視窗樣式。預設值為 WS_EX_LEFT。
- clsname:類名
- cap:視窗標題或標題。在這種情況下,它是在視窗標題欄中顯示的標題。
- styles:視窗樣式。如果要建立像這樣的頂級(父)視窗,則傳入的標誌是 WS_OVERLAPPEDWINDOW。
- x:視窗左上角的 x 座標。
- y:視窗左上角的 y 座標
- cx:視窗的寬度
- cy:視窗的高度
- hwndParent:父視窗的控制代碼。由於此視窗本身就是父視窗,因此該引數為 NULL。
- hMenuOrID:如果正在建立的視窗是父視窗,則此引數是視窗選單的控制代碼。不要把它與類選單混為一談,這就是
WNDCLASSEX::lpszClassName
。類選單對於具有相同類名的所有視窗例項都是通用的。但是,這個論點僅適用於此例項。如果正在建立的視窗是子視窗,則這是子視窗的 ID。在這種情況下,我們建立一個沒有選單的父視窗,因此傳遞 NULL。 - hInst:應用程式例項的控制代碼。
- etc:傳遞給視窗視窗過程的額外資訊。如果不傳輸額外資訊,則傳遞 NULL。
如果 x
或 y
或 cx
或 cy
是 CW_USEDEFAULT
,則該引數的值將由 Windows 確定。這是在這個例子中完成的。
CreateWindowEx
將控制代碼返回到新建立的視窗。如果視窗建立失敗,則返回 NULL
。
然後我們通過呼叫 ShowWindow
來顯示視窗。該函式的第一個引數是視窗的控制代碼。第二個引數是 show style,它指示視窗的顯示方式。大多數應用程式只傳遞 WinMain
中傳遞的 cmdshow
引數。顯示視窗後,必須通過呼叫 UpdateWindow
更新它。它會將更新訊息傳送到視窗。我們將在另一個教程中瞭解這意味著什麼。
現在是應用程式的核心:訊息泵。它泵送作業系統傳送到此應用程式的訊息,並將訊息分派給視窗過程。GetMessage
呼叫返回非零,直到應用程式收到導致它退出的訊息,在這種情況下它返回 0.唯一涉及我們的引數是指向 MSG
結構的指標,該結構將填入有關訊息的資訊。其他引數都是 0。
在訊息迴圈中,TranslateMessage
將虛擬金鑰訊息轉換為字元訊息。這再次對我們來說並不重要。它需要一個指向 MSG
結構的指標。直接跟隨它的呼叫 DispatchMessage
將其引數指向的訊息排程到視窗的視窗過程。WinMain
必須做的最後一件事是返回一個狀態程式碼。MSG
結構的 wParam
成員包含此返回值,因此返回。
但這僅僅適用於 WinMain
功能。另一個功能是 winproc
,視窗程式。它將處理 Windows 傳送給它的視窗的訊息。winproc
的簽名是:
- hwnd:正在處理其訊息的視窗控制代碼。
- wm:視窗訊息識別符號
- wp:訊息資訊引數之一。這取決於
wm
的論點 - lp:訊息資訊引數之一。這取決於
wm
的論點。此引數通常用於傳輸指標或控制代碼
在這個簡單的程式中,我們自己不處理任何訊息。但這並不意味著 Windows 也沒有。這就是為什麼必須呼叫包含預設視窗處理程式碼的 DefWindowProc
的原因。必須在每個視窗過程結束時呼叫此函式。
什麼是手柄?
一個手柄是代表一個唯一的物件的資料型別。它們是指標,但是由作業系統維護的祕密資料結構。這些結構的細節不需要我們關注。使用者需要做的只是使用 API呼叫建立/檢索控制代碼,並將其傳遞給採用該型別控制代碼的其他 API 呼叫。我們使用的唯一一種手柄是 CreateWindowEx
返回的 HWND
。
常量
在這個例子中,我們遇到了一些常量,這些常量都是全部大寫,並以 2 或 3 個字母字首開頭。 (Windows 型別也是全部大寫)
- IDI_APPLICATION:包含預設應用程式圖示的資源名稱。它與
LoadIcon
或LoadImage
(本例中為 LoadIcon)一起使用。 - IDC_ARROW:計算預設應用程式遊標的資源名稱。它與
LoadIcon
或LoadImage
(本例中為 LoadIcon)一起使用。 - WHITE_BRUSH:股票物件的名稱。這個庫存物件是白色畫筆。
- MB_ICONERROR:與
MessageBox
一起使用的標誌,用於顯示錯誤圖示。 - WS_EX_LEFT:預設的擴充套件視窗樣式。這會導致視窗具有左對齊屬性。
- WS_OVERLAPPEDWINDOW:一種視窗樣式,指示視窗應該是具有標題欄,大小框和頂級視窗典型的其他元素的父視窗。
- CW_USEDEFAULT:與
CreateWindowEx
的x
,y
,cx
或cy
引數一起使用。使 Windows 為傳遞CW_USEDEFAULT
的引數選擇有效值。
Windows 型別
在為 Windows 程式設計時,你將不得不習慣 Win32 型別,它們是內建型別的別名。這些型別都是全部大寫。此程式中使用的別名型別是:
- TCHAR:通用字元型別。如果定義了
UNICODE
,這是一個wchar_t
。其他,這是一個char
。 - UINT:無符號整數。用於表示視窗過程中的訊息識別符號以及其他用途。
- WPARAM:在 Win16 中,這是一個 WORD 引數(因此是
W
字首)。然而,隨著 Win32 的推出,現在這已經成為了一個傳播。這說明了這些 Windows 別名的重點; 他們在那裡保護計劃免受變化。 - LPARAM:這是一個
LONG
引數(Win64 中的LONG_PTR
)。 - PTSTR:
P
表示指標。T
表示通用字元,STR
表示字串。因此,這是一個指向TCHAR
字串的指標。其他字串型別包括:- LPTSTR:與
PTSTR
相同 - LPCTSTR:意為
const TCHAR *
- PCTSTR:與
LPCTSTR
相同 - LPWSTR:寬字串(
wchar_t *
) - LPCWSTR:意為
const wchar_t *
- PWSTR:與
LPWSTR
相同 - 還有更多正如你所看到的,Win32 型別可能很難理解,特別是有很多同義型別,這是 Win16 的工件。
- LPTSTR:與
- LRESULT:此型別用於表示視窗過程的返回值。它通常是一個長期(因此
L
)。