Delfos:¿Cómo implementar QueryInterface de IUnknown?
-
26-09-2020 - |
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.
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);