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.
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:
IFooBar(Obj) := TFooBar.Create(Self) as IFooBar;
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:
Foo.QueryInterface(IUnknown, I1);
I1.QueryInterface(IFooBar, B);
B.QueryInterface(IUnknown, I2);
Assert(I1 = I2);
If you just want to generate new objects of type TFooBar, then give your interface a method that generates those:
function TFoo.NewFooBar: IFooBar;
begin
Result := TFooBar.Create(Self) as IFooBar;
end;
Besides of Rob's remarks of breaking the rules here, you can even succeed with this construct:
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;
I didn't investigate this, but you might get some problems with reference counting...
Based on the implementation of TObject.GetInterface in System.pas I would suggest this:
Pointer(Obj) := TFooBar.Create(Self);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With