建立一個視窗

#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。

如果 xycxcyCW_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:包含預設應用程式圖示的資源名稱。它與 LoadIconLoadImage(本例中為 LoadIcon)一起使用。
  • IDC_ARROW:計算預設應用程式遊標的資源名稱。它與 LoadIconLoadImage(本例中為 LoadIcon)一起使用。
  • WHITE_BRUSH:股票物件的名稱。這個庫存物件是白色畫筆。
  • MB_ICONERROR:與 MessageBox 一起使用的標誌,用於顯示錯誤圖示。
  • WS_EX_LEFT:預設的擴充套件視窗樣式。這會導致視窗具有左對齊屬性。
  • WS_OVERLAPPEDWINDOW:一種視窗樣式,指示視窗應該是具有標題欄,大小框和頂級視窗典型的其他元素的父視窗。
  • CW_USEDEFAULT:與 CreateWindowExxycxcy 引數一起使用。使 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 的工件。
  • LRESULT:此型別用於表示視窗過程的返回值。它通常是一個長期(因此 L)。