Delphi: Как реализовать QueryInterface из IUnknown?
В Дельфи 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;
И все становится очень громоздким очень быстро.
3 ответа
Вам необходимо привести тип левой части оператора присваивания. Таким образом, нетипизированный параметр имеет тип, и компилятор знает, как присвоить ему значение:
IFooBar(Obj) := TFooBar.Create(Self) as IFooBar;
Обратите внимание, что вы нарушаете одно из требований 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;
Помимо замечаний Роба о нарушении правил, вы даже можете добиться успеха с помощью этой конструкции:
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;
Я не исследовал это, но у вас могут возникнуть проблемы с подсчетом ссылок...
Основываясь на реализации TObject.GetInterface в System.pas, я бы предложил это:
Pointer(Obj) := TFooBar.Create(Self);