• Windows编程系列:Windows中的消息


    win32控制台程序

    控制台程序整个执行过程是按照代码的顺序依次执行,到main函数的结束,标志着整个程序的退出。

    1 int main()
    2 {
    3     
    4     return 0;
    5 }

    整个过程可以描述为以下:

    Windows应用程序

    Windows应用程序会响应来自用户和操作系统的事件。

    来自用户的事件包括:鼠标单击,按键,触摸屏手势等。
    操作系统中的事件:用户可能插入了新的硬件设备,或者Windows可能进入了低功耗状态(睡眠或休眠)等。

    这些事件可以在程序运行时随时以几乎任何顺序发生,为了解决此问题,Windows使用了消息传递模型。 操作系统通过向其传递消息(https://www.cnblogs.com/zhaotianff/p/11285312.html)来与您的应用程序窗口通信。 消息只是指定特定事件的数字代码。

    例如,如果用户按下鼠标左键,则窗口会收到一条包含以下消息代码的消息。

    1 #define WM_LBUTTONDOWN    0x0201

    消息循环

    https://www.cnblogs.com/zhaotianff/p/11297319.html这篇文章中,介绍了如何创建一个Windows窗体应用程序。在步骤5中,创建了一个消息循环。

    Windows窗体程序在运行后,将会收到无数的消息(鼠标移动和按下,键盘按下都会产生消息)。

    一个窗体程序可能包含多个窗口,每个窗口都有自己的窗口过程(WNDPROC)。 应用程序需要一个循环来检索消息并将它们发送到对应的窗口。

    对于每个创建窗口的线程,操作系统都会为窗口消息创建一个队列。 此队列保存在该线程上创建的所有窗口的消息。通过调用GetMessage函数从队列中提取消息。

    1 BOOL GetMessage(
    2   LPMSG lpMsg,
    3   HWND  hWnd,
    4   UINT  wMsgFilterMin,
    5   UINT  wMsgFilterMax
    6 );

    取出消息后,需要对消息进行转换。这个时候就需要调用TranslateMessage()函数,该函数将虚拟消息转换为字符消息。字符消息被送到调用线程的消息队列里,当下一次线程调用函数GetMessage()时被读出。

    然后再调用DispatchMessage函数将消息分发到各个窗口,DispatchMessage函数告诉操作系统调用消息目标窗口的窗口过程。 也就是说,操作系统在其窗口列表里查找窗口句柄,找到与该窗口关联的函数指针,然后调用该函数。

    假设鼠标按下,将会发生如下事件:

    1、操作系统将WM_LBUTTONDOWN消息放入消息队列

    2、消息循环时,调用GetMessage函数取出,并将从消息队列中取出的消息将保存在MSG结构体对象中

    3、程序调用TranslateMessage函数和DispatchMesage函数

    4、在DispatchMessage函数内部,调用目标窗口的窗口过程

    5、窗口对消息进行处理或忽略

    当目标窗口的窗口过程执行完成后,将会返回DispatchMessage函数,此时,再会循环读取下一次消息进行处理。

    只要程序没有退出GetMessage函数返回非0值),这个循环会一直执行下去。

     while (GetMessage(&msg, nullptr, 0, 0))
        {
            if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }

    结束消息循环

    当需要结束消息循环时,可以执行以下函数

    1 PostQuitMessage(0);

    PostQuitMessage函数实际上是发送了一个WM_QUIT消息到消息队列中,这会使GetMessage()返回返回0。当GetMessage函数返回0时,整个消息循环结束。WM_QUIT消息不会被DispatchMessage函数传递到窗口过程,所以不需要对WM_QUIT消息进行处理。

    PostMessage和SendMessage

    PostMessage

    将消息复制到指定窗口的线程关联的消息队列中,并直接返回。无需等待线程处理消息。如果未指定窗口句柄,则将消息复制到当前线程关联的消息队列中。

    使用PostMessage发送消息时,需要判断函数返回值,以便知道消息是否正确发送。如果消息队列满了,PostMessage会返回失败,此时需要对消息进行重发。

    SendMessage

    将指定的消息发送到一个或多个窗口,直到消息被窗口的窗口过程处理完成后才返回。这种方式会要求窗口过程立即处理该消息,而不是从消息队列中去取出消息再进行处理。在进行窗口通信时,常会使用这种方式

    消息和事件

    消息(Message)就是用于描述某个事件所发生的信息,而事件(Event)则是用户操作而产生的动作(如鼠标按下)。事件产生消息,而消息可以来自事件,也可以来自其它,如SendMessage

    事件:

    消息:

    系统预定义消息

    系统预定义消息可以分为三类:

    窗口消息(Windows Messages)

    窗口消息用于窗口的内部动作,如创建窗口消息(WM_CREATE)、绘制窗口消息(WM_PAINT)、销毁窗口(WM_DESTROY)等。窗口消息可以用于一般窗口,也可以用于对话框、控件等。

    常用的窗口消息如下:

    消息   说明
    WM_CREATE 窗口创建消息,其中wParam不用,lParam指向CREATESTRUCT结构体的指针,该结构体包含将要创建的窗口的消息
    WM_PAINT 窗口绘制消息,调用函数UpdateWindow或RedrawWindow,会发送该消息。wParam和lParam参数无用
     WM_DESTROY 销毁窗口消息,wParam和lParam无用 
     WM_SETFOCUS 对于即将获取焦点的窗口,会收到WM_SETFOCUS消息,wParam参数是正在失去焦点的窗口的句柄 
     WM_KILLFOCUS 焦点消息,对于正在失去焦点的窗口,会收到WM_KILLFOCUS消息,wParam参数是即将接收输入焦点的窗口的句柄
     WM_MEASUREITEM  当具有自画风格的COMBOBOX、LISTBOX、LISTVIEW控件或MENUITEM被创建的时候,该消息会发送给这些控件或菜单的拥有者窗口,用来设置这些控件或菜单的每个项的大小。比如LISTBOX框如果具有LBS_OWNERDRAWFIXED风格,那么它被创建的时候,系统会发送WM_MEASUREITEM消息来设置每个项(每行)的高度,如果没有LBS_OWNERDRAWFIXED风格,则不会发送WM_MEASUREITEM消息。消息参数wParam的值为控件ID;lParam指向MEASUREITEMSTRUCT结构体的指针,该结构体包含自画控件或菜单项的尺寸信息
     WM_DRAWITEM  当具有自画风格的COMBOBOX、LISTBOX、LISTVIEW控件或MENUITEM的外观发生改变时(即需要重画)的时候,该消息会发送给这些控件或菜单的拥有者窗口,收到此消息之后控件才会执行重画。比如LISTBOX如果具有LBS_OWNERDRAWFIXED风格,那么它需要重画的时候,系统会发送WM_DRAWITEM消息来重画每项(每行),如果没有LBS_OWNERDRAWFIXED风格,则不会发送WM_DRAWITEM消息。消息参数wParam的值为控件ID;lParam指向DRAWITEMSTRUCT结构体的指针,该结构体为需要自绘的控件或菜单项提供了必须的信息

    命令消息(Command Messages)

    命令消息用于处理用户请求,如用户单击菜单项或工具栏按钮时,就会产生命令消息。命令消息的形式是WM_COMMAND,消息参数wParam的低字节LOWORD(wParam),表示菜单项或工具栏按钮。

    WM_COMMAND也可以作为标准控件的控件通知消息。如果消息参数lParam为NULL,则WM_COMMAND是一个命令消息,否则WM_COMMAND是一个标准控件通知消息。lParam值就是控件句柄值。标准控件可以参考推荐阅读中的链接。

    控件通知消息

    控件通知就是控件消息通知其父窗口,控件通知消息分为三种

    1.作为窗口消息的子集

    它主要发生在:

    控件窗口在创建或销毁之前,会发送WM_PARENTNOTIFY消息给它的父窗口;控件窗口绘制自身窗口的消息,比如WM_CTLCOLOR、WM_DRAWITEM、WM_MEASUREITEM、WM_DELETEITEM、WM_CHARTOITEM、WM_VKTOITEM、WM_COMMAND和COMPAREIITEM;由滚动条控件发送,通知其父窗口滚动的消息,如WM_VSCROLL和WM_HSCROLL。

    2.WM_COMMAND形式

    这种控件通知的方式是利用命令消息,向父窗口发送WM_COMMAND消息,但只有标准控件才采用这种方式,如Button、Edit和ListBox等

    在WM_COMMAND中,lParam用来区分是命令消息还是控件通知消息。如果消息参数为NULL,则是命令消息,否则为控件通知消息。为消息为控件通知消息是,lParam是控件的句柄,LOWORD(wParam)表示控件的ID,HIWORD(wParam)表示控件通知码,标记控件所发生的各种事件,如Button的通知码如下:

    控件 通知码 说明
    Button BN_CLICKED 用户单击了按钮
    BN_DISABLE 按钮被禁止
    BN_DOUBLECLICKED 用户双击了按钮
    BN_HILITE 用户选择了按钮
    BN_UNHILITE 按钮高亮应该被移除
    BN_PAINT 按钮应当重画

    可以访问推荐阅读中的标准控件链接,然后找到每个控件的通知(Notifications)部分文档来查看完整的通知码。

    3.WM_NOTIFY形式

    上面说到标准控件发送信息给父窗口是通过WM_COMMAND,除此之外的通用控件(如TreeView、ListView)发送给父窗口的消息是WM_NOTIFY。单击或双击一个通用控件或在通用控件中选择部分文本、操作通用控件的滚动条,都会产生一个WM_NOTIFY消息。WM_NOTIFY消息参数wParam表示控件ID,lParam表示 指向结构体NMHDR的指针。NMHDR包含控件通知的内容。

    说明:

    WM_NOTIFY消息的出现,是为了解决WM_COMMAND消息无法承载更多信息的问题。

    在以前控件不多的时候,这些控件发送信息给父窗口通过WM_COMMAND,然后两个消息参数里分别表示控件ID和通知码。对于简单的事件,这样的方式没问题,但是如果对于复杂的事件,比如控件绘制事件,它需要很多信息传给父窗口,而WM_COMMAND两个消息已经被占用了,应该怎么办?为了解决这个问题,就专门为控件绘制增加了一个新的消息WM_DRAWITEM,wParam表示控件ID,lParam为指向结构体DRAWITEMSTRUCT的指针。随着控件的再次增加,再次增加WM_XXX的消息变得不太现实,所以就出现了WM_NOTIFY消息,wParam表示控件ID,lParam分两种情况,对于一些通知码,它的值为指向结构体NMHDR的指针,对于另外 一些通知码,它的值为指向包含更多信息结构体的指针,并且这个结构体的第一个字段必须是NMHDR类型的变量。

    比如:

    在ListView中按下键盘,控件会发送WM_NOTIFY消息给父窗口,lParam表示指向NMLVKEYDOWN结构体的指针,而事件通知码LVN_KEYDOWN放到该结构体第一个字段NMHDR类型变量的code字段中。

    自定义消息

    系统保留的消息标识符的范围是:0x0000 到 0x03FF(WM_USER-1)

    这些值被系统定义的消息使用,用户不能使用这些值定义自己的消息。

    用户可以定义的消息范围是:0x0400 (WM_USER) 到 0x7FFF。

    如下:

    1 #define MSG_USERDEFINE WM_USER+1

    推荐阅读:

    Windows消息

    https://docs.microsoft.com/en-us/windows/win32/learnwin32/window-messages

    消息和消息队列 

    https://docs.microsoft.com/en-us/windows/win32/winmsg/about-messages-and-message-queues

    标准控件

    https://docs.microsoft.com/en-us/windows/win32/controls/window-controls

  • 相关阅读:
    全球覆盖 哈希
    陌上花开 模板 三维偏序
    洛谷 P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并
    熟练剖分(tree) 树形DP
    那一天她离我而去 二进制分组建图
    平凡的函数 线性筛积性函数
    wmz的数数(数状数组)
    跳一跳 概率与期望
    洛谷 P4284 [SHOI2014]概率充电器 概率与期望+换根DP
    SpringBoot手动事务参考链接
  • 原文地址:https://www.cnblogs.com/zhaotianff/p/13852218.html
Copyright © 2020-2023  润新知