windows消息机制

参考自:http://blog.csdn.net/sshhbb/article/details/6076156


1.消息是windows系统发送给应用程序的一个通告,它告诉应用程序某个事件发生了,最终处理消息的是应用程序的窗口函数,

如果不处理,系统会默认处理。


2.从数据结构讲,消息是一个MSG的结构体,包含了消息的类型标识符和一些附加信息。
typedef struct tagMSG
{
HWND hwnd;		
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
}MSG;


//其中hwnd 是窗口的句柄,这个参数将决定由哪个窗口过程函数对消息进行处理;message
是一个消息常量,用来表示消息的类型;wParam 和lParam 都是32 位的附加信息,具体表
示什么内容,要视消息的类型而定;time 是消息发送的时间;pt 是消息发送时鼠标所在的位
置。


3.Windows 是一消息驱动式系统;Windows 系统中有两种消息队列,一种是系统消息队列,另一种是应用程序消息队列。


4.消息处理过程:
计算机的所有输入设备由 Windows 监控,当一个事件发
生时,Windows 先将输入的消息放入系统消息队列中,然后再将输入的消息拷贝到相应的应
用程序队列中,应用程序中的消息循环从它的消息队列中检索每一个消息并发送给相应的窗
口函数中。一个事件的发生,到达处理它的窗口函数必须经历上述过程。


5.消息循环
消息循环[1]是Windows 应用程序存在的根本,应用程序通过消息循环获取各种消息,并
通过相应的窗口过程函数,对消息加以处理;正是这个消息循环使得一个应用程序能够响应
外部的各种事件,所以消息循环往往是一个Windows 应用程序的核心部分。
Windows 操作系统为每个线程维持一个消息队列,当事件产生时,操作系统感知这一事
件的发生,并包装成消息发送到消息队列,应用程序通过GetMessage()函数取得消息并存于
一个消息结构体中,然后通过一个TranslateMessage()和DispatchMessage()解释和分发消息,
while(GetMessage (&msg, NULL, 0, 0))//从系统消息队列主动获取消息,
//取得WM_QUIT 之前的返回值都为TRUE,也就是说只有获取到WM_QUIT 消息才返回FALSE,才能跳出消息循环
{
TranslateMessage (&msg) ;//解释消息
DispatchMessage (&msg) ;//分发消息,把消息发送给对应的窗口函数处理
}

6.消息处理:
分发消息之后,windows为我们预备了一个默认的窗口处理函数DefWindowProc(),这样我们可以只是处理
自己感兴趣的消息,其他的交给系统默认处理。每一个窗口类都有一个窗口过程函数,此函数是一个回调函数,
它是由Windows 操作系统负责调用的,而应用程序本身不能调用它。
对于每条已经处理过的消息都必须返回0,否则消息将不停的重试下去;对于不感兴趣的消息,
交给DefWindowProc()函数进行处理,并需要返回其处理值。
LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
…
switch(uMsgId)
{
case WM_TIMER://对WM_TIMER 定时器消息的处理过程
	return 0;
case WM_LBUTTONDOWN://对鼠标左键单击消息的处理过程
	reurn 0;
default:
return DefWindowProc(hwnd,uMsgId,wParam,lParam);
}



7.MFC消息映射的实现
在MFC 的框架结构下,“消息映射”是通过巧妙的宏定义,形成一张消息映射表格来进
行的。这样一旦消息发生,Framework 就可以根据消息映射表格来进行消息映射和命令传递。
首先在需要进行消息处理的类的头文件(.H)里,都会含有DECLARE_MESSAGE_MAP()
宏,声明该类拥有消息映射表格。
然后在类应用程序文件(.CPP)实现这一表格
BEGIN_MESSAGE_MAP(CInheritClass, CBaseClass)
//{{AFX_MSG_MAP(CInheritClass)
ON_COMMAND(ID_EDIT_COPY,OnEditCopy)
………
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

8.MFC消息处理步骤
在MFC 框架下,通过下面的步骤来对消息进行映射[7]。
1 函数AfxWndProc 接收Windows 操作系统发送的消息。
2 函数AfxWndProc 调用函数AfxCallWndProc 进行消息处理,这里一个进步是把对句柄的
操作转换成对CWnd 对象的操作。
3 函数AfxCallWndProc 调用CWnd 类的方法WindowProc 进行消息处理。
4 WindowProc 调用OnWndMsg 进行正式的消息处理,即把消息派送到相关的方法中去处理。
5 如果OnWndMsg 方法没有对消息进行处理的话,就调用DefWindowProc 对消息进行处理。
这就是MFC 对消息调用过程的巧妙封装。

