深入VCL领略BCB的动静机制3
副标题#e#
要领3 来自TApplication的要领
不消我多空话,各人都知道TApplication在BCB中的重要性。在BCB的辅佐中指出:TApplication、TScreen和TForm组成了所有BCB气势气魄的Win32 GUI措施的脊梁,他们节制着您措施的行为。TApplication类提供的属性和要领封装了尺度Windows措施的行为。TApplication表示了在Windows操纵系统中建设、运行、支持和销毁应用措施的根基道理。因此,TApplication大大简化了开拓者和Windows情况之间的接口。这正是BCB的RAD特性。
TApplication封装的尺度Windows行为大抵包罗如下几部门:
1> Windows 动静处理惩罚
2> 上下文关联的在线辅佐
3> 菜单的快捷键和键盘事件处理惩罚
4> 异常处理惩罚
5> 打点由操纵系统界说的措施基本部门,如:MainWindow 主窗口、WindowClass 窗口类等。
一般环境下,BCB会为每个措施自动生成一个TApplication类的实例。这部门源码可以在yourproject.cpp文件中见到(这里假定您的工程名称就叫yourproject.bpr)。
虽然TApplication是不行见的,他老是在您的Form背后冷静的节制着您的措施的行为。但也不是找不到蛛丝马迹。假如您新建一个措施(New Application),然后不作任何窜改,编译运行的话,你会发明措施窗体的Caption是Form1,但在Windows的状态条上的Caption确写着project1的字样。这就是TApplication存在的证据。虽然,这只是一种忖测,实战的要领应该打开BCB附带的WinSight来查察系统的历程。您可以清楚的看到TApplication类的存在,他的巨细是0(埋没的嘛),然后才是TForm1类。
好了,既然TApplication封装了动静处理惩罚的内容。我们就研究一下TApplication的实际行动吧。实际上动静达到BCB措施时,最先获得它们的就是TApplication工具。经过TApplication之后,才通报给Form的。以前的要领都是重载TForm的要领,显然要比本文所提到的要领要晚一些收到动静。对您来说,是不是但愿在第一时间收到动静并处理惩罚它们呢?
#p#副标题#e#
要清楚的知道TApplication的处理惩罚机制照旧深入VCL源码。首先看一看最最普通的一段代码吧。
#include <vcl.h> #pragma hdrstop USERES("Project1.res"); USEFORM("Unit1.cpp", Form1);
//--------------------------------------------------------------
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{ try { // 初始化Application Application->Initialize(); // 建设主窗口,并显示
Application->CreateForm(__classid(TForm1), &Form1); // 进入动静轮回,直到措施退出Application->Run();
}
catch (Exception &exception)
{ Application->ShowException(&exception);
}
return 0;
}
短短的几行代码就可以让您的BCB措施自如运行。因为一切都已经被VCL在靠山封装好了。Application->Run()要领进入措施的动静轮回,直到措施退出。一起跟进VCL源码看个毕竟吧。TApplication的界说在forms.pas中。procedure TApplication.Run;
begin FRunning := True;
try AddExitProc(DoneApplication);
if FMainForm <> nil then begin // 配置主窗口的显示属性
case CmdShow of
SW_SHOWMINNOACTIVE: FMainForm.FWindowState := wsMinimized; SW_SHOWMAXIMIZED: MainForm.WindowState := wsMaximized;
end;
if FShowMainForm then
if FMainForm.FWindowState = wsMinimized then
Minimize else
FMainForm.Visible := True;
// 瞥见了吧,这里有个轮回,直到Terminated属性为真退出。Terminated什么意思,就是打消,竣事
repeat
HandleMessage
until Terminated;
end;
finally
FRunning := False;
end;
end;
动静处理惩罚的详细实现不在Run要领中,很显然要害在HandleMessage要领,看看这函数名字-动静处理惩罚。只有跟进HandleMessage瞧瞧喽。
procedure TApplication.HandleMessage;
var
Msg: TMsg;
begin
if not ProcessMessage(Msg) then Idle(Msg);
end;
咳,这里也不是案发明场。措施先将动静交给ProcessMessage要领处理惩罚。假如没什么要处理惩罚的,就转入Application.Idle要领“措施在空闲时挪用的要领”。
呼呼,再跟进ProcessMessage要领吧。
function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
var
Handled: Boolean;
begin
Result := False;
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
begin
Result := True;
if Msg.Message <> WM_QUIT then
begin
Handled := False;
if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
if not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) and
not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end
else
FTerminate := True;
end;
end;
哎呀呀,终于有端倪了。ProcessMessage回收了一套尺度的Windows API 函数PeekMessage …. TranslateMessage;DispatchMessage。
有人说:Application->OnMessage = MyOnMessage; //不能响应SendMessage的动静,可是可以响应PostMessage发送的动静,也就是动静行列里的动静
SendMessage和PostMessage最主要的区别在于发送的动静有没有通过动静行列。
#p#分页标题#e#
原因就在这里。ProcessMessage利用了PeekMessage(Msg, 0, 0, 0, PM_REMOVE) 从动静行列中提打动静。然后先查抄是不是退出动静。不是的话,查抄是否存在OnMessage要领。假如存在就转入OnMessage处理惩罚动静。最后才将动静分发出去。
这样重载Application的OnMessage要领要比前两种要领更早获得动静,可以说是最快速的要领了吧。举个例子:
void __fastcall TForm1::MyOnMessage(tagMSG &Msg, bool &Handled)
{
TMessage Message;
switch (Msg.message)
{
case WM_KEYDOWN:
Message.Msg = Msg.message;
Message.WParam = Msg.wParam;
Message.LParam = Msg.lParam;
MessageDlg("You Pressed Key!", mtWarning, TMsgDlgButtons() << mbOK, 0);
Handled = true;
break;
}
}
void __fastcall TForm1::FormCreate(TObject *Sender)
Application->OnMessage = MyOnMessage;
}
此刻可以简短的总结一下VCL的动静机制了。
尺度的BCB措施利用Application->Run()进入动静轮回,在Application的ProcessMessage要领中,利用PeekMessage要领从动静行列中提打动静,并将此动静从动静行列中移除。然后ProcessMessage 要领查抄是否存在Application->OnMessage要领。存在则转入此要领处理惩罚动静。之后再将处理惩罚过的动静分发给措施中的各个工具。至此,WndProc要领收到动静,并举办处理惩罚。假如有无法处理惩罚的交给重载的Dispatch要领来处理惩罚。要是还不能处理惩罚的话,再交给父类的Dispatch要领处理惩罚。最后Dispatch要领实际大将动静转入DefaultHandler要领来处理惩罚。
“嘿嘿,实际上,你一样可以重载DefaultHandler要领来处理惩罚动静。可是太晚了一点。我想没有人愿意最后一个处理惩罚动静吧…:-)”
写到这里好像可以竣事了。但假如您看过上一篇的话,必然会留意到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("Really Close??", mtWarning, TMsgDlgButtons() << mbYes <<mbNo, 0)? 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);
}
这样,将本文中的两种要领相团结,您就可以自如的处理惩罚达到您的应用措施的各类动静了。(全文完)