Delphi:IunknownのQueryInterfaceを実装する方法
-
26-09-2020 - |
質問
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は以下のとおりです。
オペレータこのオペランドタイプ
には適用されません
明らかに私は untyped 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
には2つのメソッドだけではなく、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;
.
と物事は非常に速く扱いにくいです。
解決
Assignmentステートメントの左側を入力する必要があります。このように、解析されていないパラメータにはタイプがあり、コンパイラはそれに値を割り当てる方法を知っています:
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;
.
私はこれを調査しなかったが、あなたは参照カウントでいくつかの問題を得るかもしれません...
System.PASでのToBject.GetInterfaceの実装に基づいてこれを推奨します。
Pointer(Obj) := TFooBar.Create(Self);
.