Delphi:如何实现IUnknown的QueryInterface?
在 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);
Delphi 抱怨:
运算符不适用于该操作数类型
显然我不知道如何或什么分配给无类型 out
参数。我可以随机尝试一些事情,希望编译器停止抱怨:
Obj := TFooBar.Create(Self);
Obj := Pointer(TFooBar.Create(Self));
Obj := Pointer(TFooBar.Create(Self) as IFooBar);
忽略我编写的所有代码(如果需要):如何在 TInterfacedObject< 的后代对象中实现
QueryInterface
/代码>?
我一直试图解决的真正问题可以归结为我想:
我想重写接口中的方法
同样的方式重写接口中的方法:
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;
事情很快就会变得非常笨拙。
In Delphi, IUnknown
is declared as:
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
Note: The output parameter is untyped
In my TInterfacedObject
descendant i need to handle QueryInterface
, so i can return an object that supports the requested interface:
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;
The problem comes on the line:
Obj := (TFooBar.Create(Self) as IFooBar);
Delphi complains:
Operator not applicable to this operand type
Obviously i don't know how or what to assign to an untyped out
parameter. i can randomly try things, in hopes that the compiler will stop complaining:
Obj := TFooBar.Create(Self);
Obj := Pointer(TFooBar.Create(Self));
Obj := Pointer(TFooBar.Create(Self) as IFooBar);
Ignoring all the code i've written (if required): how do i implement QueryInterface
in an object descendant from TInterfacedObject
?
The real problem i've been trying to solve can be boiled down to i want to:
i want to override methods in an interface
In the same way:
TList = class(TObject)
...
function GetItem(Index: Integer): Pointer;
procedure SetItem(Index: Integer; Value: Pointer);
property Items[Index: Integer]: Pointer read GetItem write SetItem;
end;
can be overridden in a descendant class:
TStudentList = class(TList)
...
function GetItem(Index: Integer): TStudent;
procedure SetItem(Index: Integer; Value: TStudent);
property Items[Index: Integer]: TStudent read GetItem write SetItem;
end;
i want to so the same with interfaces:
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;
Problem is that how i have to begin loading up my implementing object with:
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;
And there isn't just the two methods in IFoo
, there's 6. And then if i want to add another supported interface:
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;
And things get really unwieldy very fast.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您需要对赋值语句的左侧进行类型转换。这样,非类型化参数就具有类型,并且编译器知道如何为其分配值:
请注意,您正在破坏 COM 的要求。如果您查询一个接口,您应该能够查询 IUnknown 的结果并始终获得相同的值:
如果您只想生成 TFooBar 类型的新对象,则为您的接口提供一个生成这些对象的方法:
You need to type-cast the left side of the assignment statement. That way, the untyped parameter has a type, and the compiler knows how to assign it a value:
Please note that you're breaking one of the requirements of COM. If you query for an interface, you should be able to query the result for IUnknown and always get the same value:
If you just want to generate new objects of type TFooBar, then give your interface a method that generates those:
除了 Rob 在这里违反规则的评论之外,您甚至可以通过此构造取得成功:
我没有调查这一点,但您可能会遇到引用计数的一些问题......
Besides of Rob's remarks of breaking the rules here, you can even succeed with this construct:
I didn't investigate this, but you might get some problems with reference counting...
基于 System.pas 中 TObject.GetInterface 的实现,我建议这样做:
Based on the implementation of TObject.GetInterface in System.pas I would suggest this: