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应用程序设计有所帮助。

时间: 2014-10-22

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

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

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

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

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

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

易语言创建EXCEL对象的方法

Excel对于大家来说,肯定不会陌生,办公软件"三剑客"之一,报表利器.今天,我们要学习的是易语言怎么创建EXCEL对象,好了,言归正传吧! 1.老规矩,首先运行易语言,创建"Windows窗口程序",如图: 2.绘制按钮组件,调整其位置和大小.如图: 3.修改相关组件的对应属性,并规范命名组件的名称.如图: 4.编写程序代码,具体代码如下: 5.代码编写完成,测试程序.猛戳F5键,运行程序.如图:

python在windows下创建隐藏窗口子进程的方法

本文实例讲述了python在windows下创建隐藏窗口子进程的方法.分享给大家供大家参考.具体实现方法如下: import subprocess IS_WIN32 = 'win32' in str(sys.platform).lower() def subprocess_call(*args, **kwargs): #also works for Popen. #It creates a new *hidden* window, #so it will work in frozen apps

使用C#创建Windows服务的实例代码

本文介绍了使用C#创建Windows服务的实例代码,分享给大家 一.开发环境 操作系统:Windows 10 X64 开发环境:VS2015 编程语言:C# .NET版本:.NET Framework 4.0 目标平台:X86 二.创建Windows Service 1.新建一个Windows Service,并将项目名称改为"MyWindowsService",如下图所示: 2.在解决方案资源管理器内将Service1.cs改为MyService1.cs后并点击"查看代码&

C语言创建链表错误之通过指针参数申请动态内存实例分析

本文实例讲述了C语言创建链表中经典错误的通过指针参数申请动态内存,分享给大家供大家参考之用.具体实例如下: #include <stdio.h> #include <stdlib.h>// 用malloc要包含这个头文件 typedef struct node { int data; struct node* next;// 这个地方注意结构体变量的定义规则 } Node; void createLinklist(Node* pHder, int length) { int i =

python GUI编程(Tkinter) 创建子窗口及在窗口上用图片绘图实例

注意主窗口一定要为tk.Tk(),在主窗口上通过button的点击相应子函数创建子窗口,注意此时创建出来的窗口必须是Toplevel,否则出错. 至于用图片在窗口上绘图,则按代码所示即可. # -*- coding: utf-8 -*- """ Created on Wed Oct 26 20:32:52 2016 @author: min """ import Tkinter as tk from PIL import Image, Image

C#通过创建Windows服务启动程序的方法详解

本文实例讲述了C#通过创建Windows服务启动程序的方法.分享给大家供大家参考,具体如下: 1. 新建一个Windows服务应用程序 创建项目-->Visual C# 左侧的"+"-->Windows -->Windows 服务(右侧模板)-->输入名称,确定创建项目 2. 设置Windows服务的属性(Windows服务里没有窗体,所以点击左侧设计器里空白的地方即可在右侧属性栏里看到属性) 这里属性是控制服务器是否可以停止,暂停,继续等等的操作.根据需要选择

易语言做软件的实例教学

教大家简单的做一个小软件 第一步,打开易语言,选择Windows窗口程序, 第二步,在基本组件找到按钮组件 第三步,双击按钮,然后写代码. 第四步,运行测试效果 第五步,编译 上面就是用易语言编写软件的实例步骤,感谢大家的学习和对我们的支持.

易语言创建Word对象的方法

Word对象,就是平时我们所说的COM对象,我们使用的操作系统中有很多COM对象 1.首先,运行易语言程序,新建"Windows窗口程序"并进入.如图: 2.在创建的窗口上,绘制一个按钮组件,设置按钮组件的大小和位置.如图: 3.修改窗口和按钮的标题,并规范命名按钮组件的名称.如图: 4.编写程序代码,具体代码如下: 5.代码编写完成,测试程序.如图: