⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chap11.htm

📁 c++builder入门经典,二十一天学会c++builder,祝你成功学会这一经典开发工具
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<HTML><HEAD><TITLE> </TITLE><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb2312"><META NAME="GENERATOR" CONTENT="Internet Assistant for Microsoft Word 2.0z"></HEAD><BODY><P><FONT SIZE=2>以C++ Builder处理Windows 讯息(Message)<BR></FONT><P><FONT SIZE=2>前言<BR></FONT><P><FONT SIZE=2>虽然C++Builder为一RAD式的程式发展工具,程式设计师在大多数情况下不需理会Windows讯息的细节,只要将心思放在软体元件的事件处理函式即可。然而由於Windows作业系统终究是一个以讯息驱动的系统,因此架构其上的的应用程式自然无法自外於系统之外,在遭遇到C++Builder没有定义的事件时,Windows讯息处理能力仍然是C++Builder程式人不可或缺的能力。<BR></FONT><P><FONT SIZE=2>不可否认地,C++Builder所提供的事件处理能力已具备了某一程度的完备性,然而我们也必须承认,在C++Buider建构的VCL美丽新世界中,仍然不免有漏网之鱼。例如使用者自定讯息的处理,Winsock讯息的处理及一些Windows讯息如WM_NC****系列的讯息都是C++Builder的物件模型所未包含的。<BR></FONT><P><FONT SIZE=2>在本文中我将告诉你如何以C++Builder来处理Windows讯息,并透过此一能力,来达成在一般VCL元件所无法做到的功能。<BR></FONT><P><FONT SIZE=2>何谓Window讯息(Message)<BR></FONT><P><FONT SIZE=2>大家都知道 Windows是一套以讯息驱动(Message Driven)的作业系统。然而对於讯息本身却讳莫如深,只知其然而不知其所以然,虽然C++Builder将某些Windows讯息封装於事件(Event)系统中,但身为一个Windows程式设计师,实有必要了解Windows的讯息系统。<BR></FONT><P><FONT SIZE=2>所谓讯息是由Windows作业系统送往程式的事件。它是系统中各个物件沟通的方式,举例来说,当移动滑鼠、按下滑鼠键、改变视窗大小时,Windows都会送出讯息以通知程式。当然,为了要辨别事件的内容,Windows系统中定义了许多的讯息,如WM_PAINT,WM_CHAR等等。<BR></FONT><P><FONT SIZE=2>当事件发生时,Windows会判断该事件必须由那个程式接收,然後将事件以讯息的方式送往程式的视窗中。虽然在Windows系统中包含了数以百计的事件,但是作业系统并没有为各个事件设计不同的讯息结构,而是以一个一般性的结构来描述讯息,这个结构在C++Builder就称是TMessage。<BR></FONT><P><FONT SIZE=2>当然,随着事件的不同,对於讯息的解释也有所不同,在C++Builder中也为各种常用的讯息定义了专属的结构,你可以直接使用它们来解释讯息。这些讯息定义在C++Builder目录下的Include\vcl\messages.hpp中,你可以决定要自行解释TMessage参数或是直接将其转换成专属的结构。很抽象吗?我举个例子吧,以WM_NCHITTEST讯息来说,C++Builder为它定义了TWMNCHitTest的专属结构,所以你可以直接经由它来得到XPos、YPos等值。或者你也可以直接由TMessage的LParam取得其值,端看你使用的方便。仔细观察TMessage及TWMNCHitTest两个结构,你会发现它们是等价的,也就是说它们的大小是一致的,因此你可以直接用强制转型互相转换(这有点类似union的方法)。<BR></FONT><P><FONT SIZE=2>struct TMessage</FONT><P><FONT SIZE=2>{</FONT><P><FONT SIZE=2>        Cardinal Msg;</FONT><P><FONT SIZE=2>        union</FONT><P><FONT SIZE=2>        {</FONT><P><FONT SIZE=2>                struct</FONT><P><FONT SIZE=2>                {</FONT><P><FONT SIZE=2>                        Word WParamLo;</FONT><P><FONT SIZE=2>                        Word WParamHi;</FONT><P><FONT SIZE=2>                        Word LParamLo;</FONT><P><FONT SIZE=2>                        Word LParamHi;</FONT><P><FONT SIZE=2>                        Word ResultLo;</FONT><P><FONT SIZE=2>                        Word ResultHi;</FONT><P><FONT SIZE=2>                };</FONT><P><FONT SIZE=2>                struct</FONT><P><FONT SIZE=2>                {</FONT><P><FONT SIZE=2>                        long WParam;</FONT><P><FONT SIZE=2>                        long LParam;</FONT><P><FONT SIZE=2>                        long Result;</FONT><P><FONT SIZE=2>                };</FONT><P><FONT SIZE=2>        };</FONT><P><FONT SIZE=2>};</FONT><P><FONT SIZE=2>struct TWMNCHitTest</FONT><P><FONT SIZE=2>{</FONT><P><FONT SIZE=2>        Cardinal Msg;</FONT><P><FONT SIZE=2>        long Unused;</FONT><P><FONT SIZE=2>        union</FONT><P><FONT SIZE=2>        {</FONT><P><FONT SIZE=2>                struct</FONT><P><FONT SIZE=2>                {</FONT><P><FONT SIZE=2>                        Windows::TSmallPoint Pos;</FONT><P><FONT SIZE=2>                        long Result;</FONT><P><FONT SIZE=2>                };</FONT><P><FONT SIZE=2>                struct</FONT><P><FONT SIZE=2>                {</FONT><P><FONT SIZE=2>                        short XPos;</FONT><P><FONT SIZE=2>                        short YPos;</FONT><P><FONT SIZE=2>                };</FONT><P><FONT SIZE=2>        };</FONT><P><FONT SIZE=2>} ;<BR></FONT><P><FONT SIZE=2>在收到讯息後,程式必须处理该讯息,若是不处理,则可直接将它交给Windows的内定处理程序来处理之,若是程式需要传回值,也可以在此时传回,Windows会将该值传回给呼叫方。如此就完成了讯息传递的程序。<BR></FONT><P><FONT SIZE=2>复杂吗?一点也不!了解Windows讯息系统的运作後,我们来看看可以利用它来做些什麽有趣的事吧!<BR></FONT><P><FONT SIZE=2>讯息使用范例一   使用者自定标题棒的实作<BR></FONT><P><FONT SIZE=2>一般Windows程式的标题棒位於视窗的上方,我们可以利用该标题棒来移动视窗。以下我将为你示范如何利用C++Builder实作出置於视窗左方的标题棒。如图一:<BR></FONT><P><IMG SRC="IMG00001.GIF"><P><FONT SIZE=2>图一 标题棒在左方的视窗。<BR></FONT><P><FONT SIZE=2>如上图,你可以很清楚地看到,这个视窗和其他的视窗有很大的不同;它的标题棒位於左方,而且其颜色为绿色,同时其文字的走向为由下而上的90度字形,而其功能则和一般的标题棒相同,你可以将滑鼠移至该处,然後移动该视窗。到底这是如何达成的呢?<BR></FONT><P><FONT SIZE=2>WM_NCHITTEST讯息的奥秘<BR></FONT><P><FONT SIZE=2>WM_NCHITTEST讯息是一个很特殊的讯息。它是用来决定目前滑鼠所在位置属性的讯息,因此我们可以利用此特性,当滑鼠移至指定的位置时,传回HTCAPTION,使得系统以为滑鼠目前位於标题棒,如此你就可以移动视窗了。如何?是不是很神奇呢?<BR></FONT><P><FONT SIZE=2>由上可知,只要我们适时地拦截WM_NCHITTEST讯息,然後传回HTCAPTION,就可以顺利地欺骗系统,达成在任何位置模拟出标题棒的效果。<BR></FONT><P><FONT SIZE=2>C++ Builder的处理讯息的巨集<BR></FONT><P><FONT SIZE=2>在C++Builder为了处理讯息的方便,因此定义了叁个处理讯息的巨集(Macro)。<BR></FONT><P><FONT SIZE=2>BEGIN_MESSAGE_MAP</FONT><P><FONT SIZE=2>MESSAGE_HANDLER(WM_NCHITTEST,TMessage,OnNcHitTest)</FONT><P><FONT SIZE=2>END_MESSAGE_MAP(TForm)<BR></FONT><P><FONT SIZE=2>以上的叁个巨集BEGIN_MESSAGE_MAP、MESSAGE_HANDLER及END_MESSAGE就是C++Builder定义的巨集,其中比较重要的是MESSAGE_HANDLER;它共需要叁个参数,第一个参数代表讯息的ID,第二个代表参数型态,最後一个则是讯息事件处理函数。<BR></FONT><P><FONT SIZE=2>乍看之下,这个巨集似乎和MFC及OWL所使用的巨集有几分神似,没错,不过其机制却更为简单及简洁,我们可以看看C++Builder对於这叁个巨集的原始定义:<BR></FONT><P><FONT SIZE=2>#define BEGIN_MESSAGE_MAP       virtual void __fastcallDispatch(void *Message) \</FONT><P><FONT SIZE=2>                                {                                              \</FONT><P><FONT SIZE=2>                                        switch  (((PMessage)Message)-&gt;Msg)     \</FONT><P><FONT SIZE=2>                                        {</FONT><P><FONT SIZE=2>#define MESSAGE_HANDLER(msg,type,meth)                                         \</FONT><P><FONT SIZE=2>                                        case    msg:                           \</FONT><P><FONT SIZE=2>                                                meth(*((type*)Message));       \</FONT><P><FONT SIZE=2>                                                break;</FONT><P><FONT SIZE=2>#define END_MESSAGE_MAP(base)           default:                               \</FONT><P><FONT SIZE=2>                                                               base::Dispatch(Message);        \</FONT><P><FONT SIZE=2>                                                       break;                          \</FONT><P><FONT SIZE=2>                                        }                                                                                     \</FONT><P><FONT SIZE=2>                                }<BR></FONT><P><FONT SIZE=2>相较於MFC或 OWL的可怕巨集,它实在是简单多了,这是因为C++Builder已替你完成了大部份的工作。其实若我们把以上的巨集展开後,可以得到以下的结果:<BR></FONT><P><FONT SIZE=2>virtual void __fastcall Dispatch(void *Message)</FONT><P><FONT SIZE=2>{</FONT><P><FONT SIZE=2> switch  (((PMessage)Message)-&gt;Msg)</FONT><P><FONT SIZE=2> {</FONT><P><FONT SIZE=2>        case    WM_NCHITTEST:</FONT><P><FONT SIZE=2>                OnNcHitTest(*((TMessage *)Message));</FONT><P><FONT SIZE=2>                break;</FONT><P><FONT SIZE=2>        default:</FONT><P><FONT SIZE=2>                TForm::Dispatch(Message);</FONT><P><FONT SIZE=2>                break;</FONT><P><FONT SIZE=2>    }</FONT><P><FONT SIZE=2>}<BR></FONT><P><FONT SIZE=2>怎麽样?展开之後是不是有恍然大悟的感觉,要弄清楚这个巨集在卖啥膏药是很容易的,如果你玩过MFC的讯息处理机制,再看到以上的巨集,相较之下,实在是小儿科,不过也就因其简单,所以C++Builder的优势益加彰显。<BR></FONT><P><FONT SIZE=2>我简单地说明以上的程式:在每个TForm中都定义一个名为Dispatch的虚拟函式,它就是用来处理Windows的讯息的,在大部份情况下,讯息都是呼叫C++Builder所提供的处理函式,因此你不需要修改它。<BR></FONT><P><FONT SIZE=2>换句话说,我们只要改写Dispatch函式,就可以藉以处理指定的讯息了。前面提到的叁个巨集只是将这个程序简化而已,没什麽大不了。<BR></FONT><P><FONT SIZE=2>自定标题的绘制<BR></FONT><P><FONT SIZE=2>由於我们要使用自定的标题,所以你必须将程式所使用的TForm的BorderStyle性质设为 bsNone,如此你的TForm就不会有标题棒了。<BR></FONT><P><FONT SIZE=2>再来你就必须自行绘制标题棒,我们希望绘制一个位於左於的标题,因此我们必须处理TForm的OnPaint事件,然後在此事件中绘制标题棒。以下即为其事件处理函式:<BR></FONT><P><FONT SIZE=2>void __fastcall TForm1::FormPaint(TObject *Sender)</FONT><P><FONT SIZE=2>{</FONT><P><FONT SIZE=2>    RECT rc;</FONT><P><FONT SIZE=2>    ::SetRect(&amp;rc,0,0,ClientWidth,ClientHeight);</FONT><P><FONT SIZE=2>    DrawButtonFace(Canvas,rc,1);</FONT><P><FONT SIZE=2>    Canvas-&gt;Pen-&gt;Color=clGreen;</FONT><P><FONT SIZE=2>    Canvas-&gt;Brush-&gt;Color=clGreen;</FONT><P><FONT SIZE=2> Canvas-&gt;Rectangle(0,0,20,ClientHeight);</FONT><P><FONT SIZE=2>:// 以下略去</FONT><P><FONT SIZE=2>:</FONT><P><FONT SIZE=2>}<BR></FONT><P><FONT SIZE=2>你可以看到,我们画出一个宽为20,颜色为绿色的标题棒。因此我们处理WM_NCHITTEST讯息的处理函式也必须做相对应的修改:<BR></FONT><P><FONT SIZE=2>void __fastcall TForm1::OnNcHitTest(TMessage&amp;Msg)</FONT><P><FONT SIZE=2>{</FONT><P><FONT SIZE=2>    TPoint pt;</FONT><P><FONT SIZE=2>    pt.x=LOWORD(Msg.LParam);</FONT><P>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -