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