在Delphi中,IUnknown
被声明为:
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
注意:输出参数是无类型的
在我的TInterfacedObject
后代我需要处理QueryInterface
,所以我可以返回一个支持所请求接口的对象:
function TFoo.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if IsEqualGUID(IID, IFooBar) then
begin
Obj := (TFooBar.Create(Self) as IFooBar);
Result := S_OK;
end
else
Result := inherited QueryInterface(IID, {out}Obj);
end;
问题就出现了:
Obj := (TFooBar.Create(Self) as IFooBar);
德尔福抱怨道:
运算符不适用于此操作数类型
显然,我不知道如何分配无类型 out
参数。我可以随意尝试,希望编译器停止抱怨:
Obj := TFooBar.Create(Self);
Obj := Pointer(TFooBar.Create(Self));
Obj := Pointer(TFooBar.Create(Self) as IFooBar);
忽略我编写的所有代码(如果需要):如何在QueryInterface
的对象后代中实现TInterfacedObject
?
我一直试图解决的真正问题可归结为我想:
我想覆盖界面中的方法
以同样的方式:
TList = class(TObject)
...
function GetItem(Index: Integer): Pointer;
procedure SetItem(Index: Integer; Value: Pointer);
property Items[Index: Integer]: Pointer read GetItem write SetItem;
end;
可以在后代类中重写:
TStudentList = class(TList)
...
function GetItem(Index: Integer): TStudent;
procedure SetItem(Index: Integer; Value: TStudent);
property Items[Index: Integer]: TStudent read GetItem write SetItem;
end;
我希望界面如此相同:
IFoo = interface(IUnknown)
...
function GetItem(Index: Variant): Variant;
procedure SetItem(Index: Variant; Value: Variant);
property Items[Index: Variant]: Variant read GetItem write SetItem;
end;
IFooGuidString = interface(IFoo)
...
function GetItem(Index: TGUID): string ;
procedure SetItem(Index: TGUID; Value: string );
property Items[Index: TGUID]: string read GetItem write SetItem;
end;
问题是我必须如何开始加载我的实现对象:
TFoo = class(TInterfacedObject, IFoo, IFooGuidString)
public
function IFoo.GetItem = FooGetItem;
procedure IFoo.SetItem = FooSetItem;
function FooGetItem(Index: Variant): Variant;
procedure FooSetItem(Index: Variant; Value: Variant);
function IFooGuidString.GetItem = FooGuidStringGetItem;
procedure IFooGuidString.SetItem = FooGuidStringSetItem;
function FooGuidStringGetItem(Index: TGUID): string ;
procedure FooGuidStringSetItem(Index: TGUID; Value: string );
end;
IFoo
中不仅有两种方法,还有6.然后如果我想添加另一个支持的界面:
IFooInt64String = interface(IFoo)
...
function GetItem(Index: Int64): string ;
procedure SetItem(Index: Int64; Value: string );
property Items[Index: Int64]: string read GetItem write SetItem;
end;
TFoo = class(TInterfacedObject, IFoo, IFooGuidString)
public
function IFoo.GetItem = FooGetItem;
procedure IFoo.SetItem = FooSetItem;
function FooGetItem(Index: Variant): Variant;
procedure FooSetItem(Index: Variant; Value: Variant);
function IFooGuidString.GetItem = FooGuidStringGetItem;
procedure IFooGuidString.SetItem = FooGuidStringSetItem;
function FooGuidStringGetItem(Index: TGUID): string ;
procedure FooGuidStringSetItem(Index: TGUID; Value: string );
function IFooInt64String.GetItem = FooInt64StringGetItem;
procedure IFooInt64String.SetItem = FooInt64StringSetItem;
function FooInt64StringGetItem(Index: Int64): string ;
procedure FooInt64StringSetItem(Index: Int64; Value: string );
end;
事情变得非常笨拙。
答案 0 :(得分:6)
您需要输入赋值语句的 left 侧。这样,untyped参数有一个类型,编译器知道如何为它赋值:
IFooBar(Obj) := TFooBar.Create(Self) as IFooBar;
请注意,您正在打破其中一个requirements of COM。如果您查询接口,您应该能够查询IUnknown的结果并始终获得相同的值:
Foo.QueryInterface(IUnknown, I1);
I1.QueryInterface(IFooBar, B);
B.QueryInterface(IUnknown, I2);
Assert(I1 = I2);
如果您只想生成TFooBar类型的新对象,请为您的界面提供一个生成这些对象的方法:
function TFoo.NewFooBar: IFooBar;
begin
Result := TFooBar.Create(Self) as IFooBar;
end;
答案 1 :(得分:2)
基于System.pas中TObject.GetInterface的实现,我建议:
Pointer(Obj) := TFooBar.Create(Self);
答案 2 :(得分:2)
除了Rob在这里违反规则的言论之外,你甚至可以成功使用这个结构:
function TFoo.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if IsEqualGUID(IID, IFooBar) then
Result := TFooBar.Create(Self).QueryInterface(IID, obj)
else
Result := inherited QueryInterface(IID, {out}Obj);
end;
我没有对此进行调查,但您可能会在引用计数方面遇到一些问题......