windows消息和消息队列实例详解

本文详细讲述了windows消息和消息队列的原理与应用方法。分享给大家供大家参考。具体分析如下:

与基于MS - DOS的应用程序不同,Windows的应用程序是事件(消息)驱动的。它们不会显式地调用函数(如C运行时库调用)来获取输入,而是等待windows向它们传递输入。 windows系统把应用程序的输入事件传递给各个窗口,每个窗口有一个函数,称为窗口消息处理函数。窗口消息处理函数处理各种用户输入,处理完成后再将控制权交还给系统。窗口消息处理函数一般是在注册一个窗口的时候指定的。你可以从典型的SDK程序中窗口消息处理函数是怎么声明和实现的。

对于Windows XP系统:如果顶层窗口停止响应消息超过几秒钟,系统会认为窗口无回应。在这种情况下,系统将隐藏这个窗口,然后生成一个影子(ghost)窗口覆盖在它上面。这个影子窗口具有着相同的Z轴顺序,位置,大小,显示属性。影子窗口允许用户将其移动,调整大小,甚至关闭(关闭的是停止响应的window)。此时只有这几个动作是被允许的,在调试模式下,系统不会生成影子窗口。

本节讨论以下主题:

Windows消息

1.  消息类型

2.  消息传递

3.  消息处理

4.  消息过滤

5.  post message和send message

6.  消息死锁

7.  广播消息

8.  查询消息

现分述如下:

1. Windows消息

windows通过消息的形式向窗口传递用户输入。消息可以由系统和应用程序生成。该系统会为每个输入事件产生相应的消息,

例如,用户点击鼠标,移动鼠标或滚动条,或是应用程序改变了系统的某些属性,比如说系统更改了字体资源,改变了某个窗口的

大小。 不仅如此,应用程序可以生成消息,通告发送消息指定它的窗体去执行某些任务或者是与其他的应用程序交互。

windows系统将消息发送到一个窗口消息处理函数时传递四个参数:窗口句柄,消息标识符,两个DWORD值(消息参数)。

窗口句柄标识了该消息的目的窗口。windows使用它来确定是哪个窗口的的窗口消息处理函数收到该消息。

一个消息标识符是一个有名字的常量,用来表明消息的意义。当一个窗口处理函数收到一条消息,它根据判断消息标识符来决定如何处理该消息,例如,消息标识符WM_PAINT消息告诉窗口程序窗口的客户区已发生变化,必须重绘。 消息参数(DWORD值)指定传递的数据或是数据的地址。消息参数可以是一个整型值,一个指针值。也可以为NULL。

一个窗口过程必须根据消息标识符来确定如何解释消息参数。

2. windows 消息类型

本节描述消息的两种类型:

(1) 系统定义的消息

(2) 应用程序定义的消息

系统定义的消息

操作系统向应用程序发送消息来和应用程序通讯。操作系统通过消息控制应用程序的运行,向应用程序传递用户输入以及一些其他有用的信息。

应用程序也可以发送系统定义的消息,应用程序通过这些消息去控制使用注册窗口类创建的控件的窗口的运行。

每个系统定义的消息都有一个唯一的消息标识符和相应的符号常量(在windows SDK的头文件里定义)。符号常量通常会表明系统定义的消息所属的类别。不同的前缀表明不同的类别。一下是常见的分类:

Prefix Message category

WM        General window(一般的窗口)

ABM        Application desktop toolbar (应用程序桌面工具条)

BM        Button control (按钮控件)

CB        Combo box control (组合框控件)

CBEM Extended combo box control(扩展的组合框控件)

CDM        Common dialog box (普通的对话框)

DBT        Device (设备)

DL        Drag list box (下拉列表)

DM        Default push button control (默认按钮控件)

DTM        Date and time picker control(日期和时间选择控件)

EM        Edit control (编辑控件)

HDM        Header control (表头控件)

HKM        Hot key control (热键控件)

IPM        IP address control (IP地址控件)

LB        List box control  (列表框控件)

