一、消息机制的流程

1、消息

  • 定义:一个消息,是系统定义的一个32位的值,唯一的定义了一个事件。消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息。例如,对于单击鼠标所产生的消息来说,这个记录中包含了单击鼠标时的坐标。这个记录类型叫做MSG,MSG含有来自windows应用程序消息队列的消息信息
  • 消息声明

    1. typedef struct tagMSG {
    2. HWND hwnd; //消息所属窗口
    3. UINT message; //消息标识符
    4. WPARAM wParam; //指定消息的附加信息
    5. LPARAM lParam; //指定消息的附加信息
    6. DWORD time; //消息队列中的时间
    7. POINT pt; //鼠标的当前位置
    8. } MSG;
  • 一般种类

    1. 窗口消息
      • 指由操作系统和控制其他窗口的窗口所使用的消息。例如CreateWindow、DestroyWindow和MoveWindow等都会激发窗口消息。
    2. 命令消息
      • 用来处理从一个窗口发送到另一个窗口的用户请求,例如按下一个按钮,他就会向主窗口发送一个命令消息
    3. 控件通知消息
      • 是指一个窗口内的子控件发生了一些事情,需要通知父窗口。通知消息只适用于标准的窗口控件如按钮、列表框、组合框、编辑框,以及Windows公共控件如树状视图、列表视图等
  • 从消息发送途径分类

    1. 队列消息
      • 队列消息送到系统消息队列,然后到线程消息队列
      • 从处理方式来看,消息队队列可以分成系统消息队列和线程消息队列。系统消息队列由Windows维护,线程消息队列则由每个GUI线程自己进行维护,为避免给non-GUI线程创建消息队列,所有线程产生时并没有消息队列,仅当线程第一次调用GDI函数时系统才给线程创建一个消息队列
    2. 非队列消息
      • 直接送给目的窗口过程。

        2、windows窗体创建

  • 声明WNDCLASS实例

    1. - WNDCLASS储存某一类窗口的信息
    1. typedef struct _WNDCLASS {
    2. UINT style; //样式
    3. WNDPROC lpfnWndProc; //设置窗体接收windws消息函数
    4. int cbClsExtra; //窗口类扩展
    5. int cbWndExtra; //窗口实例扩展
    6. HINSTANCE hInstance; //窗体实例名,由windows自动分发
    7. HICON hIcon; //显示上面的图标titlte
    8. HCURSOR hCursor; //窗口光标
    9. HBRUSH hbrBackground; //背景刷
    10. LPCTSTR lpszMenuName; //窗口菜单
    11. LPCTSTR lpszClassName; //窗体类名
    12. } WNDCLASS, *PWNDCLASS;
  • 窗体注册

    1. - RegisterClassRegisterClassEx
    1. ATOM WINAPI RegisterClass( _In_ const WNDCLASS *lpWndClass);
    2. //lpWndClass 指向一个 WNDCLASS 结构的指针
  • 创建窗体

    1. - CreateWindow()或CreatewindowEx(),函数返回值为创建窗体的句柄
    1. HWND WINAPI CreateWindow(
    2. _In_opt_ LPCTSTR lpClassName, // 窗口类名称
    3. _In_opt_ LPCTSTR lpWindowName, // 窗口标题
    4. _In_ DWORD dwStyle, // 窗口风格,或称窗口格式
    5. _In_ int x, // 初始 x 坐标
    6. _In_ int y, // 初始 y 坐标
    7. _In_ int nWidth, // 初始 x 方向尺寸
    8. _In_ int nHeight, // 初始 y 方向尺寸
    9. _In_opt_ HWND hWndParent, // 父窗口句柄
    10. _In_opt_ HMENU hMenu, // 窗口菜单句柄
    11. _In_opt_ HINSTANCE hInstance, // 程序实例句柄
    12. _In_opt_ LPVOID lpParam // 创建参数

3、windows消息的处理

  • 探究消息如何传递到窗体中
  • 过程:Windows为当前执行的每个Windows程序维护一个消息队列。在发生输入事件之后,Windows将事件转换为一个消息并将消息放入程序的消息队列中。程序通过执行一块称之为消息循环的程序代码从消息队列中取出消息,分发到相应的窗体函数中。具体执行流程分为三步:消息的发送、消息的接收以及消息的处理。

16.jpg

  • 消息的发送
    • 发送
      • SendMessage、SendMessageCallback、SendNotifyMessage、SendMessageTimeout
    • 寄送
      • PostMessage、PostThreadMessage、PostQuitMessage
    • 广播
      • BroadcastSystemMessage、BroadcastSystemMessageEx
  • 消息的接收
    • GetMessage
      • 从调用线程的消息队列里取得一个消息并将其放于指定的结构
      • 不接收属于其他线程或应用程序的消息
      • 获取消息成功后,线程将从消息队列中删除该消息。函数会一直等待直到有消息到来才有返回值。
    • PeekMessage
      • PeekMessage与GetMessage功能类似,不同之处在于GetMessage不将控制传回给程序,直到从程序的消息队列中取得消息,但是PeekMessage总是立刻传回,而不论一个消息是否出现
    • WaitMessage
      • 线程的消息队列中无其它消息时,该函数就将控制权交给另外的线程,同时将该应用程序挂起,直到一个新的消息被放入应用程序的队列之中才返回。

4、典型处理流程

  1. while(GetMessage(&msg, NULL, 0, 0))//复制一个消息,放在MSG结构中,没有消息则等待(第二个参数是窗口句柄,用来决定接收那个窗口的消息,后两个参数用来过滤消息)
  2. {
  3. if(!TranslateAccelerator(msg.hWnd, hAccelTable, &msg))//TranslateAccelerator判断该消息是不是一个按键消息并且是一个加速键消息,如果是,则该函数将把几个按键消息转换成一个加速键消息传递给窗口的回调函数
  4. {
  5. TranslateMessage(&msg);//把两个按键消息WM_KEYDOWN和WM_KEYUP转换成一个 WM_CHAR,不过需要注意的是,消息WM_KEYDOWN,WM_KEYUP仍然将传递给窗口的回调函数
  6. DispatchMessage(&msg);//把此消息发送给该消息指定的窗口中已设定的回调函数
  7. }
  8. }
  • 窗口过程被所有属于同一个类的窗口共享,能为不同的窗口处理消息,同一个类或一个父类的窗口使用同样的窗口过程来响应消息。通常窗口过程函数是通过一个switch语句来实现的,利用HANDLE_MSG消息分流器则可以把switch语句分成更小的函数,每一个消息都对应一个小函数,这样做的好处就是对消息更容易管理。一个窗口过程不经常忽略消息,如果他不处理,它会将消息传回到执行默认的处理。窗口过程通过调用DefWindowProc来做这个处理。

二、实例分析