C++ Builder中消息处理过程及应用 （作者：张道新） C++ Builder作为一种RAD方式的程序开发工具，其全新的可视化编程环境、面向组件的开发模式无疑会大大地提高编程效率。它对繁杂的Windows 消息及API作了较全面的封装，编程者在大多数情况下不需理会Windows消息的细节，只要将心思放在组件的事件处理函数上即可。然而，毕竟Windows操作系统是一个以消息驱动的系统，运行其上的应用程序，自然无法脱离系统之外，因此掌握并运用消息处理，对一些问题的处理会有事半功倍的效果。 尽管C++ Builder的VCL控件封装了大多数常用的消息，C++ Builder所提供的事件处理能力也具备了相当程度的完备性，但当处理C++ Builder 未定义的Windows消息或自定义消息时，掌握C++ Builder 的内部消息处理机制还是十分必要的。下面，从Windows 操作系统消息驱动机制开始，进而探讨C++ Builder的VCL控件中消息的封装、传递和处理机制，最后以新增消息处理过程的应用实例作为对所讲内容的验证和实践。 一、Windows 消息驱动机制 Windows是以消息驱动的操作系统，Windows 消息提供了应用程序与应用程序以及应用程序与Windows系统之间进行通讯的手段。 Windows 中有一个系统消息队列，对于每一个正在执行的Windows应用程序,系统为其建立一个“消息队列”，即应用程序队列，用来存放该程序可能创建的各种窗口的消息。应用程序中含有一段称作“消息循环”的代码，用来从消息队列中检索这些消息并把它们分发到相应的窗口函数中。 消息循环代码是应用程序中主函数winmain ( )中类似如下的程序段： while(GetMessage(&&msg,NULL,NULL,NULL)) { //从消息队列中取得消息 TranslateMessage(&&msg); //检索并生成字符消息WM_CHAR DispatchMessage(&&msg); //将消息发送给相应的窗口函数 } 由此可见，所谓“消息循环”,实际是程序循环。 Windows 应用程序创建的每个窗口都在系统核心注册一个相应的窗口函数，窗口函数程序代码形式上是一个巨大的switch 语句，用以处理由消息循环发送到该窗口的消息，窗口函数由Windows 采用消息驱动的形式直接调用，而不是由应用程序显示调用的，窗口函数处理完消息后又将控制权返回给Windows。 系统消息队列、应用程序队列、消息循环和窗口函数之间的关系如图1所示。 二、C++ Builder 中的消息处理 有了以上Windows 系统消息驱动模式程序设计的认识，下面分析一下C++ Builder中消息处理是如何封装、实现的。 Windows 程序框架，包括一些初始化、消息循环代码等，在类 Application中封装、实现。每一个用C++ Builder 编写的Windows GUI 应用程序，大部分缺省生成如下代码： WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) //Windows 应用程序主函数 { try { Application-〉Initialize();//作初始化 Application-〉CreateForm(__classid(TForm1), &&Form1); Application-〉Run(); //其中包含消息循环 } catch (Exception &&exception)　　//例外处理 { Application-〉ShowException(&&exception); } return 0; } 对于消息处理，C++ Builder采用基于控件(component)的程序设计模式，每种控件都继承一套完整的消息派送体系。其实现方法如下： 它为每一种类型的控件都注册一个名为 MainWndProc 的方法函数作为窗口函数，接受“消息循环”派送来的消息，它是一个非虚拟方法,不对任何特定消息作特别处理，它仅仅调用 WndProc 方法函数，并作一些例外处理。不同控件对消息处理的定制发生在WndProc 方法中，因为它是一个虚拟方法，每一种控件可以通过覆盖它来适应特别的情况。WndProc 方法检查不同的条件，作不同的处理，从而能够滤掉不希望处理的各种消息。例如：当控件正被拖动时，应忽略键盘事件，所以在Twincontrol 类的WndProc 方法中，有判断当控件不是被拖放状态、才继续传递键盘消息这样功能的代码。最终，WndProc 调用 Dispatch 方法，它是一个从所用控件的起始祖先Tobject 继承而来的虚拟方法，它确定调用哪个方法处理传来的消息。Dispatch 使用消息结构(Tmessage)中的 msg 成员变量确定如何处理一个特定的消息，如果控件定义了处理这一消息的函数，则调用它，否则，就逐级向上追溯，看祖先类是否定义此类的处理方法，直到起始祖先类(Tobject)。如果都没有定义处理方法，则调用缺省的处理方法(DefaultHandler)。 以上是消息在控件中的传递过程，INPRISE公司为方便用户，对消息处理作了进一步的封装，把常用的消息封装成相应的事件属性，这样编程者完全不用考虑消息细节，只要编写事件处理方法，并给事件属性赋值即可。 三、应用实例 下面以增加新的自定义消息处理过程为例，对以上所述内容做进一步的说明。 通过以上分析我们知道，每一条消息的具体处理过程，是在 Dispatch 中派发完成的，因此增加新的消息， 只要覆盖虚拟函数 Dispatch 即可。 C++ Builder为了方便地处理消息，定义了以下三个处理消息的宏： BEGIN_MESSAGE_MAP VCL_MESSAGE_HANDLER(msg,type,meth) END_MESSAGE_MAP(base) 定义如下： #define BEGIN_MESSAGE_MAP virtual void __fastcall Dispatch(void Message) {　　　　　　　　　　　　　　　　　　　　 switch (((PMessage)Message)-〉Msg) { #define VCL_MESSAGE_HANDLER(msg,type,meth) case　　msg: meth(((type)Message));　　　　　　 break; #define END_MESSAGE_MAP(base)　　default: base::Dispatch(Message); break; } } 我们只需在控件类或自定义控件类的public节，依次写入三个宏即可，其中宏VCL_MESSAGE_HANDLER可以根据处理消息的条数而出现多次。宏展开后，即生成一个新的Dispatch 函数，它先判断处理用户定义消息，若是其他消息，则传递至父类的Dispatch 函数处理，从而完成自定义消息的处理并保证原来消息处理体系的完整性。