LVM        List view control (列表视图控件)

MCM        Month calendar control (数学日历控件)

PBM        Progress bar (进度条控件)

PGM        Pager control ()

PSM        Property sheet (属性页)

RB        Rebar control (分隔条控件)

SB        Status bar window (状态条控件)

SBM        Scroll bar control (滚动条控件)

STM        Static control (静态控件)

TB        Toolbar (工具条)

TBM        Trackbar (跟踪栏)

TCM        Tab control (选项卡控件)

TTM        Tooltip control ()

TVM        Tree-view control ()

UDM        Up-down control ()

应用程序可以通过创建自定义的消息,用来和自己的窗口和其他进程通讯。如果应用程序创建了自己的消息,窗口处理函数可以解析这些信息,并作出相应的处理。

消息标识符值的取值范围:

该系统保留了一个消息范围,从0x0000到0x03FF(0x03FF等于WM_USER -1)范围. 这个范围内的值为系统定义的消息。应用程序不能使用这些值作为自己的自定义消息。

从0x0400(数值WM_USER)到0x7FFF的值是为应用程序保留的。应用程序可以使用这个范围内的值来定义自己的消息。

如果你的操作系用的版本(windows version)主版本为4.0版,你还可以使用0x8000(WM_APP)到0xBFF之间的值来定义自己的消息。

除此之外,应用程序还可以调用RegisterWindowMessage函数注册一个消息时,操作系统会返回一个介于0xC000和0xFFFF之间的一个消息标识符。并且保证这个返回值是系统唯一的。因此,可以避免和其他应用程序使用的消息相冲突。

3. 消息派发

windows使用两种方法将消派发到一个窗口消息处理函数:一是将消息放到消息队列(先进先出队列),二是不放到消息队列,直接发送到窗口消息处理函数,让窗口处理函数来处理消息。

派发到消息队列的消息被称为排队消息(Queued messages)。它们主要是用户输入事件,比如说鼠标或键盘消息盘,有WM_MOUSEMOVE消息,WM_LBUTTONDOWN,WM_KEYDOWN,和WM_CHAR消息。还有一些其他的,包括WM_TIMER,WM_PAINT,以及WM_QUIT。大多数其他的消息息,这是直接发送到窗口过程,被称为非队列消息(non queued messages)。

(1) 队列(Queued)消息

windows可同时显示任意数量的窗口。此时,系统使用消息队列来将键盘和鼠标事件正确的派发到正确的窗口。

windows维护着一个系统消息队列,以及分别为每个GUI线程维护一个各自的线程消息队列。为了避免非GUI线程的创建线程消息队列的开销,所有线程创建初始化时,均不创建消息队列。只有当线程第一次调用GDI函数时,系统才会为线程创建消息队列。所以那些非GUI线程是没有消息队列的。

每当用户移动鼠标,点击按钮或键盘时,鼠标或键盘的设备驱动程序会将输入转换成消息,并将消息放在系统消息队列里。删windows会检查自己的消息队列,如果消息队列不为空,则每次取出并删除一个消息,然后确定消息的目标窗口,然后把消息放到创建这个窗口的线程的线程消息队列里。线程的消息队列接收由线程创建的窗口的所有的鼠标和键盘消息。然后线程会从队列中删除信息,并告诉系统把它们派发到对应的窗口消息处理函数。

除了WM_PAINT, WM_TIMER和WM_QUIT消息以外,系统总是派发放在在消息队列的末尾的消息。这将保证让一个窗口以first-in, first-out的顺序接收消息。WM_PAINT,WM_TIMER,和WM_QUIT消息,会一直被保存在队列中,只有在队列中没有其他消息时才会被派发到窗口消息处理函数。此外,同一个窗口的多个WM_PAINT消息被合并成一个WM_PAINT消息,客户区的所有无效部分也会被合并。这样是为了减少窗口重绘客户区的次数。