9.MFC消息类型
1 命令消息(WM_COMMAND)
比如菜单项的选择,工具栏按钮点击等发出该消息。所有派生自CCmdTarget 的类都有
能力接收WM_COMMAND 消息。
2 标准消息(WM_XXX)
比如窗口创建,窗口销毁等。所有派生自CWnd 的类才有资格接收标准消息。
3 通告消息(WM_NOTIFY)
这是有控件向父窗口发送的消息,标示控件本身状态的变化。比如下拉列表框选项的改
变CBN_SELCHANGE 和树形控件的TVN_SELCHANGED 消息都是通告消息。
Window 9x 版及以后的新控件通告消息不再通过WM_COMMAND 传送,而是通过
WM_NOTIFY 传送, 但是老控件的通告消息, 比如CBN_SELCHANGE 还是通过
WM_COMMAND 消息发送。
4 自定义消息
利用MFC 编程,可以使用自定义消息。使用自定义消息需要遵循一定的步骤[2]并需要
自己编写消息响应函数


从消息队列获取消息:
可以通过PeekMessage或GetMessage函数从Windows消息队列中获取消息。
Windows保存的消息队列是以线程(Thread)来分组的,也就是说每个线程都有自己的消息队列。
1、从消息队列获取消息:
可以通过PeekMessage或GetMessage函数从Windows消息队列中获取消息。Windows保存的消息队列是以线程(Thread)来分组的,也就是说每个线程都有自己的消息队列。
2、发送消息
发送消息到指定窗体一般通过以下两个函数完成:SendMessage和PostMessage。两个函数的区别在于:PostMessage函数只是向线程消息队列中添加消息,如果添加成功,则返回True,否则返回False,消息是否被处理,或处理的结果,就不知道了。而SendMessage则有些不同,如果在同一个线程里,它并不把消息加入到队列里,而是直接翻译消息和调用消息处理(线程向自己发送消息才是这样),直到消息处理完成后才返回。所以,如果我们希望发送的消息立即被执行,就应该调用SendMessage。
还有一点,就是SendMessage发送的消息由于不会被加入到消息队列中(错:线程向其他线程发送消息也是追加到其他线程的发送消息队列的,即使这两个线程在同一个进程也是如此),所以通过PeekMessage或GetMessage是不能获取到由SendMessage发送的消息。
另外,有些消息用PostMessage不会成功,比如wm_settext。所以不是所有的消息都能够用PostMessage的。 


PeekMessage和GetMessage的区别
1.GetMessage的主要功能是从消息队列中“取出”消息,消息被取出以后,就从消息队列中将其删除;
而PeekMessage的主要功能是“窥视”消息,如果有消息,就返回true,否则返回false。也可以使用PeekMessage从消息队列中取出消息,这要用到它的一个参数(UINT wRemoveMsg),如果设置为PM_REMOVE,消息则被取出并从消息队列中删除;如果设置为PM_NOREMOVE,消息就不会从消息队列中取出。
2.如果GetMessage从消息队列中取不到消息,则线程就会被操作系统挂起,等到OS重新调度该线程时,两者的性质不同:使用GetMessage线程仍会被挂起,
使用PeekMessage线程会得到CPU的控制权,运行一段时间。
3. GetMessage每次都会等待消息,直到取到消息才返回;而PeekMessage只是查询消息队列,没有消息就立即返回,从返回值判断是否取到了消息。


PostMessage和SendMessage的区别 
1 PostMessage 是异步的,SendMessage 是同步的。
PostMessage 只把消息放到队列,不管消息是不是被处理就返回,消息可能不被处理;
SendMessage等待消息被处理完了才返回,如果消息不被处理,发送消息的线程将一直处于阻塞状态,等待消息的返回。


2 同一个线程内:
SendMessage 发送消息时,由USER32.DLL模块调用目标窗口的消息处理程序,并将结果返回,SendMessage 在同一个线程里面发送消息不进入线程消息队列;PostMessage 发送的消息要先放到消息队列,然后通过消息循环分派到目标窗口(DispatchMessage)。


3 不同线程:
SendMessage 发送消息到目标窗口的消息队列,然后发送消息的线程在USER32.DLL模块内监视和等待消息的处理结果,直到目标窗口的才处理返回,SendMessage在返回之前还需要做许多工作,如响应别的线程向它发送的SendMessage().PostMessge() 到别的线程的时候最好使用PostThreadMessage   代替。PostMessage()的HWND 参数可以为NULL,相当于PostThreadMessage() + GetCrrentThreadId.


4 系统处理消息。
系统只处理(marshal)系统消息(0--WM_USER),发送用户消息(用户自己定义)时需要用户自己处理。
使用PostMessage,SendNotifyMessage,SendMessageCallback等异步函数发送系统消息时,参数不可以使用指针,因为发送者不等待消息的处理就返回,接收者还没有处理,指针就有可能被释放了,或则内容变化了。

5 在Windows 2000/XP,每个消息队列最多只能存放一定数量的消息,超过的将不会被处理就丢掉。系统默认是10000;[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows] USERPostMessageLimit 可以修改






























  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值