C Builder中如何利用消息
规范的BCB过程利用Application->Run()进去消息循环,在Application的ProcessMessage措施中,利用PeekMessage措施从消息队列中提取消息,并将此消息从消息队列中移除。然后ProcessMessage措施察看是否存在Application->OnMessage措施。存在则转入此措施处理消息。尔后再将处理过的消息发放给过程中的各个对象。至此,WndProc措施收到消息,并举行处理。万一有无法处理的交给重载的Dispatch措施来处理。要是还不能处理的话,再交给父类的Dispatch措施处理。最后Dispatch措施切实上将消息转入DefaultHandler措施来处理。(嘿嘿,切实上,你一样能够重载DefaultHandler措施来处理消息,然而太晚了一点,我想未曾人甘心最后一个处理消息吧)。
1.TApplication的OnMessage事件的利用
在C++Builder开发的利用过程中,任何窗体接收到一个Windows消息都会引发顺次OnMessage事件,因而,能够穿越相应TApplication对象的OnMessage事件来捉拿任何发送给本过程的Windows消息。
OnMessage的事件的处理函数原型如下:
typedef void __fastcall (__closure *TMessageEvent ) (tagMsg &Msg,bool &Handled );
这个处理函数有两个参数,其中参数Msg表示的是被截获的消息,而参数Handled则用来指示本消息是否曾经处理告终。在过程中能够穿越设置参数Handled为true,以避免后续的过程处理这个消息,反之把Handled设为false则批准后继过程继续处理这个消息。
必需当心的是,OnMessage事件仅仅接受发送到消息队列的消息,而直接穿越API函数SendMessage()发送给窗口函数的消息将不会被截获。另外,当过程运行的时候,OnMessage事件被引发的频率有可能极其高,因而这个事件的处理函数代码厉行工夫将直接波及到全副过程的运行效率。
2.利用消息照射截获消息
C++Builder的VCL供给了对大多数Windows消息的处理机制,对于等闲的利用过程是足够了。然而,VCL也不是森罗万象的。有的情形下,过程必需处理那些VCL处理未曾处理的Windows消息,可能过程必需屏障某些特定的消息时,则就必需过程员自己捉拿Windows消息。
为此C++Builder供给了一种消息照射机制,穿越消息照射过程能将特定的Windows消息与对应的处理函数联系起来,当窗口捉拿到这个消息时就会积极调用对应的处理函数。
利用消息照射有一下几个环节:
(1) 消息照射表,把某些消息的处理权交给自定义的消息处理函数。
这么的消息照射列表该当位于一个组件类的定义中,它以一个未曾参数的BEGIN_MESSAGE_MAP 宏开始,以END_MESSAGE_MAP宏告终。END_MESSAGE_MAP宏的单一参数该当是组件的父类的名字。通常情形下,这个所谓的父类指的即便TForm。在宏BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间插入一个可能是多个MESSAGE_HANDLER 宏。
MESSAGE_HANDLER宏将一个消息句柄和一个消息处理函数联系在同时。
MESSAGE_HANDLER宏有三个参数:Windows消息名、消息构造体名和对应的消息处理函数名。其中,消息构造体名既能够是通用的消息构造体TMessage,也能够是特定的消息构造体,例如TWMMouse。
在利用消息照射的时候要当心以下两点:
a.一个窗口类定义中只能有一个消息照射表。
b.消息照射定然位于它所引用的所有消息处理函数声明的后面。
(2) 在窗口类中声明消息处理函数
这里的消息处理函数名和参数都定然和对应的MESSAGE_HANDLER宏统一。
一个标兵的消息处理函数的声明如下:
void __fastcall 消息处理函数名(消息构造体名 &Message);
例如:
void __fastcall WMNchitTest(TMessage &message);
(3) 告终消息处理函数
消息处理函数的编制和等闲的函数没什么太大的差异,单一不同的是,等闲在此函数的最后要加上一条语句 TForm::Dispatch(&Message),以告终VCL对于消息的默认处理。万一未曾这一句,消息将会被全面堵截;在某些情形下,VCL可能会因为得不到消息而无法工作。
3.重载WndProc()函数
在某些情形下,过程必需捉拿可能屏障某些特定的消息,这时能够用前面推荐的消息照射的措施。当然,这种措施也不是单一的,也能够穿越重载窗口函数WndProc()来告终。因为系统将在调用函数Dispatch()派发消息之前调用窗口函数WndProc(),因而,能够穿越重载函数WndProc()获得一个在分配消息之前过滤消息的时机。
这个消息处理的窗口函数的原型如下:
virtual void __fastcall WndProc(TMessage &Message);
例如:(翔实请看NowCane452.com的例子)
void __fastcall TForm1::WndProc(TMessage &Message)
{
PCOPYDATASTRUCT pMyCDS;
if(Message.Msg==g_MyMsg)
{
ShowMessage("收到登记消息,wParam="+IntToStr(Message.WParam)+" lParam="+IntToStr(Message.LParam));
Message.Result=0;//消息处理的收获,当然在本例中没故含义。
}
else if(Message.Msg==g_MyMsg1)
{
Application->MessageBoxA((char *)Message.LParam,"收到发送方的字符串",MB_OK);
}
else if(Message.Msg==WM_COPYDATA)
{
pMyCDS = (PCOPYDATASTRUCT)Message.LParam;
Application->MessageBoxA((char *)pMyCDS->lpData,"收到发送方的字符串",MB_OK);
}
TForm::WndProc(Message);//其他的消息继续递交下去
}
乍看起来,这和重载Dispatch措施好象差不多。但切实上还是有差异的。差异就在前后次序上,消息是先交给WndProc来处理,最后才调用Dispatch措施的。这么,重载WndProc措施能够比重载Dispatch措施更早一点点获得消息并处理消息。
4.Application->HookMainWindow措施
万一您计划利用Application->OnMessage来捉拿所有发送至您的利用过程的消息的话,您可能要绝望了。它无法捉拿利用SendMessage直接发送给窗口的消息,因为这不穿越消息队列。您可能会说我能够直接重载TApplication的WndProc措施。呵呵,不能够。因为TApplication的WndProc措施被Borland声明为静态的,从而无法重载。显而易见,这么做的起因很可能是Borland担心其所带来的副作用。那该如何是好呢?察看TApplication的WndProc的pascal源码能够看到:
procedure TApplication.WndProc(var Message: TMessage);
... // 节俭篇幅,这里与主题无关代码略去
begin
try
Message.Result := 0;
for I := 0 to FWindowHooks.Count - 1 do
if TWindowHook(FWindowHooks[I]^)(Message) then Exit;
... // 节俭篇幅,这里与主题无关代码略去
WndProc措施一开始先调用HookMainWindow挂钩的自定义消息处理措施,然后再调用缺省过程处理消息。这么利用HookMainWindow就能够在WndProc其中接加入自己的消息处理措施。利用这个措施响应SendMessage发送来的消息很管用。最后提醒一下,利用HookMainWindow挂钩尔后定然要对应的调用UnhookMainWindow卸载钩子过程。给个例子:
void __fastcall TForm1::FormCreate(TObject *Sender)
{
Application->HookMainWindow(AppHookFunc);
}
//---------------------------------------------------------------------------
bool __fastcall TForm1::AppHookFunc(TMessage &Message)
{
bool Handled ;
switch (Message.Msg)
{
case WM_CLOSE:
mrYes==MessageDlg(xg.sy-xghg.com"Really Close??", mtWarning, TMsgDlgButtons() << mbYes <
Handled = false : Handled = true ;
break;
}
return Handled;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
Application->UnhookMainWindow(AppHookFunc);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
SendMessage(Application->Handle,WM_CLOSE,0,0);
}
//---------------------------------------------------------------------------
5.自己发送消息
利用过程也能够像Windows系统一样在窗口可能是组件之间发送消息。C++Builder为此供给了几种门径:利用函数TControl::Perform()可能API函数SendMessage()和PostMessage()向特定的窗体发送消息,可能是利用函数TWinControl::Broadcast()和API函数BroadcastSystemMessage()广播消息。
Perform()函数的作用即便将指定的消息递交给TControl的WndProc过程,实用于所有由TControl类派生的对象,Perform()原型如下:
int __fastcall Perform(unsigned Msg, int WParam, int LParam);
要等到消息处理尔后才归来。
在统一个利用过程的不同学体和控件之间利用函数Perform()是极其方便的。然而这个函数是TControl类的成员函数。也即便说,利用它时,过程定然懂得这个接受消息的控件的实例。而在众多情形下过程并不懂得这个接受消息的窗体的实例而只是懂得这个窗体的句柄,例如,在不同利用过程的窗体之间发送消息就属于这种情形。这时,函数Perform()显明无法利用,取而代之的该当是函数SendMessage()和PostMessage()。
函数SendMessage()和PostMessage()的功能大约上一样,它们都能够用来向一个特定的窗口句柄发送消息。重要的差异是,函数SendMessage()直接把一个消息发送给窗口函数,等消息被处理尔后才归来;而函数PostMessage()则只是把消息发送到消息队列,然后就即刻归来。
这两个函数的原型声明离别如下:
LRESULT SendMessage(HWND hWnd,UINRT Msg,WPARAM,wParam,LPARAM,lParam);
BOOL PostMessage(HWND hWnd,UINT Msg,WPARAM,wParam,LPARAM,lParam);
能够看到,这两个函数参数同函数Perform()极其相仿,只是添置了一个hWnd参数用以表示目标窗口的句柄。
Broadcast()和BroadcastSystemMessage()
函数Broadcast()实用于所有由TWinControl类派生的对象,它能够向窗体上的所有子控件广播消息。其函数原型如下:
void __fastcall Broadcast(void *Message);
能够看到,这个函数只有一个Message参数,它指向被广播的TMessage种类的消息构造体。
函数Broadcast()只能向C++Builder利用过程中的指定窗体上的所有子控件广播消息,万一要向系统中其他利用过程可能窗体广播消息,函数Broadcast()就无能为力了。这时能够利用API函数BroadcastSystemMessage(),这个函数能够向任意的利用过程可能组件广播消息。其函数原型如下:
long BroadcastSystemMessage(
DWORD dwFlags,
LPWORD lpdwRecipients,
UINT uiMessage,
WPAREM wParam,
LPARAM lParam
);