学习笔记——Windows消息机制

学习笔记——Windows消息机制与程序结构

一条消息的一生

我是一条消息。随着鼠标在按钮上的一次点击,我出生了,刚睁开眼,身上就被塞了一张证件,上面是我的身份信息:

  • hwnd: 我出生的窗口的句柄,也可以说是我的户籍地址
  • message: 我的身份证号,独一无二的标识了我的身份
  • wParam和lParam: 出生时附加的一些信息。不同消息对应的内容不一定相同,在我这里存储的是被点击的按钮id
  • time: 我的出生日期
  • pt: 我出生时鼠标光标所处的位置

看我还在发愣,给我发证件的鼠标驱动程序急了:“愣着干嘛,一会赶不上消息处理了,还不快去系统队列报到!”

我连忙灰头土脸地赶去排队。到了地方抬头一看,前面已经排了很多各种类型的消息,其中最常见的是窗口消息,他们是由操作系统和控制其他窗口的窗口所使用的消息。其次是命令消息,这是一些特殊的窗口消息,用来处理一个窗口发送到另一个窗口的用户请求。最后是像我一样的控件通知消息:由操作某个控件(如单击一个按钮)所产生的消息。

看着长长的队列,我又迷茫了。好在前面的老哥及时替我解惑:

“这里是windows老大维护的系统消息队列,集中了来自各个窗口、各个线程的消息,轮到我们的时候,老大会根据身份证件来确定将我们送到哪个窗口,然后我们还要在窗口对应的线程消息队列排队,并由对应的线程决定哪一个窗口过程(消息处理函数)来处理我们。”

image

“这么说所有消息都要经过‘系统消息队列——线程消息队列——窗口过程’这个流程才能被处理呀” 虽然过程繁琐了点,不过大家都一样,也就不觉得难以忍受。

“呵呵,你太天真了,只有我们这些用PostMessage函数发送的队列消息要排这么久的队,有一些关系户,用SendMessage函数发送,叫非队列消息,直接就能被送到窗口过程那里,什么队都不需要排!”

image

还有这种事?我叹了口气,只能耐心等待。


还好我所在的计算机CPU比较快,没等太久就排到我了,操作系统查看了我的证件后,又把我送回了出生的那个应用程序。这里的队列就短多了,我很快就排完了队,进入了WinMain函数,这里有一个消息循环

    MSG msg;//这个msg用来存放我
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);//用来将虚拟键消息转换为字符消息,这是键盘消息需要用的,与我无关
        DispatchMessage(&msg);//将我分派给窗口的消息处理函数,即WndProc函数
    }

WndProc(消息处理)函数

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId;
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)//这里分出三条岔路
    {
    case WM_COMMAND://我走这一条路
        wmId    = LOWORD(wParam);//解读出我wParam中蕴含的信息:被点击的按钮ID

        switch (wmId)
        {
        case ID_BUTTON_DRAW://如果是ID_BUTTON_DRAW按钮被点击
            OnButtonWhite();//调用这个函数,让背景变白
            break;
        case ID_BUTTON_SWEEP://如果是ID_BUTTON_SWEEP被点击
            OnButtonGray();//调用这个函数,让背景变灰
            break;
        default://如果都不是,调用系统默认的消息处理函数,以免消息无人处理,产生bug
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_PAINT://用来绘制窗口的消息
        hdc = BeginPaint(hWnd, &ps);
        OnDraw(hdc);
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY://用来关闭窗口的消息
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

我对应的按钮是ID_BUTTON_DRAW,这意味这需要调用OnButtonWhite()函数,让窗口变成白色。

到了这一步,我的使命也就结束了,剩下的工作就交给绘图程序去做。

回想我的一生,真如白驹过隙,忽然而已。

一个简单的win32程序的结构

image.png

全局变量:

HINSTANCE hInst;								                            // 当前实例
TCHAR szTitle[MAX_LOADSTRING] = TEXT("Message process");					// 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING] = TEXT("AppTest");			            // 主窗口类名

WinMain函数:

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
/*
参数解释:
HINSTANCE hInstance:    当前实例的句柄。
HINSTANCE hPrevInstance  前一个实例的句柄。在Win32环境下,这个参数不再起作用。
LPSTR lpCmdLine:         一个指向字符串的指针,表示一个命令行参数,即C或C++中的main函数中的参数char *argv[]。
int nCmdShow:           窗口显示形式,最大化显示、最小化显示、正常大小显示还是隐藏显示。

APIENTRY:表明此函数是应用程序的入口点 详见 https://blog.csdn.net/guoyong10721073/article/details/52399823
*/
{
    // 注册窗口类
    if(!AppRegisterClass(hInstance))
    {
         return FALSE;
    }
    // 初始化应用程序窗口
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    // 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);//用来把键盘事件的虚拟键消息转换为字符消息。
        DispatchMessage(&msg);//把消息发送到窗口的消息处理函数,此函数在窗口注册时已经指定。
    }
    return (int) msg.wParam;
}

注册窗口类:

ATOM AppRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style          = CS_HREDRAW | CS_VREDRAW;                  //指定窗口类的类型
    wcex.lpfnWndProc    = WndProc;                                  //指定消息处理函数,是一个回调函数, 需要自己定义
    wcex.cbClsExtra     = 0;                                        //类的附加内存,通常数情况下为0
    wcex.cbWndExtra     = 0;                                        //窗口附加内存,通常情况下为0
    wcex.hInstance      = hInstance;                                //当前实例句柄,用WinMain中的形参给它赋值
    wcex.hIcon          = LoadIcon(NULL, IDI_APPLICATION);          //图标句柄,用于指示应用程序所用的是什么图标
    wcex.hIconSm        = NULL;
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);              //光标句柄,用于指示鼠标进入应用程序窗口区域时的显示
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);                 //用于指示程序的背景颜色
    wcex.lpszMenuName   = NULL;                                     //指定菜单的名字
    wcex.lpszClassName  = g_szWindowClass;                          //指定类的名字

    return RegisterClassEx(&wcex);
}

保存实例句柄并创建主窗口:

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    HWND hWnd;
    // 将实例句柄存储在全局变量中
    hInst = hInstance; 

    //创建窗口,详见 https://blog.csdn.net/lizijie7471619/article/details/51058095
    hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

    if (!hWnd)
    {
        return FALSE;
    }

    ShowWindow(hWnd, nCmdShow);//显示窗口
    UpdateWindow(hWnd);//更新窗口

    return TRUE;
}

消息循环与消息处理函数:

参见上文《一条消息的一生》点击跳转

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值