窗口(window)是我們在windows編程中接觸最頻繁的對象电禀。用戶對窗口的操作(點(diǎn)擊,鍵盤輸入)都是以消息的形式傳遞給窗口笤休,窗口間也是通過消息來和其他窗口進(jìn)行通信尖飞。windows程序設(shè)計是以事件驅(qū)動方式的程序設(shè)計模式,主要是基于消息的店雅。所以理解消息十分重要政基。
每一個windows應(yīng)用程序開始執(zhí)行后,系統(tǒng)都會為該程序創(chuàng)建一個消息隊列底洗,這個消息隊列用來存放該程序創(chuàng)建的窗口的消息腋么。
當(dāng)消息投放到消息隊列中后咕娄,應(yīng)用程序則通過一個消息循環(huán)不斷地從消息隊列中取出消息亥揖,并進(jìn)行響應(yīng)。這種消息機(jī)制圣勒,就是windows程序運(yùn)行的機(jī)制费变。
這里我搬來了windows程序設(shè)計第5版這本書中的示例代碼,感興趣的可以復(fù)制到本地圣贸,運(yùn)行下挚歧,看下效果。
通過這段代碼吁峻,我們可以大概了解下windows編程滑负。
/*------------------------------------------------------------
HELLOWIN.C -- Displays "Hello, Windows 98!" in client area
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
#include <stdio.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_CREATE:
return 0;
case WM_CHAR: // 按下鍵盤后,彈出對話框;
{
char szChar[20];
sprintf(szChar, "char is %d", wParam);
MessageBox(hwnd, szChar, "hello", MB_OK);
return 0;
}
case WM_PAINT: // 繪制主窗體
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
DrawText(hdc, TEXT("Hello, Windows 98!"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hwnd, &ps);
return 0;
case WM_LBUTTONDOWN: // 左鍵按下彈出對話框
{
MessageBox(hwnd, "mouse click", "test", MB_OK);
HDC hDC = GetDC(hwnd);
const char *str = "hello world";
TextOut(hDC, 0, 50, str, strlen(str));
ReleaseDC(hwnd, hDC);
return 0;
}
case WM_CLOSE: // 退出確認(rèn);
{
if (IDYES == MessageBox(hwnd, "你是否要退出在张?", "ddd", MB_YESNO))
DestroyWindow(hwnd);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("HelloWin");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc; // 注冊消息處理函數(shù);很重要;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance; //
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName; //
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"),
szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, // window class name
TEXT("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
在windows編程中,我們的入口是WinMain
函數(shù)矮慕,作為應(yīng)用程序的入口點(diǎn)帮匾。函數(shù)參數(shù)hInstance
為這個窗口的實(shí)例句柄,可以理解為是一個標(biāo)識(資源)痴鳄,具有唯一性瘟斜,由操作系統(tǒng)分配。通過這個標(biāo)識痪寻,操作系統(tǒng)對其進(jìn)行管理螺句。
窗口總是基于窗口類
來創(chuàng)建∠鹄啵可以理解窗口類
其實(shí)就是要創(chuàng)建的窗口的配置蛇尚,操作系統(tǒng)按照這個配置,創(chuàng)建出窗口猫态。這里給出窗口類的定義佣蓉,及各個字段的含義:
typedef struct _WNDCLASS {
UINT style;
//指定這一類型窗口的樣式,CS_HREDRAW水平重畫,CS_VREDRAW垂直重畫,CS_NOCLOSE 禁用Close命令,這將導(dǎo)致窗口沒有關(guān)閉按鈕亲雪。CS_DBLCLKS當(dāng)用戶在窗口中雙擊時勇凭,向窗口過程發(fā)送鼠標(biāo)雙擊消息。CS(Class Style)
WNDPROC lpfnWndProc;
//函數(shù)指針义辕,指向窗口過程函數(shù)虾标,窗口過程函數(shù)就是一個回調(diào)函數(shù)。一個Windows程序可以包包含多個窗口過程函數(shù)灌砖,一個窗口過程總是與某一個特定的窗口相關(guān)聯(lián)(通過本成員變量指定),基于該窗口類創(chuàng)建的窗口使用同一窗口過程璧函。
int cbClsExtra;
//可以為窗口類分配一個附加的內(nèi)存空間,由屬于這種窗口類的所有窗口所共享基显,我們一般將這個參數(shù)設(shè)置為0蘸吓。
int cbWndExtra;
//為窗口分配一個附加內(nèi)存空間,應(yīng)用程序可以用這部分內(nèi)存存儲窗口的特有的數(shù)據(jù)撩幽。初始化一般為0库继。如果應(yīng)用程序用WNDCLASS結(jié)構(gòu)注冊對話框(用資源文件中的CLASS偽指令創(chuàng)建)必須給DLGWINDOWEXTRA設(shè)置這個成員。
HINSTANCE hInstance;
//指令包含窗口過程的程序的實(shí)例句柄窜醉。
HICON hIcon;
//指定窗口類的圖標(biāo)句柄宪萄。如果這個成員為NULL,那么系統(tǒng)提供一個默認(rèn)的圖標(biāo)。在為hIcon變量賦值時可以調(diào)用LoadIcon函數(shù)加載一個圖標(biāo)資源榨惰,返回一個圖標(biāo)句柄拜英。
HCURSOR hCursor;
//為光標(biāo)變量賦值時,可以用LoadCusrsor加載一個光標(biāo)資源琅催,返回光標(biāo)句柄居凶。用法同LoadIcon
HBRUSH hbrBackground;
//指定窗口類的畫刷句柄虫给,當(dāng)窗口發(fā)生重繪時,系統(tǒng)使用這里指定的畫刷來擦除窗口背景侠碧。狰右,既可以指定畫刷句柄,也可以指定標(biāo)準(zhǔn)系統(tǒng)顏色值舆床。GetStockObject得到系統(tǒng)標(biāo)準(zhǔn)刷棋蚌。還可以獲取畫筆、字體和調(diào)色板句柄挨队,因?yàn)榉椿囟喾N資源對像句柄谷暮,使用時要根據(jù)具體情況,強(qiáng)制轉(zhuǎn)換盛垦。
LPCTSTR lpszMenuName;
//指定菜單資源的名字湿弦。如果為NULL那么基于這個窗口類創(chuàng)建的窗口將沒有菜單。
LPCTSTR lpszClassName;
//指定窗口類的名字腾夯。
} WNDCLASS;
RegisterClass
函數(shù)的作用是通知系統(tǒng)颊埃,你要定義一個新的窗體類型,然后把這個類型記錄到系統(tǒng)里面蝶俱,以后你就可以使用CreateWindow
來創(chuàng)建一個基于此類型的窗體班利。基于此類型的窗體都具有相同的屬性榨呆,比如罗标,背景色,光標(biāo)积蜻,圖標(biāo)等等闯割。
RegisterClass
函數(shù)的作用是定義一個窗體類,相對于C++中的class概念竿拆,而CreateWindow
這個函數(shù)是定義基于這個類型的對象宙拉,相對于C++中的對象概念。
注釋為 window class name的參數(shù)是szAppName
丙笋,其參數(shù)包含"HelloWin"谢澈,即剛剛注冊的窗口類的名稱。我們所要創(chuàng)建的窗口正是通過這種方式和窗口類建立了關(guān)聯(lián)不见。如果名字不一致澳化,窗口是無法建立的崔步。
hwnd = CreateWindow(szAppName, // window class name
TEXT("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
函數(shù)ShowWindow(hwnd, iCmdShow)
用于將窗口顯示在屏幕中稳吮。在調(diào)用UpdateWindow(hwnd);
后新建的窗口在屏幕中便完全可見了。此時井濒,程序必須能夠接收來自用戶的鍵盤和鼠標(biāo)的輸入灶似,這里通過循環(huán)列林,應(yīng)用程序來進(jìn)行消息的接收:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
在windows程序中,消息是由MSG結(jié)構(gòu)體來表示的酪惭。MSG結(jié)構(gòu)體定義如下:
typedef struct tagMSG {
HWND hwnd;
//消息所屬的窗口
UINT message;
//消息的標(biāo)識符希痴,消息由數(shù)值表識的不便記憶,所以定義為宏WM_XXX(WM是windows message的縮寫)XXX對定消息英文大寫春感,例如鼠標(biāo)左鍵按下消息WM_LBUTTONDOWN,鍵盤按下消息WM_KEYDOWN,字符消息就是WM_CHAR砌创。
WPARAM wParam;
LPARAM lParam;
//wParam和lParam用于指定消息的附加信息。例如收到一個字符消息時鲫懒,message的成員變量就是WM_CHAR嫩实,但用戶到底輸入的到底什么字符,就由wParam和lParam來說明窥岩。
DWORD time;
//消息投遞到消息隊列的時間甲献。
POINT pt;
//鼠標(biāo)的當(dāng)前位置。
} MSG;
在定義窗口類時颂翼,注冊的回調(diào)函數(shù)LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
決定了窗口客戶區(qū)的顯示內(nèi)容以及窗口如何對用戶的輸入做出響應(yīng)晃洒。代碼很簡單,不再贅述朦乏。
至此我們就粗淺地了解了windows編程的知識球及,這是后面理解duilib的基礎(chǔ)。
參考文獻(xiàn):
- windows程序設(shè)計第5版 (美 Charles Petzold著呻疹,方敏 張勝 梁路平 趙勇等譯) 第三章
- B站孫鑫視頻
- Windows程序內(nèi)部運(yùn)行機(jī)制_subleo的專欄-CSDN博客
- 關(guān)于RegisterClass和CreateWindow - 菊花也是花 - 博客園 (cnblogs.com)