windows向线程消息队列传递消息时,首先会填充一个MSG结构,然后将这个MSG结构复制到消息队列。MSG中的信息包括:目标窗口,消息标识符,两个消息参数,消息派发时的时间,鼠标光标位置。一个线程可以使用PostMessage或PostThreadMessage功能向自己的消息队列或者是其他线程的消息队列发送消息。

应用程序可以使用GetMessage函数从自己的消息队列中删除消息。查看而不删除消息,用的是PeekMessage函数。

PeekMessage函数会返回一个带有消息信息的MSG结构。

从消息队列中删除消息后,应用程序可以使用DispatchMessage函数指示系统将消息发送到一个窗口消息处理函数。 DispatchMessage的参数是是前一次调用GetMessage或PeekMessage获得的MSG结构的指针。 DispatchMessage会传递窗口句柄,消息标识符,这两个消息参数这些信息给窗口消息处理函数,它不会传递消息派发时间以及鼠标光标位置。应用程序可以在处理消息时调用的GetMessageTime和GetMessagePos来获得这些信息。

线程可以使用WaitMessage函数,交出自己的控制权,当它的消息队列中没有消息时,调用WaitMessage函数会挂起线程,直到自己的消息队列里有消息时才返回。

您可以调用SetMessageExtraInfo函数来关联一个值给当前线程的消息队列。然后调用GetMessageExtraInfo函数来获取由GetMessage或PeekMessage函数得到的最后一条消息相关联的值。你可以去msdn上看更多的关于这几个函数的信息。

(2) 非队列(Nonqueued)消息

Nonqueued消息被立即送往目的地的窗口消息处理函数,绕过了系统的消息队列和线程消息队列。系统通常会发送nonqueued消息,来通知那些会影响窗口的事件。例如,当用户激活一个新的应用程序窗口时,系统会发送一些列消息到窗口,包括WM_ACTIVATE,WM_SETFOCUS,WM_SETCURSOR。这些消息通知窗口被激活,键盘输入被定向到窗口,并且鼠标光标也移到窗口的边界内。

Nonqueued消息也有可能来源于应用程序调用系统函数。例如,系统调用SetWindowPos函数移动一个窗口后会发送WM_WINDOWPOSCHANGED消息。 一些函数也发送nonqueued消息, 有BroadcastSystemMessage,BroadcastSystemMessageEx,SendMessage,SendMessageTimeout,和SendNotifyMessage。 关于这些函数的详细信息,你可以查阅MSDN。

消息处理

应用程序必须删除并处理发送到它的线程消息队列的消息。单线程应用程序通常在它的WinMain函数的消息循环,删除和分发消息到适当的窗口进行处理。多线程应用程序可以在每一个线程创建一个窗口的消息循环。以下章节描述了一个消息

循环如何工作,并讲述窗口消息处理函数的作用:

(1)消息循环

(2)窗口处理函数

消息循环

一个简单的消息循环包含调用以下三个函数:GetMessage,TranslateMessage,和DispatchMessage。请注意,如果有一个错误,GetMessage返回-1 -因此,需要测试它的返回值,来判断为-1的情况

代码片段:

代码如下:

...

MSG msg;

BOOL bRet;

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

GetMessage函数从队列中获取消息,并将消息内容复制到一个MSG结构。它返回一个非零值,除非遇到WM_QUIT消息,此种返回FALSE并结束消息循环。在一个单线程应用程序,结束消息循环往往是在关闭应用程序的第一步。应用程序可以调用PostQuitMessage函数来响应WM_DESTROY,结束消息循环。

如果您指定一个窗口句柄作为GetMessage的第二个参数,那么GetMessage只获取在消息队列里和这个窗口有关的消息。 GetMessage也可以在队列中筛选消息,只获取指定范围内的消息。如需有关消息过滤的详细信息,请参考消息过滤。

