function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
...
1.2.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)
1.2.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;
1.2.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;
1.2.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;