娃娃鸭深入核心VCL架构剖析(李维)笔记

 

44TForm

TControl = class(TComponent)

 private

procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;

    procedure WMRButtonDblClk(var Message: TWMRButtonDblClk); message WM_RBUTTONDBLCLK;

procedure WMMButtonDblClk(var Message: TWMMButtonDblClk); message WM_MBUTTONDBLCLK;

...

 

end;

 

procedure TControl.WMLButtonDown(var Message: TWMLButtonDown);

begin

  SendCancelMode(Self);

  inherited;

  if csCaptureMouse in ControlStyle then MouseCapture := True;

  if csClickEvents in ControlStyle then Include(FControlState, csClicked);

  DoMouseDown(Message, mbLeft, []);

end;

procedure TControl.DoMouseDown(var Message: TWMMouse; Button: TMouseButton;

  Shift: TShiftState);

begin

  if not (csNoStdEvents in ControlStyle) then

    with Message do

      if (Width > 32768) or (Height > 32768) then

        with CalcCursorPos do

          MouseDown(Button, KeysToShiftState(Keys) + Shift, X, Y)

      else

        MouseDown(Button, KeysToShiftState(Keys) + Shift, Message.XPos, Message.YPos);

end;

声明:

   procedure MouseDown(Button: TMouseButton; Shift: TShiftState;

      X, Y: Integer); dynamic;

