Delphi下的接口编程

转载 2012年03月31日 10:29:30

基于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这样就行。
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;


 说明(2):此GUID(全球唯一标识符)可选,如果要实现具有COM特性的接口的话则需要加上,Delphi中对于有GUID的接口在运行时在VMT表的预定位置生成接口的信息,如接口方法的定义、方法参数定义能详细信息。

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

2、接口的实现

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

TIntfClass = class(TObject,      //继承唯一的一个T打头的父类

IMyInterface)  //可继承实现多个I打头的接口
private
 FCounter: Integer;
 FRefCount: Integer;
public

  // IMyInterface接口方法的实现
 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下的接口编程学习笔记   1.1  ...
  • rocklee
  • rocklee
  • 2015年09月22日 21:12
  • 1369

Delphi环境下使用定制接口开发OPC数据访问客户程序

http://www.kongzhi.net/cases/caseview.php?id=1112 Delphi环境下使用定制接口开发OPC数据访问客户程序 OPC(用于过程控制的OLE)...
  • gencheng
  • gencheng
  • 2013年07月19日 09:10
  • 2955

基于Delphi的接口编程入门

为什么使用接口?   举个例子好了:有这样一个卖票服务,电影院可以卖票,歌剧院可以卖票,客运站也可以卖票,那么我们是否需要把电影院、、歌剧院和客运站都设计成一个类架构以提供卖票服务?要知道,连经理人...
  • SnowDayX
  • SnowDayX
  • 2013年02月21日 19:21
  • 91

基于Delphi的接口编程入门

为什么使用接口? 举个例子好了:有这样一个卖票服务,电影院可以卖票,歌剧院可以卖票,客运站也可以卖票,那么我们是否需要把电影院、、歌剧院和客运站都设计成一个类架构以提供卖票服务?要知道,连经理人都可...
  • YmirBoy
  • YmirBoy
  • 2013年02月06日 19:15
  • 580

几种编程语言的优缺点

圣经记载:在远古的时候,人类都使用一种语言,全世界的人决定一起造一座通天的塔,就是巴别塔,后来被上帝知道了,上帝就让人们使用不同的语言,这个塔就没能造起来。 巴别塔不建自毁,与其说上帝的分化将人类的语...
  • dbyoung
  • dbyoung
  • 2017年01月22日 09:57
  • 1285

【Delphi】接口类型中的陷阱

function abc(A: Integer): Integer; 这是一个Delphi的函数声明,看上去很简单,只有一个参数而已,但是真实情况呢?在编译成二进制代码后,实际上函数的参数已经有3个了...
  • aqtata
  • aqtata
  • 2014年02月11日 16:08
  • 1679

Delphi 多线程编程(1)

本文的内容取自万一博客,并重新加以整理,在此留存仅仅是方便自己学习和查阅。所有代码均亲自测试 delphi7下测试有效。图片均为自己制作。 多线程应该是编程工作者的基础技能, 但这个基础我从来没...
  • lailai186
  • lailai186
  • 2013年04月09日 07:55
  • 2583

DELPHI短信接口开发经验及具体开发实现

一、群发短信mt 参数名称 说明 是否必须    备注 Sn 软件序列号 是 格式XXX-...
  • baidu_31002735
  • baidu_31002735
  • 2015年09月07日 09:33
  • 1363

Delphi 串口通信(1)

利用 Delphi实现串口通信的常用的方法有 3种: 一是利用控件,如 MSCOMM控件和 SPCOMM控件; 二是使用 API函数; 三是调用其他串口通信程序。其中利用 API编写...
  • lailai186
  • lailai186
  • 2013年08月02日 10:16
  • 4675

Delphi调用DLL中的接口

最近很少上论坛,这几次发现好几个问题都是围绕如何使用DLL中的接口而展开的。 问题描述: 具体问题就是在隐式使用接口变量后,在FreeLibrary执行后,就会出现一个非法访址的错误。 ...
  • yanjinrong
  • yanjinrong
  • 2014年11月02日 10:57
  • 1206
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Delphi下的接口编程
举报原因:
原因补充:

(最多只允许输入30个字)