经过查看System.Rtti
源代码和一些测试,我终于让它正常工作了。
据我所知,有四种可能性
1.接口是从OLE对象获取的。在这种情况下,强制转换AIntf as Object
会抛出异常。类型是IDispatch
,我可以通过以下方式获取
TRttiContext.Create.GetType(TypeInfo(System.IDispatch))
2. 接口是从TRawVirtualClass
获得的,这是一个动态创建的类。(例如,所有本地的Android IOS和Mac接口)。使用AIntf as TObject
将接口转换为TRawVirtualClass
对象,然后使用RTTI获取此对象的FIIDs
字段。它的类型是TArray<TGUID>
,第一个元素是此接口(然后是其祖先接口)的GUID。然后我们可以通过GUID获取它的RTTI。
3. 接口是从TVirtualInterface
获得的。使用AIntf as TObject
将其转换为TVirtualInterface
实例,然后获取其FIID
字段(类型为TGUID
)。
4. 接口是从Delphi对象获得的。请参考@Remy Lebeau的答案。
我编写了一个TInterfaceHelper:
unit InterfaceHelper;
interface
uses System.Rtti, System.TypInfo, System.Generics.Collections, System.SysUtils;
type
TInterfaceHelper = record
strict private
type
TInterfaceTypes = TDictionary<TGUID, TRttiInterfaceType>;
class var FInterfaceTypes: TInterfaceTypes;
class var Cached: Boolean;
class var Caching: Boolean;
class procedure WaitIfCaching; static;
class procedure CacheIfNotCachedAndWaitFinish; static;
class constructor Create;
class destructor Destroy;
public
class procedure RefreshCache; static;
class function GetType(AIntf: IInterface): TRttiInterfaceType;
overload; static;
class function GetType(AGUID: TGUID): TRttiInterfaceType; overload; static;
class function GetType(AIntfInTValue: TValue): TRttiInterfaceType;
overload; static;
class function GetTypeName(AIntf: IInterface): String; overload; static;
class function GetTypeName(AGUID: TGUID): String; overload; static;
class function GetQualifiedName(AIntf: IInterface): String;
overload; static;
class function GetQualifiedName(AGUID: TGUID): String; overload; static;
class function GetMethods(AIntf: IInterface): TArray<TRttiMethod>; static;
class function GetMethod(AIntf: IInterface; const MethodName: String)
: TRttiMethod; static;
class function InvokeMethod(AIntf: IInterface; const MethodName: String;
const Args: array of TValue): TValue; overload; static;
class function InvokeMethod(AIntfInTValue: TValue; const MethodName: String;
const Args: array of TValue): TValue; overload; static;
end;
implementation
uses System.Classes,
System.SyncObjs, DUnitX.Utils;
class function TInterfaceHelper.GetType(AIntf: IInterface): TRttiInterfaceType;
var
ImplObj: TObject;
LGUID: TGUID;
LIntfType: TRttiInterfaceType;
TempIntf: IInterface;
begin
Result := nil;
try
ImplObj := AIntf as TObject;
except
Result := TRttiContext.Create.GetType(TypeInfo(System.IDispatch))
as TRttiInterfaceType;
Exit;
end;
if ImplObj.ClassType.InheritsFrom(TRawVirtualClass) then
begin
LGUID := ImplObj.GetField('FIIDs').GetValue(ImplObj).AsType < TArray <
TGUID >> [0];
Result := GetType(LGUID);
end
else if ImplObj.ClassType.InheritsFrom(TVirtualInterface) then
begin
LGUID := ImplObj.GetField('FIID').GetValue(ImplObj).AsType<TGUID>;
Result := GetType(LGUID);
end
else
begin
for LIntfType in (TRttiContext.Create.GetType(ImplObj.ClassType)
as TRttiInstanceType).GetImplementedInterfaces do
begin
if ImplObj.GetInterface(LIntfType.GUID, TempIntf) then
begin
if AIntf = TempIntf then
begin
Result := LIntfType;
Exit;
end;
end;
end;
end;
end;
class constructor TInterfaceHelper.Create;
begin
FInterfaceTypes := TInterfaceTypes.Create;
Cached := False;
Caching := False;
RefreshCache;
end;
class destructor TInterfaceHelper.Destroy;
begin
FInterfaceTypes.DisposeOf;
end;
class function TInterfaceHelper.GetQualifiedName(AIntf: IInterface): String;
var
LType: TRttiInterfaceType;
begin
Result := string.Empty;
LType := GetType(AIntf);
if Assigned(LType) then
Result := LType.QualifiedName;
end;
class function TInterfaceHelper.GetMethod(AIntf: IInterface;
const MethodName: String): TRttiMethod;
var
LType: TRttiInterfaceType;
begin
Result := nil;
LType := GetType(AIntf);
if Assigned(LType) then
Result := LType.GetMethod(MethodName);
end;
class function TInterfaceHelper.GetMethods(AIntf: IInterface)
: TArray<TRttiMethod>;
var
LType: TRttiInterfaceType;
begin
Result := [];
LType := GetType(AIntf);
if Assigned(LType) then
Result := LType.GetMethods;
end;
class function TInterfaceHelper.GetQualifiedName(AGUID: TGUID): String;
var
LType: TRttiInterfaceType;
begin
Result := string.Empty;
LType := GetType(AGUID);
if Assigned(LType) then
Result := LType.QualifiedName;
end;
class function TInterfaceHelper.GetType(AGUID: TGUID): TRttiInterfaceType;
begin
CacheIfNotCachedAndWaitFinish;
Result := FInterfaceTypes.Items[AGUID];
end;
class function TInterfaceHelper.GetTypeName(AGUID: TGUID): String;
var
LType: TRttiInterfaceType;
begin
Result := string.Empty;
LType := GetType(AGUID);
if Assigned(LType) then
Result := LType.Name;
end;
class function TInterfaceHelper.InvokeMethod(AIntfInTValue: TValue;
const MethodName: String; const Args: array of TValue): TValue;
var
LMethod: TRttiMethod;
LType: TRttiInterfaceType;
begin
LType := GetType(AIntfInTValue);
if Assigned(LType) then
LMethod := LType.GetMethod(MethodName);
if not Assigned(LMethod) then
raise Exception.Create('Method not found');
Result := LMethod.Invoke(AIntfInTValue, Args);
end;
class function TInterfaceHelper.InvokeMethod(AIntf: IInterface;
const MethodName: String; const Args: array of TValue): TValue;
var
LMethod: TRttiMethod;
begin
LMethod := GetMethod(AIntf, MethodName);
if not Assigned(LMethod) then
raise Exception.Create('Method not found');
Result := LMethod.Invoke(TValue.From<IInterface>(AIntf), Args);
end;
class function TInterfaceHelper.GetTypeName(AIntf: IInterface): String;
var
LType: TRttiInterfaceType;
begin
Result := string.Empty;
LType := GetType(AIntf);
if Assigned(LType) then
Result := LType.Name;
end;
class procedure TInterfaceHelper.RefreshCache;
var
LTypes: TArray<TRttiType>;
begin
WaitIfCaching;
FInterfaceTypes.Clear;
Cached := False;
Caching := True;
TThread.CreateAnonymousThread(
procedure
var
LType: TRttiType;
LIntfType: TRttiInterfaceType;
begin
LTypes := TRttiContext.Create.GetTypes;
for LType in LTypes do
begin
if LType.TypeKind = TTypeKind.tkInterface then
begin
LIntfType := (LType as TRttiInterfaceType);
if TIntfFlag.ifHasGuid in LIntfType.IntfFlags then
begin
FInterfaceTypes.AddOrSetValue(LIntfType.GUID, LIntfType);
end;
end;
end;
Caching := False;
Cached := True;
end).Start;
end;
class procedure TInterfaceHelper.WaitIfCaching;
begin
if Caching then
TSpinWait.SpinUntil(
function: Boolean
begin
Result := Cached;
end);
end;
class procedure TInterfaceHelper.CacheIfNotCachedAndWaitFinish;
begin
if Cached then
Exit
else if not Caching then
begin
RefreshCache;
WaitIfCaching;
end
else
WaitIfCaching;
end;
class function TInterfaceHelper.GetType(AIntfInTValue: TValue)
: TRttiInterfaceType;
var
LType: TRttiType;
begin
Result := nil;
LType := AIntfInTValue.RttiType;
if LType is TRttiInterfaceType then
Result := LType as TRttiInterfaceType;
end;
end.
然后:
uses InterfaceHelper;
function GetRttiFromInterface(AIntf: IInterface; out RttiType: TRttiType): Boolean;
begin
RttiType := TInterfaceHelper.GetType(AIntf);
Result := Assigned(RttiType);
end;
IInterface
)时就不是这样了。 - Remy Lebeau