procedure TControl.MouseDown(Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

begin

  if Assigned(FOnMouseDown) then FOnMouseDown(Self, Button, Shift, X, Y);

end;

 

45DefaultHandler函数

VCL Framework中最后对于未处理的窗口消息的处理函数是DefaultHandler。在传统的Windows程序设计中DefWindowProc这个Windows API是窗口回调函数调用来处理窗口应用程序不处理的窗口消息的处理函数。在VCL Framework中使用VCL组件处理其它VCL组件不处理的窗口消息,以提供VCL Framework一些基础的功能。对于DefaultHandler最后仍然不处理的窗口消息,DefaultHandler最后也是调用Windows ApiDefWindowProc来处理。

TObject = class

procedure DefaultHandler(var Message); virtual;

end;

TObject以虚拟方法定义DefaultHandler就是为了让VCL Framework中的派生类能够重载DefaultHandler以便让VCL组件能够在把未处理的窗口消息转回给DefWindowProc之前有机会进行处理,以便让特定VCL组件能够提供基础服务,因此在TObject中的虚拟方法DefaultHandler是一个空白的方法:

procedure TObject.DefaultHandler(var Message);

begin

end;

其目的在于提供一个Placeholder,让派生类重载使用。而在TWinControl类中提供了最重要的DefaultHandler重载程序代码。TWinControl.DefaultHandler为所有从TWinControl类继承的VCL组件提供了通用的实现程序代码,以提供基础的服务并且调用DefWindowProc处理最后VCL Framework不处理的窗口消息:

procedure TWinControl.DefaultHandler(var Message);

begin

  if FHandle <> 0 then

  begin

    with TMessage(Message) do

    begin

      if (Msg = WM_CONTEXTMENU) and (Parent <> nil) then

      begin

        Result := Parent.Perform(Msg, WParam, LParam);

        if Result <> 0 then Exit;

      end;

      case Msg of

        WM_CTLCOLORMSGBOX..WM_CTLCOLORSTATIC:

          Result := SendMessage(LParam, CN_BASE + Msg, WParam, LParam);

        CN_CTLCOLORMSGBOX..CN_CTLCOLORSTATIC:

          begin

            SetTextColor(WParam, ColorToRGB(FFont.Color));

            SetBkColor(WParam, ColorToRGB(FBrush.Color));

            Result := FBrush.Handle;

          end;

      else

        if Msg = RM_GetObjectInstance then

          Result := Integer(Self)

        else

          Result := CallWindowProc(FDefWndProc, FHandle, Msg, WParam, LParam);//调用DefWindowProc

      end;

      if Msg = WM_SETTEXT then

        SendDockNotification(Msg, WParam, LParam);

    end;

  end

  else

    inherited DefaultHandler(Message);

end;

TWinControl只处理VCL自定义的消息,TCustomForm也重载了TWinControlDefaultHandler,以便为TForm提供额外需要的功能。

TCustomForm = class(TScrollingWinControl)

  ...

 public

procedure DefaultHandler(var Message); override;

end;

procedure TCustomForm.DefaultHandler(var Message);

begin

  if ClientHandle <> 0 then

    with TMessage(Message) do

      if Msg = WM_SIZE then

        Result := DefWindowProc(Handle, Msg, wParam, lParam) else

        Result := DefFrameProc(Handle, ClientHandle, Msg, wParam, lParam)

  else

    inherited DefaultHandler(Message)

end;

 

46VCL消息处理设计模式(Design Pattern

Dispatcher设计模式

Dispatcher设计模式是TObject使用的分派消息设计模式,其主要的功能是提供自动消息分派并且把应用程序的执行权自动转移到相对的消息处理函数。

P257再看看回头

P280

47、接口

type

  IInterface = interface

    ['{00000000-0000-0000-C000-000000000046}']

    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;

    function _AddRef: Integer; stdcall;

    function _Release: Integer; stdcall;

  end;

Object Pascal中接口服务是由类来实现的。由于在接口的程序区块中只有服务的声明而没有任何存取限制符(Accessor)的声明,即privateprotect或是public,因此当类实现接口时可以结合类的存取限制符来限制接口之中的服务。

类名=class(父类,实现接口1,实现接口2...)

接口只是服务的声明,而真正的服务是由对象提供,因此一般来说接口不应该控制对象的生命周期,也不应该会造成内存泄漏,因为接口说穿了只是一个指针。接口也不应该会造成存取违规错误,因为只要实现接口的对象存在就可以正确地使用接口。

在日常的开发应用中,对象的生命周期可分为两种类型:

·对象生命周期掌握在他人手中

·对象生命周期掌握在程序员手中

 

·对象生命周期掌握在他人手中

COM中当程序员使用COM API或是Delphi提供的CreateComObject/CreateOleObject/CreateRemoteComObject等方法创建了COM对象之后,创建的COM对象的生命周期是控制在COM的执行环境手中的。程序员只能遵照COM的规范来使用COM对象,最后释放COM对象的接口来建议COM的执行环境释放COM对象。不过当程序员建议COM的执行环境释放COM对象时,COM的执行环境并不一定会马上释放,而会在考虑许多的情形后才决定是否释放COM对象,其中最重要的条件是COM接口的引用计数值是否为0。

在这种对象生命周期由他人控制的应用中,必须有一种方式让客户端来帮助执行环境一起正确地管理对象生命周期,通常使用的机制就是接口和Proxy/StubCOM使用了接口而EJB则使用Stub

由于在这种对象生命周期由他人控制的应用中对象可能会因为客户端没有正确地遵照使用规范因此造成执行环境不能释放对象而形成内存泄漏,因此读者只要记住在这种应用中切实遵照使用规范就不会造成内存泄漏错误。

不过在这种情形中很可能发生的错误就是存取违规错误,这个意思是指由于客户端以接口/Proxy来建议对象释放的,因此如果客户端不小心额外释放了接口/Proxy,就会造成执行环境过早释放对象而造成其他客户端存取违规错误。

·对象生命周期掌握在程序员手中

另一种情形是实现接口的对象完全掌握在程序的代码中,那么我们可以更简化使用接口可能产生的问题,那就是程序员只要思考对象本身的生命周期即可,完全不须多虑接口的影响,因为在这种应用中本来就不应该让接口来干扰对象的生命周期。这种应用应该注重的是接口的服务而不是接口的引用计数机制,而且由于对象是掌握在程序员手中,因此最后只要释放对象即可,接口只是指针,不会造成内存泄漏。如果程序又把接口的引用计数值引入这种应用中,那么反而会造成存取违规错误。

 

48、声明继承和实现继承

所谓使用接口委托是指声明实现接口的类在接受客户端调用时,是使用内部的一个其他接口变量来调用真正提供服务的接口。而使用类对象委托则是调用内部的一个其他对象变量来调用真正提供服务的接口。

 

49TInterfacedObject

TInterfacedObject = class(TObject, IInterface)

  protected

    FRefCount: Integer;

    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;

    function _AddRef: Integer; stdcall;

    function _Release: Integer; stdcall;

  public

    procedure AfterConstruction; override;

    procedure BeforeDestruction; override;

    class function NewInstance: TObject; override;

    property RefCount: Integer read FRefCount;

  end;

function TInterfacedObject.QueryInterface(const IID: TGUID; out Obj): HResult;

begin

  if GetInterface(IID, Obj) then

    Result := 0

  else

    Result := E_NOINTERFACE;

end;

 

function TInterfacedObject._AddRef: Integer;

begin

  Result := InterlockedIncrement(FRefCount);

end;

 

function TInterfacedObject._Release: Integer;

begin

  Result := InterlockedDecrement(FRefCount);

  if Result = 0 then

    Destroy;

end;

回传对象本身

IInterfaceComponentReference = interface

    ['{E28B1858-EC86-4559-8FCD-6B4F824151ED}']

    function GetComponent: TComponent;

  end;

TComponent = class(TPersistent, IInterface, IInterfaceComponentReference)

  private

...

function IInterfaceComponentReference.GetComponent = IntfGetComponent;

function IntfGetComponent: TComponent;

end;

function TComponent.IntfGetComponent: TComponent;

begin

  Result := Self;

end;

P360

P447

50Delphi的持久化机制

TPersistent = class(TObject)

  private

    procedure AssignError(Source: TPersistent);

  protected

    procedure AssignTo(Dest: TPersistent); virtual;

    procedure DefineProperties(Filer: TFiler); virtual;

    function  GetOwner: TPersistent; dynamic;

  public

    destructor Destroy; override;

    procedure Assign(Source: TPersistent); virtual;

    function  GetNamePath: string; dynamic;

  end;

 

TFiler = class(TObject)

  private

    FStream: TStream;

    FBuffer: Pointer;

    FBufSize: Integer;

    FBufPos: Integer;

    FBufEnd: Integer;

    FRoot: TComponent;

    FLookupRoot: TComponent;

    FAncestor: TPersistent;

    FIgnoreChildren: Boolean;

  protected

    procedure SetRoot(Value: TComponent); virtual;

  public

    constructor Create(Stream: TStream; BufSize: Integer);

    destructor Destroy; override;

    procedure DefineProperty(const Name: string;

      ReadData: TReaderProc; WriteData: TWriterProc;

      HasData: Boolean); virtual; abstract;

    procedure DefineBinaryProperty(const Name: string;

      ReadData, WriteData: TStreamProc;

      HasData: Boolean); virtual; abstract;

    procedure FlushBuffer; virtual; abstract;

    property Root: TComponent read FRoot write SetRoot;

    property LookupRoot: TComponent read FLookupRoot;

    property Ancestor: TPersistent read FAncestor write FAncestor;

    property IgnoreChildren: Boolean read FIgnoreChildren write FIgnoreChildren;

  end;

 

51Framework设计模式

.Notify设计模式

我们经常会希望当特定事件发生时能够通知特定的类对象或是系统。由于发生的事件可能会有许多种,因此我们可以预先定义事件动作代码,当事件发生时就使用特定的事件动作代码来通知须要知道这个事件的类对象。这种应用就是Notify设计模式被发展出来的动力。

好处:

客户端程序代码可以使用一致的语法来通知Notify类执行工作,使用枚举值的方式也允许类可以不断地加入更多的枚举值来增加更多的功能而不需要大量改写实现程序代码。

场景:

·程序员开发了特定的系统,但是想让后来开发的应用程序或是组件能够与原先开发的系统进行交互

·在设计Container类时程序员想定义和外界一致的交互接口

·程序员想对一群组件执行预告定义好的特定工作

 

Facade设计模式

场景:

·提供一个一致的接口以便调用一群属于finer-Grained组件提供的服务

·客户端和服务器端许多的对象都有相互依存的关系时,如果我们想降低这些依存关系,那么可以引入Facade设计模式来切割客户端和服务器端。

·许多的组件或是子系统想分门别类整理时可以引入Facade设计模式为每一个子系统定义一个入口的Facade接口

·在分布式计算环境中为了减少客户端进行的远程调用次数,可以适时地使用Facade设计模式来有效减少远程调用

·为了隐藏后端复杂的对象或是系统,可以使用Facade设计模式重新定义只对客户端开放的服务

 

Command设计模式/Action设计模式

Command设计模式使用的目的在于使用对象来封装客户端的请求或是命令,由于使用了对象封装请求,因此可以达到下面的效果:

·请求对象可结合多态以及虚拟讲方法来提供更大的弹性

·负责执行请求的目的对象可以和客户端分离,这也表示许多的客户端都可以发出相同的请求,例如菜单或是工具栏中的按钮都可以发出开启文件的请求,如此一来菜单和工具栏按钮便可以使用相同的请求对象,而负责执行开启文件的程序代码并不会绑定到单一的菜单或是工具栏按钮

·由于使用了请求对象,因此不单是图形用户界面的控件可以触发请求,一般的程序代码也可以通过请求对象来要求执行特定的工作

·由于请求对象可以使用一个完整的类架构来实现,因此可以让客户端使用一致的程序代码格式来触发各种不同的请求

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
深入核心——VCL架构剖析》光盘说明-、光盘用途 本光盘为《深入核心——VCL架构剖析》一书的配套光盘,供读者阅读图书时参考和学习。二、光盘内容 光盘“源代码”目录中包含了书中所有源代码,文件目录和图书的目录相对应。如“Chap01”表示书中第1章的范例源代码。 光盘包含了全部的pas、dfm和dpr文件。 我们已经对所有文件进行了简体化工作。如果您在使用中发现有界面乱码问题,请将窗体Font改为“宋体”,Charset改为gb2312即可,并请即时告知我们,让更多读者受益。三、运行环境 多数代码可以直接在Delphi6和Delphi7环境下运行。部分涉及.NET技术内容的代码,需要在Delphi 7上安装Borland .NET Complier for Delphi编译器方可编译执行。Borland已经正式推出Delphi 8 for .NET,所以本书范例中部分内容可能与D8最终版本不符。四、使用方法 直接将范例文件拷贝至硬盘适当目录即可。 多数完整应用程序代码已经编译为.exe可执行文件,读者可直接运行之。五、防病毒 本光盘所有文件都已经过Norton Antivirus扫描,未发现有任何已知病毒。六、风险 读者须对使用光盘所附代码、文件所造成的一切后果负责。 七、如果对代码有任何疑问、建议或者发现有遗漏、错误之处请与 liwei@csdn.net联系。六、所有源代码可以在学习和工作中直接使用,但请不要用于商业目的。
VCL(Visual Component Library)是Delphi编程语言中的一个重要特性,它是一个层次化的组件库,用于构建用户界面和应用程序的交互逻辑。VCL架构核心是一系列基础组件,这些组件包括窗体、按钮、标签等常用控件,以及数据访问、图形绘制等功能模块。 VCL架构设计十分灵活,能够满足各种应用程序开发的需求。其核心思想是面向对象编程(OOP),通过组合和继承来构建复杂的控件和功能模块。 VCL框架可以分为两个主要层次:可视化层和非可视化层。可视化层负责处理用户界面的显示和交互,而非可视化层则处理底层的逻辑和数据操作。 在可视化层中,VCL使用窗体作为最基础的容器,并提供了一系列的控件用于用户界面的构建和展示。这些控件通过事件机制实现与用户的交互,并通过属性设置来调整外观和行为。 在非可视化层中,VCL提供了许多功能模块,例如数据访问模块(如数据库连接和查询),图形绘制模块(如图表和图像处理),以及网络通信模块等。这些模块通过封装底层API和算法,为开发者提供了便利的功能接口。 在VCL架构中,还有一个重要的特性是可视化设计器(IDE),它提供了开发者友好的图形界面,用于快速创建和编辑界面和逻辑。开发者可以通过拖放和属性设置等方式来构建复杂的界面和控制流程,从而快速实现应用程序的开发和调试。 总的来说,VCL架构是一种灵活而强大的开发框架,能够帮助开发者快速构建高效、易用的应用程序。通过深入核心vcl架构剖析,开发者可以更好地理解和应用VCL框架,提高开发效率和程序质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值