1. 程式人生 > >Win32 程式開發:建立一個應用程式視窗

Win32 程式開發:建立一個應用程式視窗

1)簡單介紹建立應用程式的步驟

1.設計一個視窗類

2.註冊這個視窗類

3.建立應用程式視窗

4.更新顯示視窗

5.應用程式訊息迴圈

2)下面根據這個步驟進行建立一個應用程式視窗吧


/* 標頭檔案 */
#include <windows.h>

/* 全域性變數 */
WCHAR g_lpszClassName[] = L"CLASSNAME";
WCHAR g_lpszWindowName[] = L"哈嘍,新的征程";

/* 函式宣告 */
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

/* 應用程式主函式 */
INT APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, INT nCmdShow)
{
    /* 1.設計一個視窗類 */
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;        
    wcex.cbClsExtra = 0;    
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.lpfnWndProc = WndProc;
    wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = g_lpszClassName;
    wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    /* 2.註冊這個視窗類 */
    if (RegisterClassEx(&wcex) == ((ATOM)0))
    {
        MessageBox(NULL, L"註冊視窗類失敗!", L"錯誤", MB_YESNO | MB_ICONERROR);
        exit(-1);
    }

    /* 3.建立視窗 */
    HWND hWnd = CreateWindowEx(NULL, g_lpszClassName, g_lpszWindowName, WS_OVERLAPPEDWINDOW, 10, 10, 800, 800, NULL, NULL, hInstance, NULL);
    if (hWnd == NULL)
    {
        MessageBox(NULL, L"建立視窗失敗!", L"錯誤", MB_YESNO | MB_ICONERROR);
        exit(-1);
    }

    /* 4.更新顯示視窗 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    /* 5.應用程式訊息迴圈 */
    MSG msg = { 0 };
    BOOL bRet;
    /* GetMessage 發生錯誤會返回-1 */
    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
    {
        if (bRet != -1)
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return msg.wParam;
}

/* 應用程式訊息處理回撥函式 */
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

下面進行講解上面的程式:

2.註冊這個設計的視窗類呼叫RegisterClassEx函式進行註冊

3.進行建立視窗,呼叫CreateWindowEx函式進行建立

4.更新顯示此視窗,分別呼叫ShowWindow(顯示視窗)和UpdateWindow(更新視窗)

5.應用程式訊息迴圈,通過while迴圈

6.視窗訊息處理過程

1.設計一個視窗類(...)

2.註冊這個設計的視窗類

①RegisterClassEx函式講解(註冊視窗類函式,winuser.h中提供的函式)

函式原型:

ATOM WINAPI RegisterClassExW(_In_ CONST WNDCLASSEXW *);

_In_ CONST WNDCLASSEXW *: 為我們設計的視窗類

返回值(ATOM也就是原子的意思):成功返回註冊的原子,失敗返回0

②示例中的使用

所以我們只需要通過返回值判斷是否為註冊失敗,註冊失敗返回0,0也就是false

if (RegisterClassEx(&wcex) == ((ATOM)0)) { 提示註冊失敗資訊 }

3.進行建立視窗

①CreateWindowEx函式講解(註冊視窗類函式,winuser.h中提供的函式)

函式原型:(用點多,希望認真看完)

HWND WINAPI CreateWindowExW(
    _In_ DWORD dwExStyle,
    _In_opt_ LPCWSTR lpClassName,
    _In_opt_ LPCWSTR lpWindowName,
    _In_ DWORD dwStyle,
    _In_ int X,
    _In_ int Y,
    _In_ int nWidth,
    _In_ int nHeight,
    _In_opt_ HWND hWndParent,
    _In_opt_ HMENU hMenu,
    _In_opt_ HINSTANCE hInstance,
    _In_opt_ LPVOID lpParam);

dwExStyle:                視窗擴充套件風格

lpClassName:           我們註冊的視窗類的名稱

lpWindowName:       視窗的名稱(說白了就是視窗的標題)

dwStyle:                    視窗建立的風格(跟dwExStyle不同哦)

X:                               視窗左上角的點位於螢幕的橫座標

Y:                               視窗左上角的點位於螢幕的縱座標

nWidth:                     視窗的寬度

nHeight:                    視窗的高度

hWndParent:            視窗所有者的控制代碼

hMenu:                      選單控制代碼

hInstance:                 所在模組的例項控制代碼

lpParam:                    在WM_CREATE中進行傳遞的引數

返回值:                       建立成功返回建立的視窗控制代碼,失敗返回NULL

②示例中的使用 

HWND hWnd = CreateWindowEx(NULL, g_lpszClassName, g_lpszWindowName, WS_OVERLAPPEDWINDOW, 
                            10, 10, 800, 800, NULL, NULL, hInstance, NULL);

視窗的擴充套件風格為NULL,也就是沒有擴充套件風格

視窗類的名稱是g_lpszClassName("CLASSNAME")

視窗名稱是g_lpszClassName("哈嘍,新的征程")

視窗的風格為WS_OVERLAPPEDWINDOW

視窗的位於螢幕的橫縱座標為(10, 10)

視窗的寬和高為(800, 800)

視窗的所有者為NULL,也就是沒有所有者

視窗的選單控制代碼為NULL,也就是沒有選單

視窗的所在模組的例項控制代碼,為作業系統在WinMain中傳遞過來的示例控制代碼

在WM_CREATE中傳遞的引數為NULL,也就是傳遞一個NULL的引數

最後通過返回值進行判斷是否建立成功:

if (hWnd == NULL) { 建立視窗失敗的提示資訊 }

4.更新顯示此視窗

①ShowWindow函式講解(顯示視窗,winuser.h中提供的函式)

函式原型:

BOOL WINAPI ShowWindow(
    _In_ HWND hWnd,
    _In_ int nCmdShow);

hWnd:顯示的視窗控制代碼

nCmdShow:視窗顯示的方式(全屏,最大化視窗,最小化視窗,······)

返回值:成功返回非0,失敗返回0

②UpdateWindow函式講解(顯示視窗,winuser.h中提供的函式)

函式原型:

BOOL WINAPI UpdateWindow(
    _In_ HWND hWnd);

hWnd:更新的視窗

返回值:成功返回非0,失敗返回0

③示例中的使用

ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

hWnd為CreateWindowEx中建立的視窗

nCmdShow為視窗的顯示的方式(在WinMain由作業系統傳入)

5.應用程式訊息迴圈

①GetMessage函式講解(從呼叫執行緒的訊息佇列中取得一個訊息,winuser.h中提供的函式)

函式原型:

BOOL WINAPI GetMessageW(
    _Out_ LPMSG lpMsg,
    _In_opt_ HWND hWnd,
    _In_ UINT wMsgFilterMin,
    _In_ UINT wMsgFilterMax);

lpMsg:接收從訊息佇列中獲取的訊息

hWnd:接收訊息的視窗控制代碼

wMsgFilterMin:指定被檢測的最小訊息值

wMsgFilterMax:指定被檢測的最大訊息值

返回值:獲取錯誤返回-1,訊息為WM_QUIT返回0,其他為非0

②TranslateMessage函式講解(將虛擬鍵訊息轉換為字元訊息,winuser.h中提供的函式)

函式原型:

BOOL WINAPI TranslateMessage(
    _In_ CONST MSG *lpMsg);

lpMsg:接收的訊息

返回值:成功返回非0,失敗返回0

③DispatchMessage函式講解(將訊息分發到視窗處理,winuser.h中提供的函式)

函式原型:

LRESULT WINAPI DispatchMessageW(
    _In_ CONST MSG *lpMsg);

lpMsg:分發的訊息

返回值:視窗處理過程的返回值

④示例中的使用

while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
    {
        if (bRet != -1)
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

while迴圈的結束,當GetMessage獲取到的訊息為WM_QUIT視窗退出,則返回0(false) 當GetMessage的返回值不為-1(獲取訊息失敗)的時候,進行虛擬鍵轉換和分發訊息

6.視窗訊息處理過程

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }

    return 0;
}

引數:

hWnd:      視窗處理過程的視窗控制代碼

message:訊息ID

wParam:  附加訊息

lParam:    附加訊息

①PostQuitMessage(執行緒終止請求,winuser.h中提供的函式)

函式原型:

VOID WINAPI PostQuitMessage(
    _In_ int nExitCode);

nExitCode:程式退出程式碼

②DefWindowProc(預設的視窗處理過程,winuser.h中提供的函式)

函式原型:

LRESULT CALLBACK DefWindowProcW(
    _In_ HWND hWnd,
    _In_ UINT Msg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam);

引數和返回值跟我們的視窗處理過程一樣

這個函式的意義就是我們不想處理的訊息就交給他吧

小插曲:

return msg.wParam

其實這個跟main的return是一樣的,但對於初學者會有疑問,為什麼不是return 0

解釋部分:

當GetMessage獲取到的訊息為WM_DESTROY(銷燬視窗)的時候呼叫我們的處理:

case WM_DESTROY:
    PostQuitMessage(0);
    break;

當我們呼叫PostQuitMessage傳入的nExitCode(程式退出程式碼,0為正常退出,非0為異常)

呼叫了PostQuitMessage後會向訊息佇列中新增一個WM_QUIT(視窗退出訊息)

然後GetMessage接收到WM_QUIT訊息的MSG

MSG的wParam為PostQuitMessage中的nExitCode

所以最後return的值也就是PostQuitMessage的nExitCode