Pregunta

En Delfos, IUnknown se declara como:

function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;

Nota: El parámetro de salida no está escrito.

En mi TInterfacedObject descendiente que necesito manejar QueryInterface, entonces puedo devolver un objeto que admita la interfaz solicitada:

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;

El problema viene en la línea:

Obj := (TFooBar.Create(Self) as IFooBar);

Delfos se queja:

Operador no aplicable a este tipo de operando

Obviamente no sé cómo ni qué asignar a un sin escribir out parámetro.Puedo probar cosas al azar, con la esperanza de que el compilador deje de quejarse:

Obj := TFooBar.Create(Self);

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

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

Haciendo caso omiso de todo el código que he escrito (si es necesario):¿Cómo lo implemento? QueryInterface en un objeto descendiente de TInterfacedObject?


El verdadero problema que he estado tratando de resolver se puede reducir a lo que quiero:

quiero anular métodos en una interfaz

Del mismo modo:

TList = class(TObject)
...
   function GetItem(Index: Integer): Pointer; 
   procedure SetItem(Index: Integer; Value: Pointer);
   property Items[Index: Integer]: Pointer read GetItem write SetItem;
end;

se puede anular en una clase descendiente:

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

Quiero hacer lo mismo con las 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;

El problema es que tengo que comenzar a cargar mi objeto de implementación con:

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;

Y no existen sólo los dos métodos en IFoo, hay 6.Y luego, si quiero agregar otra interfaz compatible:

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;

Y las cosas se vuelven realmente difíciles de manejar muy rápidamente.

¿Fue útil?

Solución

Necesitas encasillar el izquierda lado de la declaración de asignación.De esa manera, el parámetro sin tipo tiene un tipo y el compilador sabe cómo asignarle un valor:

IFooBar(Obj) := TFooBar.Create(Self) as IFooBar;

Tenga en cuenta que está rompiendo uno de los requisitos de COM.Si consulta una interfaz, debería poder consultar el resultado de IUnknown y obtener siempre el mismo valor:

Foo.QueryInterface(IUnknown, I1);
I1.QueryInterface(IFooBar, B);
B.QueryInterface(IUnknown, I2);
Assert(I1 = I2);

Si solo desea generar nuevos objetos de tipo TFooBar, proporcione a su interfaz un método que los genere:

function TFoo.NewFooBar: IFooBar;
begin
  Result := TFooBar.Create(Self) as IFooBar;
end;

Otros consejos

Además de los comentarios de Rob sobre romper las reglas aquí, incluso puedes tener éxito con esta construcción:

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;

No investigué esto, pero es posible que tengas algunos problemas con el recuento de referencias...

Basado en la implementación de TObject.GetInterface en System.pas, sugeriría esto:

  Pointer(Obj) := TFooBar.Create(Self);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top