Delphi: Как реализовать QueryInterface IUnknown?

В 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);

При игнорировании всего кода я записал (при необходимости): как я реализую 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;

И вещи становятся действительно громоздкими очень быстро.

11
задан Ian Boyd 21 July 2010 в 20:54
поделиться

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;
6
ответ дан 3 December 2019 в 10:03
поделиться

Кроме замечаний Роба о нарушении правил здесь, вы можете даже преуспеть с этой конструкцией:

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;

Я не исследовал это, но вы можете получить некоторые проблемы с подсчетом ссылок...

2
ответ дан 3 December 2019 в 10:03
поделиться

Основываясь на реализации TObject.GetInterface в System.pas, я бы предложил следующее:

  Pointer(Obj) := TFooBar.Create(Self);
2
ответ дан 3 December 2019 в 10:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: