文章作者:灰狐 (Bink&&E.S.T)
信息来源:邪恶八进制信息安全团队(
www.eviloctal.com)
注:文章首发灰狐's Blog (Www.HuiHu32.cN),后由原创作者友情提交到邪恶八进制信息安全团队技术论坛。
本文绝大部分内容来源于《C++ Builder 高级应用开发指南》,清华大学出版社出版,李幼仪、甘志 编著,版权归他们所有。作者仅仅对其进行了拷贝,不享有除学习之外的任何权利。
本文以及作者其他系列文章中出现的所有程序代码均经过严格的验证,如果没有特别说明即是在Win XP SP2 + C++ Builder 6.0的环境下测试通过。
一、消息的基本概念
消息(Message)指的就是Windows操作系统发给应用程序的一个通告,它告诉应用程序某个特定的事件发生了。比如,用户单击鼠标或按键都会引发Windows系统发送相应的消息。最终处理消息的是应用程序的窗口函数,如果程序不负责处理的话系统将会作出默认处理。
从数据结构的角度来说,消息是一个结构体,它包含了消息的类型标识符以及其他的一些附加信息。比如对于鼠标单击产生的消息而言,它就包含了窗口句柄、此消息的常量标识符、鼠标的位置坐标等相关信息。
Windows系统定义了许多消息常量,包括标准的Windows消息、通知消息、命令消息等等。这些消息常量通常具有XX_YYYY的形式,其他XX通常代表消息的类型,而后面的YYYY通常是这个消息所对应事件的英文缩写。比如WM_LBUTTONDOWN代表的事件就是按下了鼠标左键。
二、Windows的消息系统
Windows的消息系统由3个部分组成:
消息队列。Windows操作系统本身维护了一个系统消息队列,而对于每一个正在执行的Windows应用程序,系统会为其建立一个应用程序消息队列。应用程序可以从这个消息队列中获取消息,然后分派给对应的窗口。
消息循环。Windows应用程序中都包含了一段称作“消息循环(也称消息泵)”的代码,用来从消息队列中检索消息并把他们分发到相应的窗口函数中。正是这个消息循环使得一个应用程序能够响应外部的各种事件,所以消息循环往往是一个Windows应用程序的核心部分。
窗口函数。最终为了处理各种消息,Windows应用程序所创建的每个窗口(广义,包括实际窗口、控件等诸如此类的的内容)都会在系统中注册一个相应的窗口函数,此窗口函数从形式上看一个巨大的switch语句,用以处理由消息循环发送到该窗口的各种消息。窗口函数是一种回调函数(Callback Function),也就是说,它是由Windows操作系统负责调用的,而应用程序本身不能调用它。
Windows操作系统中的消息从发生到被处理一般有5个步骤:
(1)系统发生了一个事件。
(2)Windows系统把事件翻译为对应的消息,并把它放到消息队列中。
(3)应用程序从消息队列中获取消息,然后把它封装在TMsg结构中。
(4)应用程序通过消息循环把消息分派给对应的窗口函数。
(5)窗口函数负责最终处理这个消息。
三、C++ Builder中的OnMessage事件
C++ Builder为了方便消息处理,进一步把常用的消息封装成了各种相应事件,这样程序员通常情况下就无需考虑消息的具体细节,只要编写相应的事件处理函数即可。这就是所谓的基于事件的Windows程序开发。
在C++ Builder开发的应用程序中,任何窗体接收到一个Windows消息都会触发一次OnMessage事件,所以可以通过响应TApplication对象的OnMessage事件来捕获任何发送给本程序的Windows消息。
OnMessage事件的处理函数原型如下:
typedef void __fastcall (__closure *TMessageEvent)(tagMSG &Msg,bool &Handled);
这个函数有两个参数,其中参数Msg表示的是被截获的消息,而参数Handled则用来指示本消息是否已经处理完成。在程序中可以通过设置参数Handled为true以避免后续过程处理这个消息,反之亦然。
需要注意的是,OnMessage事件仅仅接受发送到消息队列中的消息,而直接使用API函数 SendMessage()发送给窗口函数的消息将不会被截获到。另外,当程序运行的时候,OnMessage事件被触发的频率一般非常高,所以这个事件处理函数中的代码执行事件将直接影响到整个程序的运行效率。
下面我们通过一个实例来演示OnMessage事件的处理过程。在这个范例中,程序将累计发生的OnMessage事件次数并显示在窗体上。
我们在主窗体上放置一个Label组件,命名为Label1,用于显示消息发生次数。
程序中全部采用默认名字。
在窗体类头文件Unit1.h中添加以下代码:
private: // User declarations
long num;
void __fastcall AppMessage(tagMSG &Msg,bool &Handled);
然后在Unit1.cpp中添加以下代码:
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
num=0;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
Application->OnMessage=AppMessage;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::AppMessage(tagMSG &Msg,bool &Handled)
{
num++;
Label1->Caption=AnsiString(num);
Handled=false;
}
//---------------------------------------------------------------------------
其中Application->OnMessage=AppMessage;的作用是把事件处理函数AppMessage()和OnMessage事件联系起来。Handled=false;是为了让后续过程能继续处理消息,以避免窗口无法正常接收消息而引起死锁。
最后按F9运行程序,可以看到窗体上显示出了消息事件发生的次数,当你移动鼠标或按下按键的时候它都会不断地变化。
本文到此结束,敬请关注续篇:Windows消息高级应用。