1.从一个简单地windows程序说起

窗口(window)是我们在windows编程中接触最频繁的对象。用户对窗口的操作(点击,键盘输入)都是以消息的形式传递给窗口,窗口间也是通过消息来和其他窗口进行通信。windows程序设计是以事件驱动方式的程序设计模式,主要是基于消息的。所以理解消息十分重要。

每一个windows应用程序开始执行后,系统都会为该程序创建一个消息队列,这个消息队列用来存放该程序创建的窗口的消息。

当消息投放到消息队列中后,应用程序则通过一个消息循环不断地从消息队列中取出消息,并进行响应。这种消息机制,就是windows程序运行的机制。

这里我搬来了windows程序设计第5版这本书中的示例代码,感兴趣的可以复制到本地,运行下,看下效果。

通过这段代码,我们可以大概了解下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:  // 退出确认;
        {
            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; // 注册消息处理函数;很重要;
    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函数,作为应用程序的入口点。函数参数hInstance为这个窗口的实例句柄,可以理解为是一个标识(资源),具有唯一性,由操作系统分配。通过这个标识,操作系统对其进行管理。

窗口总是基于窗口类来创建。可以理解窗口类其实就是要创建的窗口的配置,操作系统按照这个配置,创建出窗口。这里给出窗口类的定义,及各个字段的含义:

typedef struct _WNDCLASS {
UINT       style;
//指定这一类型窗口的样式,CS_HREDRAW水平重画,CS_VREDRAW垂直重画,CS_NOCLOSE 禁用Close命令,这将导致窗口没有关闭按钮。CS_DBLCLKS当用户在窗口中双击时,向窗口过程发送鼠标双击消息。CS(Class Style)
    
WNDPROC    lpfnWndProc; 
//函数指针,指向窗口过程函数,窗口过程函数就是一个回调函数。一个Windows程序可以包包含多个窗口过程函数,一个窗口过程总是与某一个特定的窗口相关联(通过本成员变量指定),基于该窗口类创建的窗口使用同一窗口过程。
    
int        cbClsExtra;
//可以为窗口类分配一个附加的内存空间,由属于这种窗口类的所有窗口所共享,我们一般将这个参数设置为0。
    
int        cbWndExtra;
//为窗口分配一个附加内存空间,应用程序可以用这部分内存存储窗口的特有的数据。初始化一般为0。如果应用程序用WNDCLASS结构注册对话框(用资源文件中的CLASS伪指令创建)必须给DLGWINDOWEXTRA设置这个成员。
    
HINSTANCE  hInstance;
//指令包含窗口过程的程序的实例句柄。
    
HICON      hIcon;
//指定窗口类的图标句柄。如果这个成员为NULL,那么系统提供一个默认的图标。在为hIcon变量赋值时可以调用LoadIcon函数加载一个图标资源,返回一个图标句柄。
    
HCURSOR    hCursor;
//为光标变量赋值时,可以用LoadCusrsor加载一个光标资源,返回光标句柄。用法同LoadIcon
    
HBRUSH     hbrBackground;
//指定窗口类的画刷句柄,当窗口发生重绘时,系统使用这里指定的画刷来擦除窗口背景。,既可以指定画刷句柄,也可以指定标准系统颜色值。GetStockObject得到系统标准刷。还可以获取画笔、字体和调色板句柄,因为反回多种资源对像句柄,使用时要根据具体情况,强制转换。
    
LPCTSTR    lpszMenuName;
//指定菜单资源的名字。如果为NULL那么基于这个窗口类创建的窗口将没有菜单。
    
LPCTSTR    lpszClassName;
//指定窗口类的名字。
} WNDCLASS;

RegisterClass函数的作用是通知系统,你要定义一个新的窗体类型,然后把这个类型记录到系统里面,以后你就可以使用CreateWindow来创建一个基于此类型的窗体。基于此类型的窗体都具有相同的属性,比如,背景色,光标,图标等等。

RegisterClass函数的作用是定义一个窗体类,相对于C++中的class概念,而CreateWindow这个函数是定义基于这个类型的对象,相对于C++中的对象概念。

注释为 window class name的参数是szAppName,其参数包含"HelloWin",即刚刚注册的窗口类的名称。我们所要创建的窗口正是通过这种方式和窗口类建立了关联。如果名字不一致,窗口是无法建立的。

    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);
    }

在windows程序中,消息是由MSG结构体来表示的。MSG结构体定义如下:

typedef struct tagMSG {

HWND  hwnd;
//消息所属的窗口

UINT   message;
//消息的标识符,消息由数值表识的不便记忆,所以定义为宏WM_XXX(WM是windows message的缩写)XXX对定消息英文大写,例如鼠标左键按下消息WM_LBUTTONDOWN,键盘按下消息WM_KEYDOWN,字符消息就是WM_CHAR。

WPARAM wParam;
LPARAM lParam;
//wParam和lParam用于指定消息的附加信息。例如收到一个字符消息时,message的成员变量就是WM_CHAR,但用户到底输入的到底什么字符,就由wParam和lParam来说明。

DWORD  time;
//消息投递到消息队列的时间。

POINT  pt;
//鼠标的当前位置。

} MSG;

在定义窗口类时,注册的回调函数LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);决定了窗口客户区的显示内容以及窗口如何对用户的输入做出响应。代码很简单,不再赘述。

至此我们就粗浅地了解了windows编程的知识,这是后面理解duilib的基础。

参考文献:

  1. windows程序设计第5版 (美 Charles Petzold著,方敏 张胜 梁路平 赵勇等译) 第三章
  2. B站孙鑫视频
  3. Windows程序内部运行机制_subleo的专栏-CSDN博客
  4. 关于RegisterClass和CreateWindow - 菊花也是花 - 博客园 (cnblogs.com)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容