windows消息机制深入详解-1
Windows 是一个事件驱动的操作系统。事件驱动围绕着消息的产生与处
理展开,事件驱动是靠消息循环机制来实现的。也可以理解为消息是一种报告有关事件发生
的通知,消息是Windows 操作系统的灵魂。
在屏幕显示一个窗口一般是以下几个步骤:
1. 得到应用程序的句柄(GetMouduleHandle,一个程序被加载进内存之后,被称为一个模块,代表着这个文件中所有的代码和资源,取这个模块的句柄方便以后操作这个应用程序)。
2. 注册窗口类(RegisterClassEx),在注册之前,要先填写RegisterClassEX的参数WNDCLASSEX结构。
3. 建立窗口(CreateWindow)。
4. 显示窗口(ShowWindow)。
5. 刷新窗口客户区(UpdateWindow)。
6. 进入无限的消息循环,如果有消息到达,将消息分派到回调函数处理(DispatchMessage),如果是WM_QUIT消息,则退出循环。回调函数是用来处理消息的,也叫窗口过程,这是由windows自己调用的,我们调用DispatchMessage函数,而DispatchMessage函数会自动调用窗口过程。
while (GetMessage(&Msg, NULL, 0, 0))
{
if (!TranslateAccelerator(hwnd, hAccelerator, &Msg))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
当用户按下某个键、鼠标移动或者拖动窗口等都会产生消息传给应用程序,应用程序再根据不同的消息作不同的处理。
Windows的系统内部有个系统消息队列,当用户当用户按下某个键、鼠标移动或者拖动窗口时,windows都会产生相应的记录放在消息队列里。每个记录中包含着消息的类型、发生的位置和发生的时间等。
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
#ifdef _MAC
DWORD lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
窗口程序运行过程
同时,Windows为每个线程维护一个消息队列,Windows检查系统消息队列的发生位置,当位置位于某个应用程序的窗口范围内时,就把这个消息派送到应用程序的消息队列里。
当程序中的消息循环执行到GetMessage时,控制权转移到GetMessage所在的USER32.DLL领空(箭头1),USER32.DLL从程序消息队列中取出一条消息(箭头2),然后把这条消息返回给应用程序。(箭头3)
应用程序可以对这条消息先作预处理,如可以用TranslateMessage把基于键盘扫描码的按键消息转换成基于ASCII的键盘消息,如果用到快捷键,可以调用TranslateAccelerator把键盘快捷键转换成命令消息。
IDM_MAIN menu discardable
BEGIN
popup "关于(&A)"
BEGIN
menuitem "关于我(&A)...\tCtrl+A", IDC_ABOUT
END
END
//
IDA_MAIN accelerators //加速键
BEGIN
"O", IDC_OPEN,VIRTKEY,CONTROL //Ctrl+O
"A", IDC_ABOUT,VIRTKEY,CONTROL //Ctrl+A
END
快捷键
然后应用程序将处理这条消息,但不是自己直接调用窗口过程来完成,而是通过DispatchMessage间接调用窗口过程,DispatchMessage 的英文含义是“分派消息”,之所以是“分派”,是因为一个程序可能不只有一个窗口,不同的窗口消息必须分派给相应的窗口过程。当控制权转移到USER32.DLL领空中的DispatchMessage函数时,DispatchMessage找出消息对应的窗口过程,然后把消息的具体信息当作参数来调用它(箭头5),窗口过程根据消息找到对应的分支去处理,然后返回(箭头6),这时控制权又回到DispatchMessage,最后DispatchMessage 函数返回到应用程序领空。(箭头7)这样,一个循环就结束了,程序又开始新一轮的GetMessage。
窗口程序和对话框程序(分为模态对话框和非模态对话框)工作区别
/*******************************
Windows是一个消息(Message)驱动系统。Windows的消息提供了应用程序之间、应用程序与Windows系统之间进行通信的手段。应用程序想要实现的功能由消息来触发,并且靠对消息的响应和处理来完成。必须注意的是,消息并非是抢占性的,无论事件的缓急,总是按照到达的先后派对,依次处理(一些系统消息除外),这样可能使一些实时外部事件得不到及时处理。
Windows的应用程序一般包含窗口(Window),它主要为用户提供一种可视化的交互方式,窗口是总是在某个线程(Thread)内创建的。Windows系统通过消息机制来管理交互,消息(Message)被发送,保存,处理,一个线程会维护自己的一套消息队列(Message Queue),以保持线程间的独占性。队列的特点无非是先进先出,这种机制可以实现一种异步的需求响应过程。
目录:
1、消息
2 、消息类型
3 、消息队列(Message Queues)
4 、队列消息(Queued Messages)和非队列消息(Non-Queued Messages)
5 、PostMessage(PostThreadMessage), SendMessage
6 、GetMessage, PeekMessage
7 、TranslateMessage, TranslateAccelerator
8、(消息死锁( Message Deadlocks)
9、BroadcastSystemMessage
10、消息的处理
11、MFC的消息映射
12、消息反射机制
1、消息
消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息。例如,对于单击鼠标所产生的消息来
说,这个记录中包含了单击鼠标时的坐标。这个记录类型叫做MSG,MSG含有来自windows应用程序消息队列的消息信息,它在
2、消息类型
3、消息队列(Message Queues)
4、队列消息(Queued Messages)和非队列消息(Non-Queued Messages)
PostMessage(异步)和SendMessage(同步)的区别
a、 PostMessage 是异步的,SendMessage 是同步的。
PostMessage 只把消息放到队列,不管消息是不是被处理就返回,消息可能不被处理;
SendMessage等待消息被处理完了才返回,如果消息不被处理,发送消息的线程将一直处于阻塞状态,等待消息的返回。
系统只处理(marshal)系统消息(0--WM_USER),发送用户消息(用户自己定义)时需要用户自己处理。
6 、GetMessage, PeekMessage
PeekMessage会立即返回 可以保留消息
GetMessage在有消息时返回 会删除消息
在Windows的内部,GetMessage和PeekMessage执行着相同的代码,Peekmessage和Getmessage都是向系统的消息队列中取得消息,并将其放置在指定的结构。
PeekMessage:有消息时返回TRUE,没有消息返回FALSE
GetMessage:有消息时且消息不为WM_QUIT时返回TRUE,如果有消息且为WM_QUIT则返回FALSE,没有消息时不返回。
GetMessage:取得消息后,删除除WM_PAINT消息以外的消息。
PeekMessage:取得消息后,根据wRemoveMsg参数判断是否删除消息。PM_REMOVE则删除,PM_NOREMOVE不删除。
10、消息的处理
接下来我们谈一下消息的处理,首先我们来看一下VC中的消息泵:
while(GetMessage(&msg, NULL, 0, 0))
if(!TranslateAccelerator(msg.hWnd, hAccelTable, &msg))
用来把虚拟键消息转换为字符消息。由于Windows对所有键盘编码都是采用虚拟键的定义,这样当按键按下时,并不得字符消息,需要键盘映射转换为字符的消息。
也就是说TranslateMessage会发现消息里是否有字符键的消息,如果有字符键的消息,就会产生WM_CHAR消息,如果没有就会产生什么消息。
把 TranslateMessage转换的消息发送到窗口的消息处理函数,此函数在窗口注册时已经指定。
首先,GetMessage从进程的主线程的消息队列中获取一个消息并将它复制到MSG结构,如果队列中没有消息,则GetMessage函数将等待一个消息的到来以后才返回。如果你将一个窗口句柄作为第二个参数传入GetMessage,那么只有指定窗口的的消息可以从队列中获得。GetMessage也可以从消息队列中过滤消息只接受消息队列中落在范围内的消息。这时候就要利用GetMessage/PeekMessage指定一个消息过滤器。这个过滤器是一个消息标识符的范围或者是一个窗体句柄,或者两者同时指定。当应用程序要查找一个后入消息队列的消息是很有用。WM_KEYFIRST 和 WM_KEYLAST 常量用于接受所有的键盘消息。 WM_MOUSEFIRST 和 WM_MOUSELAST 常量用于接受所有的鼠标消息。
然后TranslateAccelerator判断该消息是不是一个按键消息并且是一个加速键消息,如果是,则该函数将把几个按键消息转换成一个加速键消息传递给窗口的回调函数。处理了加速键之后,函数TranslateMessage将把两个按键消息WM_KEYDOWN和WM_KEYUP转换成一个 WM_CHAR,不过需要注意的是,消息WM_KEYDOWN,WM_KEYUP仍然将传递给窗口的回调函数。
处理完之后,DispatchMessage函数将把此消息发送给该消息指定的窗口中已设定的回调函数。如果消息是WM_QUIT,则 GetMessage返回0,从而退出循环体。应用程序可以使用PostQuitMessage来结束自己的消息循环。通常在主窗口的 WM_DESTROY消息中调用。
11、MFC的消息映射
使用MFC编程时,消息发送和处理的本质和Win32相同,但是,它对消息处理进行了封装,简化了程序员编程时消息处理的复杂性,它通过消息映射机制来处理消息,程序员不必去设计和实现自己的窗口过程。
说白了,MFC中的消息映射机制实质是一张巨大的消息及其处理函数对应表。消息映射基本上分为两大部分:
在头文件(.h)中有一个宏DECLARE_MESSAGE_MAP(),它放在类的末尾,是一个public属性的;与之对应的是在实现部分(.cpp)增加了一个消息映射表,内容如下:
BEGIN_MASSAGE_MAP(当前类,当前类的基类)
//{{AFX_MSG_MAP(CMainFrame)
消息的入口项
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
但是仅是这两项还不足以完成一条消息,要是一个消息工作,必须还有以下3个部分去协作:
1、在类的定义中加入相应的函数声明;
2、在类的消息映射表中加入相应的消息映射入口项;
3、在类的实现中加入相应的函数体;
消息的添加
(1)、利用Class Wizard实现自动添加
在菜单中选择View -> Class Wizard激活Class Wizard,选择Message Map标签,从Class name组合框中选取我们想要添加消息的类。在Object IDs列表框中,选取类的名称。此时,Messages列表框显示该类的可重载成员函数和窗口消息。可重载成员函数显示在列表的上部,以实际虚构成员函数的大小写字母来表示。其他为窗口消息,以大写字母出现。选中我们要添加的消息,单击Add Funtion按钮,Class Wizard自动将该消息添加进来。
有时候,我们想要添加的消息在Message列表中找不到,我们可以利用Class Wizard上Class Info标签以扩展消息列表。在该页中,找到Message Filter组合框,通过它可以改变首页中Messages列表框中的选项。
(2)、手动添加消息
如果Messages列表框中确实没有我们想要的消息,就需要我们手工添加:
1)在类的.h文件中添加处理函数的声明,紧接着在//}}AFX_MSG行之后加入声明,注意,一定要以afx_msg开头。
通常,添加处理函数声明的最好的地方是源代码中Class Wizard维护的表的下面,在它标记其领域的{{ }}括弧外面。这些括弧中的任何东西都有可能会被Class Wizard销毁。
2)接着,在用户类的.cpp文件中找到//}}AFX_MSG_MAP行,紧接在它之后加入消息入口项。同样,也放在{{ }}外面。
3)最后,在该文件中添加消息处理函数的实体。
对于能够使用Class Wizard添加的消息,尽量使用Class Wizard添加,以减少我们的工作量;对于不能使用Class Wizard添加的消息和自定义消息,需要手动添加。总体说来,MFC的消息编程对用户来说,相对比较简单,在此不再使用实例演示。
12、消息反射机制
什么叫消息反射?
父窗口将控件发给它的通知消息,反射回控件进行处理(即让控件处理这个消息),这种通知消息让控件自己处理的机制叫做消息反射机制。
通过前面的学习我们知道,一般情况下,控件向父窗口发送通知消息,由父窗口处理这些通知消息。这样,父窗口(通常是一个对话框)会对这些消息进行处理,换句话说,控件的这些消息处理必须在父窗口类体内,每当我们添加子控件的时候,就要在父窗口类中复制这些代码。很明显,这对代码的维护和移植带来了不便,而且,明显背离C++的对象编程原则。
从4.0版开始,MFC提供了一种消息反射机制(Message Reflection),可以把控件通知消息反射回控件。具体地讲,对于反射消息,如果控件有该消息的处理函数,那么就由控件自己处理该消息,如果控件不处理该消息,则框架会把该消息继续送给父窗口,这样父窗口继续处理该消息。可见,新的消息反射机制并不破坏原来的通知消息处理机制。
消息反射机制为控件提供了处理通知消息的机会,这是很有用的。如果按传统的方法,由父窗口来处理这个消息,则加重了控件对象对父窗口的依赖程度,这显然违背了面向对象的原则。若由控件自己处理消息,则使得控件对象具有更大的独立性,大大方便了代码的维护和移植。
实例M8:简单地演示MFC的消息反射机制。(见附带源码 工程M8)
开VC++ 6.0,新建一个基于对话框的工程M8。
在该工程中,新建一个CMyEdit类,基类是CEdit。接着,在该类中添加三个变量,如下:
private:
CBrush m_brBkgnd;
COLORREF m_clrBkgnd;
COLORREF m_clrText;
在CMyEdit::CMyEdit()中,给这三个变量赋初值:
{
m_clrBkgnd = RGB( 255, 255, 0 );
m_clrText = RGB( 0, 0, 0 );
m_brBkgnd.CreateSolidBrush(RGB( 150, 150, 150) );
}
打开ClassWizard,类名为CMyEdit,Messages处选中“=WM_CTLCOLOR”,您是否发现,WM_CTLCOLOR消息前面有一个等号,它表示该消息是反射消息,也就是说,前面有等号的消息是可以反射的消息。
消息反射函数代码如下:
HBRUSH CMyEdit::CtlColor(CDC* pDC, UINT nCtlColor)
{
// TODO: Change any attributes of the DC here
pDC->SetTextColor( m_clrText );//设置文本颜色
pDC->SetBkColor( m_clrBkgnd );//设置背景颜色
//请注意,在我们改写该函数的内容前,函数返回NULL,即return NULL;
//函数返回NULL将会执行父窗口的CtlColor函数,而不执行控件的CtlColor函数
//所以,我们让函数返回背景刷,而不返回NULL,目的就是为了实现消息反射
return m_brBkgnd; //返回背景刷
}
在IDD_M8_DIALOG对话框中添加一个Edit控件,使用ClassWizard给该Edit控件添加一个CMyEdit类型的变量m_edit1,把Edit控件和CMyEdit关联起来。
windows消息机制深入详解-1相关推荐
- Windows消息机制详解-5
一. 什么是消息 在解释什么是消息之前,我们先讨论一下程序的执行机制问题.大体上说,程序按照执行机制可以分为两类: 第一类是过程驱动.比如我们最早接触编程时写的C程序,又或者单片机程序.这类程序往往预 ...
- windows消息机制详解-3
1. 引言 Windows 在操作系统平台占有绝对统治地位,基于Windows 的编程和开发越来越广泛. Dos 是过程驱动的,而Windows 是事件驱动的[6],这种差别的存在使得很多Dos 程序 ...
- DELPHI 中 Window 消息大全使用详解
Window 消息大全使用详解 导读: Delphi是Borland公司的一种面向对象的可视化软件开发工具. Delphi集中了Visual C++和Visual Basic两者的优点:容易上手.功能 ...
- mfc编程vc6.0实现进程的创建和通信_免费送书:windows黑客编程技术详解
01 书怎么送 点赞并留言,关注在下面的公众号后台回复「抽奖」,弹出小程序后点击参与. 开奖时间是 7 月 7 号 20:00 ,一定要留意微信消息,如果你中奖了,请尽快在中奖页面提交收件人信息并备注 ...
- C# Windows 窗体编程入门详解
C# Windows 窗体编程入门详解 基于Web的B/S架构应用程序近年来确实非常流行,B/S易于部署.易于维护的特点使Web应用程序开发得到了前所未有的发展.但是,Web应用程序的缺点是,它们有时 ...
- Window 消息大全使用详解(无聊没事做)
Window 消息大全使用详解(无聊没事做) 楼主zhangqu_980371(能坚持一辈子的东西太少)2004-12-19 16:35:23 在 VC/MFC / 基础类 提问 消息,就是指 ...
- Web 实时消息推送详解
title: Web 实时消息推送详解 category: 系统设计 head: meta name: keywords content: 消息推送,短轮询,长轮询,SSE,Websocket,MQT ...
- 《Windows驱动开发技术详解》学习笔记
Abstract 如果推荐 Windows 驱动开发的入门书,我强烈推荐<Windows驱动开发技术详解>.但是由于成书的时间较早,该书中提到的很多工具和环境都已不可用或找不到,而本文 ...
- Windows 消息机制浅析
Windows 消息机制浅析 1. Windows 的历史 中国人喜欢以史为鉴,而事实也确实是,如果你能知道一件事情的来龙去脉,往往可以更容易地理解事物为什么会表现为当前这样的现状.所以, ...
最新文章
- C语言链表是否为循环表的算法(附完整源码)
- mybatis配置properties属性
- [vue-cli]vue-cli3你有使用过吗?它和2.x版本有什么区别?
- 第九节:深究并行编程Parallel类中的三大方法 (For、ForEach、Invoke)和几大编程模型(SPM、APM、EAP、TAP)
- (1).数据结构概述
- hdu - 3415 Max Sum of Max-K-sub-sequence
- mysql约束_Mysql约束条件
- java 并发组件_Java 并发计数组件Striped64详解
- 最美的十大精典爱情句子
- kubectl mysql 集群_mysql-kubernetes
- 泰安出差,使用产品有所感触
- NoteExpress文献题录如何导出到excel
- Tslib1.20和Qt 4.8.4与在ARM开发板上的移植 多点触摸
- 35岁的大龄程序员都去哪里了?
- html游戏官网制作 英雄联盟LOL游戏网站设计与实现 (web课程与设计)
- html怎么画虚线空心圆,PS怎么画虚线圆圈 一个工具轻松搞定
- spyder报错汇总
- 网络工程师【软考】02
- 关于读书的名人名言,让你体会读书的好处有哪些
- Java工具类-获取请求ip/浏览器/操作系统/浏览器版本
热门文章
- PyCharm的光标插入与覆盖模式
- python 禁用网卡_如何编程实现启用禁用网卡
- 爬虫python 科研有用吗_为什么说用python写爬虫有优势?
- java oracle管理系统_哔站播放量高达三百万的Java视频教程,如此惊人,还不来学?...
- python学习-序列化对象(pickle)
- tomcat命令无法启动 the catalina_home environment variable is not defined correctly this environment variab
- python音频转文字speech recognition_使用SpeechRecognition进行语音识别
- java 文件通配符_Java中泛型通配符的使用方法示例
- flutter图片预览_Flutter 视频缩略图
- 你的安全设置不允许在您的计算机,你的安全设置不允许网站使用安装在你的计算机上的ActiveX控件...