Delphi:如何实现IUnknown的QueryInterface?

Ian*_*oyd 11 delphi interface queryinterface iunknown

在Delphi中,IUnknown声明为:

function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
Run Code Online (Sandbox Code Playgroud)

注意:输出参数是无类型的

在我的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;
Run Code Online (Sandbox Code Playgroud)

问题就出现了:

Obj := (TFooBar.Create(Self) as IFooBar);
Run Code Online (Sandbox Code Playgroud)

德尔福抱怨:

运算符不适用于此操作数类型

显然我不知道如何或分配给无类型 out参数.我可以随意尝试,希望编译器停止抱怨:

Obj := TFooBar.Create(Self);

Obj := Pointer(TFooBar.Create(Self));

Obj := Pointer(TFooBar.Create(Self) as IFooBar);
Run Code Online (Sandbox Code Playgroud)

忽略我写的所有代码(如果需要):我如何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;
Run Code Online (Sandbox Code Playgroud)

可以在后代类中重写:

TStudentList = class(TList)
...
   function GetItem(Index: Integer): TStudent; 
   procedure SetItem(Index: Integer; Value: TStudent);
   property Items[Index: Integer]: TStudent read GetItem write SetItem;
end;
Run Code Online (Sandbox Code Playgroud)

我想要与接口相同:

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;
Run Code Online (Sandbox Code Playgroud)

问题是我如何开始加载我的实现对象:

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;
Run Code Online (Sandbox Code Playgroud)

并且不仅有两种方法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;
Run Code Online (Sandbox Code Playgroud)

事情变得非常笨拙很快.

Rob*_*edy 6

您需要在赋值语句的左侧键入类型.这样,untyped参数有一个类型,编译器知道如何为它赋值:

IFooBar(Obj) := TFooBar.Create(Self) as IFooBar;
Run Code Online (Sandbox Code Playgroud)

请注意,您违反了COM的一项要求.如果查询接口,您应该能够查询IUnknown的结果并始终获得相同的值:

Foo.QueryInterface(IUnknown, I1);
I1.QueryInterface(IFooBar, B);
B.QueryInterface(IUnknown, I2);
Assert(I1 = I2);
Run Code Online (Sandbox Code Playgroud)

如果您只想生成TFooBar类型的新对象,那么为您的接口提供一个生成这些对象的方法:

function TFoo.NewFooBar: IFooBar;
begin
  Result := TFooBar.Create(Self) as IFooBar;
end;
Run Code Online (Sandbox Code Playgroud)