【二】Windows API 零门槛编程指南——CreateWindow 窗口创建 “万字长篇专业术语全解”
本系列博文幾乎沒(méi)有難啃的“專業(yè)術(shù)語(yǔ)”,盡量讓讀者能夠看明白文章所述內(nèi)容,是本系列博文的核心宗旨之一。(由于本人也是由于項(xiàng)目需要,所以才來(lái)查閱相關(guān)資料,文中出現(xiàn)的錯(cuò)誤歡迎指出,共同進(jìn)步!謝謝!)
讀本系列博文的讀者必須具備以下的知識(shí)儲(chǔ)備:
- C/C++語(yǔ)言基礎(chǔ)語(yǔ)法及了解面向?qū)ο蟾拍?/strong>
窗口在 Windows 中指一個(gè)矩形區(qū)域,一般情況下這個(gè)區(qū)域是用戶與應(yīng)用程序交互的樞紐;上一小節(jié)使用 MessageBox 創(chuàng)建的簡(jiǎn)單窗口也是與用戶交互的一個(gè)窗口,該窗口的功能有限,只能夠簡(jiǎn)單的展示一些想要表達(dá)的信息,想創(chuàng)建一個(gè)能表達(dá)更多信息的窗口,可以使用 CreateWindow 函數(shù)創(chuàng)建。
開(kāi)始創(chuàng)建
創(chuàng)建 Windows 桌面應(yīng)用程序需要 windows.h,在頭部引入 windows.h 頭文件。
#include <windows.h>WinMain
在C語(yǔ)言中,每個(gè)C語(yǔ)言程序都有一個(gè)入口函數(shù),在Windows桌面程序中,這個(gè)入口函數(shù)是 WinMain ,具體聲明如下:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow);在程序中,緊接著在頭部文件后,我們使用 WinMain作為程序的入口函數(shù):
#include <windows.h> int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {}寫好入口函數(shù)后,必須要使用 RegisterClassEx 注冊(cè)一個(gè)新的窗口類型,再使用 CreateWindow 進(jìn)行創(chuàng)建。
WNDCLASSEX
在注冊(cè)新窗口前,我們可以使用一個(gè) WNDCLASSEX 結(jié)構(gòu)用來(lái)描述創(chuàng)建的Windows,這是窗口類;微軟開(kāi)發(fā)中心對(duì)WNDCLASSEXA的描述:“Contains window class information. It is used with the RegisterClassEx and GetClassInfoEx functions.”;WNDCLASSEXA 是包含窗口信息的結(jié)構(gòu)。語(yǔ)法如下:
typedef struct tagWNDCLASSEXA {UINT cbSize;UINT style;WNDPROC lpfnWndProc;int cbClsExtra;int cbWndExtra;HINSTANCE hInstance;HICON hIcon;HCURSOR hCursor;HBRUSH hbrBackground;LPCSTR lpszMenuName;LPCSTR lpszClassName;HICON hIconSm; } WNDCLASSEXA, *PWNDCLASSEXA, *NPWNDCLASSEXA, *LPWNDCLASSEXA;結(jié)構(gòu)成員:
- cbSize 窗口的大小:為 WNDCLASSEX 這個(gè)結(jié)構(gòu)的字節(jié)數(shù)大小,賦值為 sizeof(WNDCLASSEX)
- style 窗口的風(fēng)格:為該窗口的樣式,取值為 CS_HREDRAW | CS_VREDRAW
- lpfnWndProc 窗口處理指針:為指向窗體的的過(guò)程函數(shù),為指針,使用 WndProc 處理應(yīng)用程序在發(fā)生事件時(shí)從 Windows 接收的消息,以下將會(huì)講解 WndProc
- cbClsExtra 窗口類結(jié)構(gòu)后的附加字節(jié)數(shù),一般為0
- cbWndExtra 窗口事例后的附加字?jǐn)?shù),一般為0
- hInstance 當(dāng)前實(shí)例句柄,直接把WinMain參數(shù) hInstance(表示當(dāng)前實(shí)例句柄) 賦值給 hInstance 即可
- hIcon 圖標(biāo)的句柄,暫時(shí)賦值為NULL
- hCursor 光標(biāo)的句柄:使用 LoadCursor 加載光標(biāo),以下講解語(yǔ)法
- lpszClassName: 類別名稱的指針賦值為static TCHAR szWindowClass[] = _T("CSDN @1_bit");
- hIconSm: 窗口類關(guān)聯(lián)的小圖標(biāo),使用 LoadIcon函數(shù)加載,不過(guò)在文檔中提示,這個(gè)函數(shù)已過(guò)時(shí),可以使用 LoadImage 函數(shù)加載,本篇使用的是 LoadIcon ,LoadImage 后面再做補(bǔ)充;LoadIcon 函數(shù)語(yǔ)法將會(huì)在以下講解
- hbrBackground 背景畫刷的句柄,將會(huì)在以下給出設(shè)置的值參考
- lpszMenuName 指向菜單資源名的指針,為NULL即可
代碼實(shí)現(xiàn)如下:
WNDCLASSEX wcex;wcex.cbSize = sizeof(WNDCLASSEX);wcex.style = CS_HREDRAW | CS_VREDRAW;wcex.lpfnWndProc = WndProc;wcex.cbClsExtra = 0;wcex.cbWndExtra = 0;wcex.hInstance = hInstance;wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));wcex.hCursor = LoadCursor(NULL, IDC_CROSS);wcex.hbrBackground = (HBRUSH)(COLOR_ACTIVECAPTION);wcex.lpszMenuName = NULL;wcex.lpszClassName = szWindowClass;wcex.hIconSm = LoadIcon(NULL, MAKEINTRESOURCE(IDI_INFORMATION));——————————————————————————————————
WNDCLASSEX hbrBackground
值參考:
——————————————————————————————————
LoadCursor
LoadCursor 返回類型為 HCURSOR:的語(yǔ)法如下:
HCURSOR LoadCursorW(HINSTANCE hInstance,LPCWSTR lpCursorName );參數(shù)說(shuō)明:
- hInstance :可賦值當(dāng)前實(shí)例
- lpCursorName:要加載的游標(biāo)資源的名稱
在微軟的參考文檔中說(shuō)明,lpCursorName 的可設(shè)置為以下值:
——————————————————————————————————
lpfnWndProc
lpfnWndProc 為接收窗口處理的指針,使用 WndProc 處理應(yīng)用程序在發(fā)生事件時(shí)從 Windows 接收的消息。在微軟的文檔中寫道:“WndProc 是每個(gè) Windows 桌面應(yīng)用程序必須的窗口過(guò)程功能。 此函數(shù)通常命名為WndProc,但您可以隨心所欲地命名它。 例如,如果用戶在應(yīng)用程序中選擇"確定"按鈕,Windows 會(huì)向您發(fā)送消息,您可以在WndProc函數(shù)內(nèi)編寫代碼,執(zhí)行任何適當(dāng)?shù)牟僮鳌?這稱為處理事件。 您只處理與應(yīng)用程序相關(guān)的事件。WndProc 具有以下語(yǔ)法”;如下:。
LRESULT CALLBACK WndProc(_In_ HWND hWnd,_In_ UINT message,_In_ WPARAM wParam,_In_ LPARAM lParam );那我們?cè)诔绦蛑新暶饕踩绱寺暶?#xff0c;那么定義如下(使用微軟文檔示例):
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {PAINTSTRUCT ps;HDC hdc;TCHAR greeting[] = _T("Hello, 我是CSDN 1_bit 博客主頁(yè):https://me.csdn.net/A757291228 ");switch (message){case WM_PAINT:hdc = BeginPaint(hWnd, &ps);TextOut(hdc,5, 5,greeting, _tcslen(greeting));EndPaint(hWnd, &ps);break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hWnd, message, wParam, lParam);break;}return 0; }在以上 WndProc 的實(shí)現(xiàn)中,使用了 switch 語(yǔ)句,在 switch 中判斷了 WM_PAINT 消息;WM_PAINT 消息為繪制主窗體,在文檔中寫到:
要處理的一條重要信息是WM_PAINT消息。 當(dāng)必須更新其顯示W(wǎng)M_PAINT窗口的一部分時(shí),應(yīng)用程序?qū)⒔邮障ⅰ?br /> 當(dāng)用戶在窗口前面移動(dòng)窗口,然后再次將其移開(kāi)時(shí),可能會(huì)發(fā)生此事件。 您的應(yīng)用程序不知道這些事件何時(shí)發(fā)生。 只有 Windows
知道,因此它會(huì)通過(guò)消息WM_PAINT通知你的應(yīng)用。 首次顯示窗口時(shí),必須更新所有窗口。 要處理 WM_PAINT 消息,首先應(yīng)調(diào)用
BeginPaint,然后處理所有的邏輯以在窗口中布局文本、按鈕和其他控件,然后調(diào)用 EndPaint。
——————————————————————————————————
BeginPaint
BeginPaint 的語(yǔ)法為:
HDC BeginPaint(HWND hWnd,LPPAINTSTRUCT lpPaint );參數(shù)說(shuō)明:
- HWND:處理要重繪的窗口
- lpPaint:接收繪制的接收繪畫信息的 **PAINTSTRUCT**結(jié)構(gòu)的指針
——————————————————————————————————
EndPaint
該調(diào)用EndPaint函數(shù)標(biāo)記指定窗口畫的結(jié)束。每次調(diào)用BeginPaint函數(shù)都需要此函數(shù),但是僅在繪制完成之后。
語(yǔ)法:
BOOL EndPaint(HWND hWnd,const PAINTSTRUCT *lpPaint );參數(shù)說(shuō)明:
- hWnd:處理的窗口
- lpPaint:指向PAINTSTRUCT結(jié)構(gòu)的指針
——————————————————————————————————
PostQuitMessage
向系統(tǒng)指示線程已請(qǐng)求終止(退出)。通常用于響應(yīng)WM_DESTROY消息。
語(yǔ)法:
void PostQuitMessage(int nExitCode );參數(shù)說(shuō)明:
- nExitCode:應(yīng)用程序退出代碼。此值用作WM_QUIT消息的wParam參數(shù)。
——————————————————————————————————
DefWindowProc
調(diào)用默認(rèn)窗口過(guò)程以為應(yīng)用程序未處理的任何窗口消息提供默認(rèn)處理。此功能確保處理所有消息。DefWindowProc用窗口過(guò)程接收到的相同參數(shù)調(diào)用。
語(yǔ)法:
LRESULT LRESULT DefWindowProcA(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam );參數(shù)說(shuō)明:
- hWnd:窗口句柄
- Msg:消息
- wParam:附加消息
- lParam:附加消息信息
——————————————————————————————————
TextOut
所述的TextOut函數(shù)在指定位置寫入的字符串,利用當(dāng)前選擇的字體,背景顏色和文本顏色。
語(yǔ)法:
BOOL TextOutW(HDC hdc,int x,int y,LPCWSTR lpString,int c );參數(shù)說(shuō)明:
- hdc:上下文句柄
- x,y:對(duì)齊字符串的x,y坐標(biāo)
- lpString:字符串指針,指向字符串
- c:字符串長(zhǎng)度
——————————————————————————————————
HDC
引用文檔解釋:
HDC代碼中是設(shè)備上下文的句柄,這是 Windows 用于使應(yīng)用程序與圖形子系統(tǒng)通信的數(shù)據(jù)結(jié)構(gòu)。
WM_DESTROY
銷毀窗口時(shí)發(fā)送。從窗口中刪除窗口后,它將被發(fā)送到銷毀窗口的窗口過(guò)程。
此消息首先發(fā)送到被銷毀的窗口,然后發(fā)送到被銷毀的子窗口(如果有)。在處理消息期間,可以假定所有子窗口仍然存在。
WM_DESTROY 在 WndProc 函數(shù)中使用
——————————————————————————————————
補(bǔ)充
WM_CREATE
當(dāng)應(yīng)用程序通過(guò)調(diào)用CreateWindowEx或CreateWindow函數(shù)請(qǐng)求創(chuàng)建窗口時(shí)發(fā)送。(在函數(shù)返回之前發(fā)送消息。)在創(chuàng)建窗口之后,但在該窗口變?yōu)榭梢?jiàn)之前,新窗口的窗口過(guò)程會(huì)收到此消息。
——————————————————————————————————
RegisterClassEx
之后注冊(cè)該窗口,使用 RegisterClassEx:
RegisterClassEx(&wcex);注冊(cè)后使用 CreateWindow 進(jìn)行注冊(cè)的窗口創(chuàng)建語(yǔ)法如下:
HWND CreateWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HANDLE hInstance, PVOID lpParam );參數(shù)說(shuō)明:
- lpClassName:應(yīng)用程序窗體名
- lpWindowName:標(biāo)題名
- DWORD dwStyle:窗口類型風(fēng)格
- x,y:初始位置(x,y)
- nWidth, nHeight:初始尺寸
- hWndParent,:窗體父級(jí),可為NULL
- hMenu,:菜單欄,可為NULL
- hInstance:當(dāng)前實(shí)例
- lpParam:應(yīng)用程序使用,可為NULL
創(chuàng)建窗體:
HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 1000, 1000, NULL, NULL, hInstance, NULL);應(yīng)用窗體名為 szWindowClass:
static TCHAR szWindowClass[] = _T("win32 Demo");應(yīng)用窗體名為 szTitle:
static TCHAR szTitle[] = _T("This Win32");窗體風(fēng)格類型為:WS_OVERLAPPEDWINDOW
初始位置為:CW_USEDEFAULT,默認(rèn)左上角出現(xiàn)
尺寸為:1000, 1000
父級(jí)及菜單欄都為:NULL
hInstance為:當(dāng)前實(shí)例 hInstance
lpParam應(yīng)用程序使用為:NULL
代碼如下:
#include <windows.h> #include <tchar.h> static TCHAR szWindowClass[] = _T("CSDN @1_bit"); static TCHAR szTitle[] = _T("Win32 桌面應(yīng)用程序"); HINSTANCE hInst;LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {WNDCLASSEX wcex;wcex.cbSize = sizeof(WNDCLASSEX);wcex.style = CS_HREDRAW | CS_VREDRAW;wcex.lpfnWndProc = WndProc;wcex.cbClsExtra = 0;wcex.cbWndExtra = 0;wcex.hInstance = hInstance;wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));wcex.hCursor = LoadCursor(NULL, IDC_CROSS);wcex.hbrBackground = (HBRUSH)(COLOR_ACTIVECAPTION);wcex.lpszMenuName = NULL;wcex.lpszClassName = szWindowClass;wcex.hIconSm = LoadIcon(NULL, MAKEINTRESOURCE(IDI_INFORMATION));RegisterClassEx(&wcex);hInst = hInstance;HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, hInstance, NULL);} LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {PAINTSTRUCT ps;HDC hdc;TCHAR greeting[] = _T("Hello, 我是CSDN 1_bit 博客主頁(yè):https://me.csdn.net/A757291228 ");switch (message){case WM_PAINT:hdc = BeginPaint(hWnd, &ps);TextOut(hdc,5, 5,greeting, _tcslen(greeting));EndPaint(hWnd, &ps);break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hWnd, message, wParam, lParam);break;}return 0; }——————————————————————————————————
ShowWindow
完成以上代碼后,還需使用 ShowWindow 讓W(xué)indows窗體指定如何顯示,代碼如下:
ShowWindow(hWnd, nCmdShow);語(yǔ)法:
BOOL ShowWindow(HWND hWnd,int nCmdShow );參數(shù)說(shuō)明:
- hWnd:窗口句柄
- nCmdShow:窗口的顯示方式
nCmdShow 參考:
——————————————————————————————————
UpdateWindow
使用 UpdateWindow 發(fā)送 WM_PAINT 消息,更新指定窗口。
語(yǔ)法:
參數(shù):
- hWnd:窗口句柄
整體代碼如下:
#include <windows.h> #include <tchar.h> static TCHAR szWindowClass[] = _T("CSDN @1_bit"); static TCHAR szTitle[] = _T("Win32 桌面應(yīng)用程序"); HINSTANCE hInst;LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {WNDCLASSEX wcex;wcex.cbSize = sizeof(WNDCLASSEX);wcex.style = CS_HREDRAW | CS_VREDRAW;wcex.lpfnWndProc = WndProc;wcex.cbClsExtra = 0;wcex.cbWndExtra = 0;wcex.hInstance = hInstance;wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));wcex.hCursor = LoadCursor(NULL, IDC_CROSS);wcex.hbrBackground = (HBRUSH)(COLOR_ACTIVECAPTION);wcex.lpszMenuName = NULL;wcex.lpszClassName = szWindowClass;wcex.hIconSm = LoadIcon(NULL, MAKEINTRESOURCE(IDI_INFORMATION));RegisterClassEx(&wcex);hInst = hInstance;HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, hInstance, NULL);ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);return 0; }LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {PAINTSTRUCT ps;HDC hdc;TCHAR greeting[] = _T("Hello, 我是CSDN 1_bit 博客主頁(yè):https://me.csdn.net/A757291228 ");switch (message){case WM_PAINT:hdc = BeginPaint(hWnd, &ps);TextOut(hdc,5, 5,greeting, _tcslen(greeting));EndPaint(hWnd, &ps);break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hWnd, message, wParam, lParam);break;}return 0; }運(yùn)行程序,發(fā)現(xiàn)出現(xiàn)了一閃而過(guò)的窗口,這個(gè)很像剛學(xué)習(xí)C語(yǔ)言的時(shí)候,沒(méi)有加上停止;那我們就循環(huán)偵聽(tīng) Windows 發(fā)送的消息即可:
MSG msg; while (GetMessage(&msg, NULL, 0, 0)) {TranslateMessage(&msg);DispatchMessage(&msg); }return (int) msg.wParam;——————————————————————————————————
GetMessage
GetMessage
從調(diào)用線程的消息隊(duì)列中檢索消息。該函數(shù)分派傳入的已發(fā)送消息,直到已發(fā)布的消息可供檢索為止。
語(yǔ)法:
BOOL GetMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax );參數(shù)說(shuō)明:
- lpMsg:指向
MSG
結(jié)構(gòu)的指針,該結(jié)構(gòu)從線程的消息隊(duì)列接收消息信息。 - hWnd:獲取消息的的窗口句柄,文檔中解釋到:“如果hWnd為NULL,則GetMessage檢索屬于當(dāng)前線程的任何窗口的消息,以及當(dāng)前線程的消息隊(duì)列中hwnd值為NULL的消息(請(qǐng)參閱MSG結(jié)構(gòu))。因此,如果hWnd為NULL,則將同時(shí)處理窗口消息和線程消息。”
- wMsgFilterMin,wMsgFilterMax:要檢索的最低、最高消息值的整數(shù)值“**
- wMsgFilterMin 和 wMsgFilterMax 都為零,則 GetMessage 返回所有可用消息**”
——————————————————————————————————
完整代碼
#include <windows.h> #include <tchar.h> static TCHAR szWindowClass[] = _T("CSDN @1_bit"); static TCHAR szTitle[] = _T("Win32 桌面應(yīng)用程序"); HINSTANCE hInst;LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {WNDCLASSEX wcex;wcex.cbSize = sizeof(WNDCLASSEX);wcex.style = CS_HREDRAW | CS_VREDRAW;wcex.lpfnWndProc = WndProc;wcex.cbClsExtra = 0;wcex.cbWndExtra = 0;wcex.hInstance = hInstance;wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));wcex.hCursor = LoadCursor(NULL, IDC_CROSS);wcex.hbrBackground = (HBRUSH)(COLOR_ACTIVECAPTION);wcex.lpszMenuName = NULL;wcex.lpszClassName = szWindowClass;wcex.hIconSm = LoadIcon(NULL, MAKEINTRESOURCE(IDI_INFORMATION));RegisterClassEx(&wcex);hInst = hInstance;HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, hInstance, NULL);ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);MSG msg;while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return (int)msg.wParam; }LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {PAINTSTRUCT ps;HDC hdc;TCHAR greeting[] = _T("Hello, 我是CSDN 1_bit 博客主頁(yè):https://me.csdn.net/A757291228 ");switch (message){case WM_PAINT:hdc = BeginPaint(hWnd, &ps);TextOut(hdc,5, 5,greeting, _tcslen(greeting));EndPaint(hWnd, &ps);break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hWnd, message, wParam, lParam);break;}return 0; }運(yùn)行結(jié)果如下:
總結(jié)
以上是生活随笔為你收集整理的【二】Windows API 零门槛编程指南——CreateWindow 窗口创建 “万字长篇专业术语全解”的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【一】Windows API 零门槛编程
- 下一篇: 「零门槛多语言 Python/C/C#