一、創建一個應用程序窗口
代碼如下:
// 頭文件
#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 函數從調用線程的消息隊列中取得一個消息
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;
}
二、程序分析
1.首先設計一個窗口類。
不懂可以看我的這篇文章:Win32 程序開發:窗口類結構體 WNDCLASS 和 WNDCLASSEX ,此處就不多贅述了。
2.注冊先前設計的窗口類。
調用 RegisterClassEx 函數注冊窗口。函數原型:
// 返回值(ATOM,原子):成功返回注冊的原子,失敗返回 0。
ATOM WINAPI RegisterClassExW(_In_ CONST WNDCLASSEXW *); // 參數:為我們設計的窗口類
3.創建窗口。
調用 CreateWindowEx 函數創建窗口。函數原型:
// 返回值:創建成功返回創建的窗口句柄,失敗返回NULL
HWND WINAPI CreateWindowExW(
_In_ DWORD dwExStyle, // 窗口擴展風格
_In_opt_ LPCWSTR lpClassName, // 我們注冊的窗口類的名稱
_In_opt_ LPCWSTR lpWindowName, // 窗口的名稱(說白了就是窗口的標題)
_In_ DWORD dwStyle, // 窗口創建的風格(跟dwExStyle不同)
_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); // 在WM_CREATE中進行傳遞的參數
4.更新和顯示窗口。
調用 ShowWindow 函數顯示窗口。函數原型:
// 返回值:成功返回非0,失敗返回0
BOOL WINAPI ShowWindow(
_In_ HWND hWnd, // 顯示的窗口句柄
_In_ int nCmdShow); // 窗口顯示的方式(全屏,最大化窗口,最小化窗口,······)
調用 UpdateWindow 函數更新窗口。函數原型:
// 返回值:成功返回非0,失敗返回0
BOOL WINAPI UpdateWindow(
_In_ HWND hWnd); // 更新的窗口
5.應用程序消息循環。
調用 GetMessage 函數從調用線程的消息隊列中取得一個消息。函數原型:
// 返回值:獲取錯誤返回-1,消息為WM_QUIT返回0,其他為非0
BOOL WINAPI GetMessageW(
_Out_ LPMSG lpMsg, // 接收從消息隊列中獲取的消息
_In_opt_ HWND hWnd, // 接收消息的窗口句柄
_In_ UINT wMsgFilterMin, // 指定被檢測的最小消息值
_In_ UINT wMsgFilterMax); // 指定被檢測的最大消息值
調用 TranslateMessage 函數將虛擬鍵消息轉換為字符消息。函數原型:
// 返回值:成功返回非0,失敗返回0
BOOL WINAPI TranslateMessage(
_In_ CONST MSG *lpMsg); // 接收的消息
調用 DispatchMessage 函數將消息分發到窗口處理。函數原型:
// 返回值:窗口處理過程的返回值
LRESULT WINAPI DispatchMessageW(
_In_ CONST MSG *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:附加消息
參考: