windows消息分发机制
来源:互联网 发布:pitta mask 口罩 知乎 编辑:程序博客网 时间:2024/06/06 20:40
在TObject类中,有一个Dispatch()方法和一个DefaultHandler()方法,它们都是与消息分发机制相关的。Dispatch()负责将特定的消息分发给合适的消息处理函数。首先它会在对象本身类型的类中寻找该消息的处理函数,如果找到,则调用它;如果没有找到而该类覆盖了TObject的DefaultHandler(),则调用该类的DefaultHandler();如果两者都不存在,则继续在其基类中寻找,直至寻找到TObject这一层,而TObject已经提供了默认的DefaultHandler()方法。首先自定义一个消息结构TMyMsg,它是我们自定义的消息记录类型。对于自定义的消息类型,VCL只规定它的首4字节必须是消息编号,其后的数据类型任意。同时,VCL也提供了一个TMessage类型用于传递消息。在此程序中,不使用TMessage,而用TMyMsg代替:
- type
- TMyMsg = record
- Msg : Cardinal;
- MsgText : ShortString;
- end;
TMyMsg记录类型的第2个域我们定义为MsgText,由该域的字符串来给出对这个消息的具体描述信息。当然,这些信息都是由消息分发者给出的。然后,定义一个类,由它接受外界发送给它的消息。这个类可以说明这个演示程序的核心问题。- TMsgAccepter = class
- private
-
- procedure AcceptMsg2000(var msg : TMyMsg); message 2000;
-
- procedure AcceptMsg2002(var msg : TMyMsg); message 2002;
- public
- procedure DefaultHandler(var Message); override;
- end;
在Object Pascal中,指明类的某个方法为某一特定消息的处理函数,则在其后面添加message关键字与消息值,以此来通知编译器。正如上面类定义中的- procedure AcceptMsg2000(var msg : TMyMsg); message 2000;
指明AcceptMsg2000()方法用来处理值为2000的消息,该消息以及参数将通过msg参数传递给处理函数。TMsgAccepter类除提供了值为2000和2002的两个消息的处理函数外,还提供了一个默认的消息处理方法DefaultHandler()。该方法是在TObject中定义的虚方法,而在TMsgAccepter类中覆盖(override)了该方法,重新给出了新的实现。TMyMsg结构声明与TMsgAccepter类的声明与实现都被定义在MsgDispTest单元中。
TControl与Windows消息的封装
TObject提供了最基本的消息分发和处理的机制,而VCL真正对Windows系统消息的封装则是在TControl中完成的。TControl将消息转换成VCL的事件,以将系统消息融入VCL框架中。
窗口函数(WndProc)处理消息
窗口函数是一个回调函数,它被Windows系统所调用,其参数会被给出消息编号、消息参数等信息,以便进行处理。典型的窗口函数中会包含一个大的case分支,以处理不同的消息。现在就来看一下这个WndProc,请注意其中的注释:
- procedure TApplication.WndProc(var Message: TMessage);
- type
- TInitTestLibrary = function(Size: DWord; PAutoClassInfo: Pointer):
- Boolean; stdcall;
- var
- I: Integer;
- SaveFocus, TopWindow: HWnd;
- InitTestLibrary: TInitTestLibrary;
-
-
- procedure Default;
- begin
- with Message do
- Result := DefWindowProc(FHandle, Msg, WParam, LParam);
- end;
- procedure DrawAppIcon;
- var
- DC: HDC;
- PS: TPaintStruct;
- begin
- with Message do
- begin
- DC := BeginPaint(FHandle, PS);
- DrawIcon(DC, 0, 0, GetIconHandle);
- EndPaint(FHandle, PS);
- end;
- end;
- begin
- try
- Message.Result := 0;
- for I := 0 to FWindowHooks.Count - 1 do
- if TWindowHook(FWindowHooks[I]^)(Message) then Exit;
- CheckIniChange(Message);
- with Message do
-
- case Msg of
- WM_SYSCOMMAND:
- case WParam and $FFF0 of
- SC_MINIMIZE: Minimize;
- SC_RESTORE: Restore;
- else
- Default;
- end;
- WM_CLOSE:
- if MainForm <> nil then MainForm.Close;
- WM_PAINT:
- if IsIconic(FHandle) then DrawAppIcon else Default;
- WM_ERASEBKGND:
- begin
- Message.Msg := WM_ICONERASEBKGND;
- Default;
- end;
- WM_QUERYDRAGICON:
- Result := GetIconHandle;
- WM_SETFOCUS:
- begin
- PostMessage(FHandle, CM_ENTER, 0, 0);
- Default;
- end;
- WM_ACTIVATEAPP:
- begin
- Default;
- FActive := TWMActivateApp(Message).Active;
- if TWMActivateApp(Message).Active then
- begin
- RestoreTopMosts;
- PostMessage(FHandle, CM_ACTIVATE, 0, 0)
- end
- else
- begin
- NormalizeTopMosts;
- PostMessage(FHandle, CM_DEACTIVATE, 0, 0);
- end;
- end;
- WM_ENABLE:
- if TWMEnable(Message).Enabled then
- begin
- RestoreTopMosts;
- if FWindowList <> nil then
- begin
- EnableTaskWindows(FWindowList);
- FWindowList := nil;
- end;
- Default;
- end else
- begin
- Default;
- if FWindowList = nil then
- FWindowList := DisableTaskWindows(Handle);
- NormalizeAllTopMosts;
- end;
- WM_CTLCOLORMSGBOX..WM_CTLCOLORSTATIC:
- Result := SendMessage(LParam, CN_BASE + Msg, WParam, LParam);
- WM_ENDSESSION:
- if TWMEndSession(Message).EndSession then FTerminate := True;
- WM_COPYDATA:
- if (PCopyDataStruct(Message.lParam)^.dwData = DWORD($DE534454))
- and (FAllowTesting) then
- if FTestLib = 0 then
- begin
- FTestLib := SafeLoadLibrary('vcltest3.dll');
- if FTestLib <> 0 then
- begin
- Result := 0;
- @InitTestLibrary := GetProcAddress(
- FTestLib,
- 'RegisterAutomation'
- );
- if @InitTestLibrary <> nil then
- InitTestLibrary(
- PCopyDataStruct(Message.lParam)^.cbData,
- PCopyDataStruct(Message.lParam)^.lpData
- );
- end
- else
- begin
- Result := GetLastError;
- FTestLib := 0;
- end;
- end
- else
- Result := 0;
- CM_ACTIONEXECUTE, CM_ACTIONUPDATE:
- Message.Result := Ord(DispatchAction(
- Message.Msg,
- TBasicAction(Message.LParam))
- );
- CM_APPKEYDOWN:
- if IsShortCut(TWMKey(Message)) then Result := 1;
- CM_APPSYSCOMMAND:
- if MainForm <> nil then
- with MainForm do
- if (Handle <> 0) and IsWindowEnabled(Handle) and
- IsWindowVisible(Handle) then
- begin
- FocusMessages := False;
- SaveFocus := GetFocus;
- Windows.SetFocus(Handle);
- Perform(WM_SYSCOMMAND, WParam, LParam);
- Windows.SetFocus(SaveFocus);
- FocusMessages := True;
- Result := 1;
- end;
- CM_ACTIVATE:
- if Assigned(FOnActivate) then FOnActivate(Self);
- CM_DEACTIVATE:
- if Assigned(FOnDeactivate) then FOnDeactivate(Self);
- CM_ENTER:
- if not IsIconic(FHandle) and (GetFocus = FHandle) then
- begin
- TopWindow := FindTopMostWindow(0);
- if TopWindow <> 0 then Windows.SetFocus(TopWindow);
- end;
- WM_HELP,
- CM_INVOKEHELP: InvokeHelp(WParam, LParam);
- CM_WINDOWHOOK:
- if wParam = 0 then
- HookMainWindow(TWindowHook(Pointer(LParam)^)) else
- UnhookMainWindow(TWindowHook(Pointer(LParam)^));
- CM_DIALOGHANDLE:
- if wParam = 1 then
- Result := FDialogHandle
- else
- FDialogHandle := lParam;
- WM_SETTINGCHANGE:
- begin
- Mouse.SettingChanged(wParam);
- SettingChange(TWMSettingChange(Message));
- Default;
- end;
- WM_FONTCHANGE:
- begin
- Screen.ResetFonts;
- Default;
- end;
- WM_NULL:
- CheckSynchronize;
- else
- Default;
- end;
- except
- HandleException(Self);
- end;
- end;
整个WndProc()方法,基本上只包含了一个庞大的case分支,其中给出了每个消息的处理代码,“WM_”打头的为Windows定义的窗口消息,“CM_”打头的为VCL库自定义的消息。需要注意的是,这里给出WndProc是属于TApplication的,也就是那个0×0大小的Application窗口的窗口函数,而每个Form另外都有自己的窗口函数。至此,读者应该清楚了VCL框架是如何封装Windows程序框架的了。知道VCL为我们做了什么,它想要提供给我们的是怎样的一个世界,这对于我们更好地融入VCL是大有好处的。这比从RAD角度看待VCL,有了更深一层的理解。好了,关于VCL和消息的话题到此为止。 0 0