• 注册
  • 关于作者
    企业认证:趣记站长
    关注 6 粉丝 4 喜欢 9 内容 992
    江西省·赣州市
    聊天 送礼
    • 查看作者
    • 基于Delphi的接口编程入门

        为什么使用接口?

        举个例子好了:有这样一个卖票服务,电影院可以卖票,歌剧院可以卖票,客运站也可以卖票,那么我们是否需要把电影院、、歌剧院和客运站都设计成一个类架构以提供卖票服务?要知道,连经理人都可以卖票,很显然不适合把经理人也包括到卖票服务的继承架构中,我们需要的只是一个共通的卖票服务。于是,卖票的服务是个接口,电影院、歌剧院什么的只要都遵循这样一个服务定义就能很好地相互交互和沟通(如果须要的话)。

        如何在Delphi中使用接口

        1、声明接口

      IMyInterface = interface(IInterface) //说明(1)

      ['{63E072DF-B81E-4734-B3CB-3C23C7FDA8EA}'] //说明(2)

      function GetName(const str: String): String; stdcall;

      function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; //说明(3)

      function _AddRef: Integer; stdcall; //使接口引用数加1。

      function _Release: Integer; stdcall;//使接口引用数减1,当小于等于0时作释放动作。

      end;

        说明(1):如果有继续关系则在括号里填父接口,否则省却,如:IMyInterface = interface这样就行。

        说明(2):此GUID可选,如果要实现具有COM特性的接口的话则需要加上,

      用Delphi实现文件下载的几种方法
      基于Delphi的屏幕抓图技术的实现,Delphi中数据的自动录入,Delphi编程:用流来读取TXT文件中的数据,使用Delphi创建IIS虚拟目录的方法,Delphi初学者应小心的六大陷阱,Delphi“判断服务器路径”与“清空日志文件”,利用VFW在Delphi中开发视频捕获程序,Delphi实现网页表单数据的自动提交,Delphi编程使用HOOK监视Windows,Delphi编程禁止用户关闭操作系统,Delphi中利用钩子实现QQ聊天窗口的修改,一个判断定文件是否为文本文件的函数,把Flash文件转换为Exe文件,用Delphi实现文件下载的几种方法,Delphi中保存图像列表,基于Delphi的接口编程入门,在Delphi中使用CreateOleObject方法对WORD文件进行操作,利用Delphi开发旅游景点微机售票系统,Delphi中用ICMP探测远程主机状态,Delphi7对XML的支持分析
      Delphi

      Delphi中对于有GUID的接口在运行时在VMT表的预定位置生成接口的信息,如接口方法的定义、方法参数定义能详细信息。

        说明(3):接口必须实现这三个函数。

        2、接口的实现

        接口服务是由类来实现的。

      TIntfClass = class(TObject, IMyInterface)

      private

       FCounter: Integer;

       FRefCount: Integer;

      public

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

       ...

      end;

        3、获取接口

        a. 使用类型转换。 如:

      var aIntf: IMyInterface;

      begin

       aObj := TIntfClass.Create;

      try

       aIntf := (IMyInterface(aObj);

       ...

        b. 利用Delphi编译器内建机制。 如:aIntf := aObj。

        c. 利用对象的QueryInterface方法。如OleCheck(aObj.QueryInterface(IID, aIntf)); 只能存取有GUID的COM接口。

        d. 利用as操作符。

        使用as操作符必须符合下面条件:

        1.接口必须明确地指定是从IInterface接口继承下来。

        2.必须拥有GUID值

        在Delphi7中接口的实现类还必须是从TInterfacedObject继承下来才行,如:

      TIntfClass = class(TInterfacedObject, IMyInterface)

        4、接口和对象生命期

        因为Delphi会自行检查接口如果在使用后没有释放而在生成的程序里加上释放代码,但也因这样带来了问题,如下面代码:

      var

       i: Integer;

       aObj: TIntfClass;

       aIntf: IMyInterface;

      begin

       aObj := TIntfclass.Create;

       try

        aIntf := aObj;

        aIntf.GetName...

       finally

        aIntf := nil;

        FreeAndNil(aObj);

      end;

        上面的代码执行的话会产生存取违规错误,是因为对接口置nil时已释放接口,而FreeAndNil(aObj)会再释放aIntf一次,而在对aIntf置

      nil时已释放了该对象。解决这个问题只要不让接口干扰对象的生命期就可以了,在Release中只需减引用计数而不做释放的动作。

      function TIntfClass._Release: Integer;

      begin

      Result := InterlockedDecrement(FRefCount);

      end;

        5、接口的委托(Interface Delegation)

        分为两种:

        1. 对象接口委托

        2. 类对象委托。

        . 对象接口委托,假如已有下面接口定义:

      IImplInterface = interface(IInterface)

      function ConvertToUSD(const iNTD: Integer): Double;

      function ConvertToRMB(const iNTD: Integer): Double;

      end;

        接着有一个类实现了该接口:

      TImplClass = class(TObject, IImplInterface)

      private

       FRefCount: Integer;

      public

       function ConvertToUSD(const iNTD: Integer): Double;

       ...

      end;

      implementation

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

      begin

      if GetInterface(IID, Obj) then

       Result := 0

      else

       Result := E_NOINTERFACE;

      end;

      function TImplClass._Release: Integer;

      begin

       Result := InterlockedDecrement(FRefCount);

      if Result = 0 then

       Destroy;

      end;

      ... ...

        现在有另外一个类TIntfServiceClass要实现IImplInterface接口,不用重新定义,只须使用上面的TImplClass就可以:

      TIntfServiceClass = class(TObject, IImplInterface)

      private

       FImplService: IImplInterface;

       //FSrvObj: TImplClass; //如果是用类对象委托的话

      public

       Constructor Create; overload;

       Destructor Destroy; override;

       Constructor Create(aClass: TClass); overload;

       property MyService: IImplInterface read FImplService implements IImplInterface;

       // property MyService: TImplClass read FSrvObj implements IImplInterface; //如果是用对象委托的话。

      end;

        实现如下:

      constructor TIntfServiceClass.Create;

      begin

       FImplService := TImplClass.Create;

      end;

      constructor TIntfServiceclass.Create(aClass: TClass);

      var

       instance: TImplClass;

      begin

       instance := TImplClass(aClass.NewInstance);

       FImplService := instance.Create;

      end;

      destructor TIntfServiceClass.Destroy;

      begin

       FImplService := nil; //遵照TImplClass使用引用计数来控制对象生命周期,看TImplClass的Destroy实现。

       inherited;

      end;

        6、接口和RTTI

        Delphi中在VMT-72位移处定义了接口哥格指针:vmtIntfTable = -72。

        相关函数:

      GetInterfaceCount; //获取接口数量。

      GetInterfaceTable; //获取接口表格。

        相关结构:

      TInterfaceEntry = packed record

      IID: TGUID;

      VTable: Pointer;

      IOffset: Integer;

      ImplGetter: Integer;

      end;

      PInterfaceTable = ^TInterfaceTable;

      TInterfaceTable = packed record

      EntryCount: Integer;

      Entries: array[0..9999] of TInterfaceEntry;

      end;

        Self是指向VMT指针的指针,所以:Self.GetInterfaceTable.EntryCount等价于:

      aPtr := PPointer(Integeer((Pointer(Self))^) + vmtIntfTable)^;

        只要在声明中使用M+/M-指令就能在Delphi中编译出的程序里添加RTTI信息,如:

      {$M+}

      iInvokable = interface(IInterface)

      {$M-}

        接口的RTTI信息由TIntfMetaData记录结构定义:

      TIntfMetaData = record

      name: String; //接口名称

      UnitName: String; //接口声明的程序单元名称

      MDA: TIntfMethEntryArray; //储存接口中方法信息的动态数组

      IID: TGUID; //接口的GUID值

      Info: PTypeInfo; //描述接口信息的指针

      AncInfo: PTypeInfo; //描述父代信息的指针

      NumAnc: Integer; //此接口继承自父代接口的方法数目

      end;

        TIntfMethEntryArray的定义如下:

      type

       TCallConv = (ccReg, ccCdecl, ccPascal, ccStdCall, ccSafeCall);

       TIntfMethEntry = record

       Name: String; //方法名称

       CC: TCallConv; //调用惯例

       Pos: Integer; //方法在接口中的位置

       ParamCount: Integer; //方法的参数数目

       ResultInfo: PTypeInfo; //描述方法回传类型的信息指针

       SelfInfo: PTypeInfo; //描述方法本身的信息指针

       Params: TIntfParamEntryArray; //描述参数信息的动态数组

       HasRTTI: Boolean; //这个方法是否拥有RTTI信息的布尔值

      end;

      TIntfMethEntryArray = array of TIntfMethEntry;

        参数信息TIntfParamEntry定义:

      TIntfParamEntry = record

      Flags: TParamFlags;

      Name: String;

      Info: PTypeInfo;

      end;

      TTypeInfo = record

      Kind: TTypeKind; //数据类型

      Name: ShortString; //类型信息的字符串格式

      end;

      用Delphi实现文件下载的几种方法
      基于Delphi的屏幕抓图技术的实现,Delphi中数据的自动录入,Delphi编程:用流来读取TXT文件中的数据,使用Delphi创建IIS虚拟目录的方法,Delphi初学者应小心的六大陷阱,Delphi“判断服务器路径”与“清空日志文件”,利用VFW在Delphi中开发视频捕获程序,Delphi实现网页表单数据的自动提交,Delphi编程使用HOOK监视Windows,Delphi编程禁止用户关闭操作系统,Delphi中利用钩子实现QQ聊天窗口的修改,一个判断定文件是否为文本文件的函数,把Flash文件转换为Exe文件,用Delphi实现文件下载的几种方法,Delphi中保存图像列表,基于Delphi的接口编程入门,在Delphi中使用CreateOleObject方法对WORD文件进行操作,利用Delphi开发旅游景点微机售票系统,Delphi中用ICMP探测远程主机状态,Delphi7对XML的支持分析
      Delphi

    • 0
    • 0
    • 0
    • 65
    • 单栏布局 侧栏位置: