1.從一個簡單地windows程序說起

窗口(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):

  1. windows程序設(shè)計第5版 (美 Charles Petzold著呻疹,方敏 張勝 梁路平 趙勇等譯) 第三章
  2. B站孫鑫視頻
  3. Windows程序內(nèi)部運(yùn)行機(jī)制_subleo的專欄-CSDN博客
  4. 關(guān)于RegisterClass和CreateWindow - 菊花也是花 - 博客園 (cnblogs.com)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末桶略,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子诲宇,更是在濱河造成了極大的恐慌际歼,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姑蓝,死亡現(xiàn)場離奇詭異鹅心,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)纺荧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門旭愧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宙暇,你說我怎么就攤上這事输枯。” “怎么了占贫?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵桃熄,是天一觀的道長。 經(jīng)常有香客問我型奥,道長瞳收,這世上最難降的妖魔是什么碉京? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮螟深,結(jié)果婚禮上谐宙,老公的妹妹穿的比我還像新娘。我一直安慰自己界弧,他們只是感情好凡蜻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著垢箕,像睡著了一般咽瓷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舰讹,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天茅姜,我揣著相機(jī)與錄音,去河邊找鬼月匣。 笑死钻洒,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的锄开。 我是一名探鬼主播素标,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼萍悴!你這毒婦竟也來了头遭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤癣诱,失蹤者是張志新(化名)和其女友劉穎计维,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體撕予,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鲫惶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了实抡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片欠母。...
    茶點(diǎn)故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖吆寨,靈堂內(nèi)的尸體忽然破棺而出赏淌,到底是詐尸還是另有隱情,我是刑警寧澤啄清,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布六水,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏缩擂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一添寺、第九天 我趴在偏房一處隱蔽的房頂上張望胯盯。 院中可真熱鬧,春花似錦计露、人聲如沸博脑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叉趣。三九已至,卻和暖如春该押,著一層夾襖步出監(jiān)牢的瞬間疗杉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工蚕礼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烟具,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓奠蹬,卻偏偏與公主長得像朝聋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子囤躁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容