线程的消息循环必须包括TranslateMessage,如果线程需要接受键盘字符的输入。每次用户按下一个键,该系统产生相应的虚拟键消息(WM_KEYDOWN和WM_KEYUP)。虚拟键消息包含一个虚拟键码,标识的是被按下的键,而不是它相关的字符值。要获得此值,消息循环必须包含TranslateMessage,用来将虚拟键消息翻译成字符消息(WM_CHAR)的并放到应用程序的消息队列里。经过若干次循环后,WM_CHAR消息会被并派发到一个窗口。

DispatchMessage函数将消息发送到到与MSG结构中的窗口句柄关联的窗口。如果窗口句柄是HWND_TOPMOST,DispatchMessage则将消息发送到操作系统所有的顶层窗口。如果窗口句柄是NULL,DispatchMessage不做任何事。

一个应用程序的主线程初始化后,系统就启动应用程序的消息循环,并创造至少一个窗口。一旦启动,消息循环持续从该线程的消息队列中删除消息,并派发他们到相应的窗口。GetMessage函数从消息列表中获取到WM_QUIT消息时,消息循环结束。

一个消息队列只需要一个消息循环,即使一个应用程序包含有多个窗口。 DispatchMessage总是调度消息到正确的窗口,这是因为每个队列中的消息是MSG结构,它包含着消息所属的窗口的句柄。

您可以以多种方式来修改消息循环。例如,您可以从队列中删除消息,但是不派发他们。当发送有些不带有目的地窗口的消息时这非常有用。您也可以使用GetMessage只获取指定的消息,这是有用的,如果你必须你暂时绕过正常的消息队列FIFO顺序。

应用程序使用快捷键时,必须能够将键盘消息转换为命令消息。因此,应用程序的消息循环必须包括TranslateAccelerator函数调用。关于快捷键的更多信息,请参见键盘加速器。

如果一个线程使用一个无模式对话框,那么消息循环必须包括IsDialogMessage函数,以便该对话框可以接收键盘输入。

(3)窗口消息处理函数

窗口消息函数接收和处理的所有发送到窗口的消息。每个窗口类有一个窗口消息处理函数,用该类创建的每个窗口使用同一窗口消息处理函数。

该系统将消息发送到一个窗口的程序,并传递消息的相关信息到窗口消息处理函数,窗口消息处理函数检查消息标识符,根据传过来的参数识别并处理不同的消息,

一个窗口过程通常不会忽略一个消息。如果消息没有被处理,必须被发送给系统默认的窗口消息处理函数,这是否通过调用DefWindowProc函数,来执行一个默认的处理,并返回一个处理的结果。窗口程序必须然后返回该值作为自己的消息处理的结果。大多数窗口消息处理函数只处理一少部分消息,并将其他的返回给系统默认的窗口消息处理函数。

因为窗口消息处理函数被所有属于同一个窗口类的窗口共享,它可以处理几个不同的窗口的消息。要确定具体的窗口消息,窗口消息处理函数可以检查消息结构里的窗口句柄。

希望本文所述对大家的Windows应用程序设计有所帮助。

(0)

相关推荐

  • Windows运行bat批处理文件时隐藏cmd命令提示符窗口的方法

    我们在Windows中运行bat批处理文件时往往会弹出一个cmd命令提示符窗口,然后等一会儿cmd窗口就自动关闭了,有人会说,直接在bat批处理文件中加入echo off命令么好了,没错,echo off确实可以关闭cmd窗口,但是cmd窗口最初还是会弹出一下再消失,下面就教大家运行bat批处理文件时如何彻底隐藏cmd命令提示符窗口的方法. 方法就是通过vbs脚本来彻底隐藏执行bat批处理文件,将以下代码保存为.vbs文件,然后直接执行这个.vbs文件即可彻底隐藏cmd窗口了: Set ws =

  • Windows消息传递机制详解

    对于windows程序设计,这里有几个关键词需要注意:消息,消息循环,窗口过程.   所谓的Windows消息传递机制就类似于生活中的物流公司.当寄件人(例如鼠标.键盘)将包裹(消息)交给物流公司(Windows系统)时,物流公司(Windows系统)会进行整理并且派发(整理及派发主要由消息循环完成),交给相应的快递员(窗口过程)来处理.快递员(窗口过程)拿到包裹(消息)后则有多种方式来处理,如立马交给收件人,等一天交给收件人,或转交给其他快递派发,这就需要在窗口过程中用swich/case来区

  • Python实现遍历windows所有窗口并输出窗口标题的方法

    本文实例讲述了Python实现遍历windows所有窗口并输出窗口标题的方法.分享给大家供大家参考.具体如下: 这段代码可以让Python遍历当前Windows下所有运行程序的窗口,并获得运行窗口的标题输出 #! /usr/bin/env python # -*- coding: utf-8 -*- from win32gui import * titles = set() def foo(hwnd,mouse): #去掉下面这句就所有都输出了,但是我不需要那么多 if IsWindow(hwn

  • Windows窗口消息实例详解

    本文实例总结了Windows窗口消息.分享给大家供大家参考.具体如下: 复制代码 代码如下: //////////////////////////////////////////////////////////////////////////    #include "AFXPRIV.H"//消息值的定义来源    #include "Dde.h"//DDE消息值的定义来源    #include "CPL.H"//控制面板消息值的定义来源   

  • C语言创建windows窗口实例

    耐得住寂寞,禁得起诱惑,这就是程序人生 步骤: 1.在WinMain中定义各种变量 2.注册窗口类RegisterClass 3.创建窗口CreateWindow 4.显示窗口和更新窗口 复制代码 代码如下: ShowWindow (hwnd, iCmdShow) ;      UpdateWindow (hwnd) ; 5.消息循环 复制代码 代码如下: while (GetMessage (&msg, NULL, 0, 0))      {           TranslateMessag

  • C#窗体编程(windows forms)禁止窗口最大化的方法

    本文介绍在C#窗体编程时,如何禁用系统默认的三种将窗口最大化的方式,包括系统菜单.最大化按钮,以及窗口的拖拽. Windows环境下的窗体,要想最大化,有多种办法.比如最大化按钮,比如拉伸窗口大小,或者是使用系统菜单中的最大化.系统菜单即在一个窗口中按(Alt+空格)出现在窗口左上角的那个菜单. 那么有没有办法将一个窗体中所有的最大化功能全部去掉呢?需求肯定是有的,就看我们怎么来实现了. 1.处理系统菜单中的最大化功能 首先在窗体类中声明: 复制代码 代码如下: public class For

  • C++ 中"priority_queue" 优先级队列实例详解

    C++ 中"priority_queue" 优先级队列实例详解 1. 简介 标准库队列使用了先进先出(FIFO)的存储和检索策略. 进入队列的对象被放置在尾部, 下一个被取出的元素则取自队列的首部. 标准库提供了两种风格的队列: FIFO 队列(FIFO queue, 简称 queue), 以及优先级队列(priority queue). priority_queue 允许用户为队列中存储的元素设置优先级. 这种队列不是直接将新元素放置在队列尾部, 而是放在比它优先级低的元素前面. 标

  • C++ 中"priority_queue" 优先级队列实例详解

    C++ 中"priority_queue" 优先级队列实例详解 1. 简介 标准库队列使用了先进先出(FIFO)的存储和检索策略. 进入队列的对象被放置在尾部, 下一个被取出的元素则取自队列的首部. 标准库提供了两种风格的队列: FIFO 队列(FIFO queue, 简称 queue), 以及优先级队列(priority queue). priority_queue 允许用户为队列中存储的元素设置优先级. 这种队列不是直接将新元素放置在队列尾部, 而是放在比它优先级低的元素前面. 标

  • .net msmq消息队列实例详解

    本文为大家分享了.net msmq消息队列实例代码,供大家参考,具体内容如下 1.msmq消息队列windows环境安装 控制面板---->程序和功能---->启用或关闭Windows程序---->Microsoft Message Queue(MSMQ)服务器 选中如图所示功能点击"确认"进行安装,安装好后可在 "计算机管理"中进行查看 2.创建消息队列实体对象 /// <summary> /// 消息实体 /// </summ

  • windows消息和消息队列实例详解

    本文详细讲述了windows消息和消息队列的原理与应用方法.分享给大家供大家参考.具体分析如下: 与基于MS - DOS的应用程序不同,Windows的应用程序是事件(消息)驱动的.它们不会显式地调用函数(如C运行时库调用)来获取输入,而是等待windows向它们传递输入. windows系统把应用程序的输入事件传递给各个窗口,每个窗口有一个函数,称为窗口消息处理函数.窗口消息处理函数处理各种用户输入,处理完成后再将控制权交还给系统.窗口消息处理函数一般是在注册一个窗口的时候指定的.你可以从典型

  • ActiveMQ消息签收机制代码实例详解

    这篇文章主要介绍了ActiveMQ消息签收机制代码实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 消费者客户端成功接收一条消息的标志是:这条消息被签收. 消费者客户端成功接收一条消息一般包括三个阶段: 1.消费者接收消息,也即从MessageConsumer的receive方法返回 2.消费者处理消息 3.消息被签收 其中,第三阶段的签收可以有ActiveMQ发起,也可以由消费者客户端发起,取决于Session是否开启事务以及签收模式的

  • Windows程序内部运行机制实例详解

    本文以孙鑫老师VC++教程中的程序为基础,详细讲解了Windows程序内部运行机制,相信可以帮助大家更好的理解Windows程序运行原理及相应的VC++程序设计.具体内容如下: 创建一个Win32应用程序步骤: 1.编写WinMain函数; 2.创建窗口(步骤如下): a.设计(一个)窗口类(WNDCLASS) b.注册(该)窗口类. c.创建窗口. d.显示并更新窗口. 3.编写消息循环. 4.编写窗口过程函数. //WinMain.cpp #include <windows.h> #inc

  • JavaScript数据结构之优先队列与循环队列实例详解

    本文实例讲述了JavaScript数据结构之优先队列与循环队列.分享给大家供大家参考,具体如下: 优先队列 实现一个优先队列:设置优先级,然后在正确的位置添加元素. 我们这里实现的是最小优先队列,优先级的值小(优先级高)的元素被放置在队列前面. //创建一个类来表示优先队列 function Priorityqueue(){ var items=[];//保存队列里的元素 function QueueEle(e,p){//元素节点,有两个属性 this.element=e;//值 this.pr

  • Java数据结构之栈与队列实例详解

    目录 一,栈 1,概念 2,栈的操作 3,栈的实现  4,实现mystack 二,队列 1,概念  2,队列的实现  3,实现myqueue 栈.队列与数组的区别? 总结 一,栈 1,概念 在我们软件应用 ,栈这种后进先出数据结构的应用是非常普遍的.比如你用浏 览器上网时不管什么浏览器都有 个"后退"键,你点击后可以接访问顺序的逆序加载浏览过的网页.   很多类似的软件,比如 Word Photoshop 等文档或图像编 软件中 都有撤销 )的操作,也是用栈这种方式来实现的,当然不同的

  • Linux消息队列实现进程间通信实例详解

    Linux消息队列实现进程间通信实例详解 一.什么是消息队列 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法.  每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构.我们可以通过发送消息来避免命名管道的同步和阻塞问题.但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制. Linux用宏MSGMAX和MSGMNB来限制一条消息的最大长度和一个队列的最大长度. 二.在Linux中使用消息队列 Linux提供了一系列消息队列的函数接口来让我们方便地使用

  • Android 消息队列模型详解及实例

    Android 消息队列模型详解及实例 Android系统的消息队列和消息循环都是针对具体线程的,一个线程可以存在(当然也可以不存在)一个消息队列(Message Queue)和一个消息循环(Looper).Android中除了UI线程(主线程),创建的工作线程默认是没有消息循环和消息队列的.如果想让该线程具有消息队列和消息循环,并具有消息处理机制,就需要在线程中首先调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环.如以下代码所示: class

随机推荐