3

私はまだインターフェイスの使用を理解しようとしています。私は、DLL 内でインスタンス化されたオブジェクトと対話するという唯一の目的でそれらを実装しています。私がそれを使用すると、すべてが正常に機能し、すべてのメソッドが期待どおりに機能するなどです。問題は、そのインターフェイスの背後にあるオブジェクトをクリーンアップするときです。

私はそのようなシンプルなインターフェースを持っています

IMyInterface = interface
  ['{d52b14f3-156b-4df8-aa16-cb353193d27c}']
  procedure Foo;
end;

そしてそれのためのオブジェクト

TMyObject = class(TInterfacedObject, IMyInterface)
private
  procedure Foo;
end;

DLL 内には、このオブジェクトのグローバル変数と、このインスタンスを作成および破棄するための 2 つのエクスポート関数があります。

var
  _MyObject: TMyObject;

function CreateMyObject: IMyInterface; stdcall;
begin
  _MyObject:= TMyObject.Create;
  Result:= IMyInterface(_MyObject);
end;

function DestroyMyObject: Integer; stdcall;
begin
  _MyObject.Free; //   <--   Invalid Pointer Operation
end;

オブジェクトのデストラクタは事実上何もしませんが、inherited私はまだこの問題を抱えています。でも乗りInvalid Pointer Operationます_MyObject.Free

これらのエクスポートされたメソッドにアクセスするにはLoadLibrary、 とを使用します。GetProcAddress

これが発生する理由と修正方法を教えてください。

4

2 に答える 2

6

無効なポインター操作は、割り当てられていないものを解放したことを意味します。

この場合、解放しようとしているオブジェクトはすでに破棄されています。デストラクタにブレークポイントを置いて、自分の目で確かめてください。

インターフェイスには参照カウント コードが関連付けられているため、インターフェイスについて読んだすべてのアドバイスで、参照カウントを持たないオブジェクト参照とインターフェイスを混在させないようにと書かれているのはそのためです。

オブジェクトをインスタンス化してグローバル変数に割り当てると、オブジェクトの参照カウントはゼロになり、インターフェイスはまだ関与していません。関数の結果に代入すると、参照カウントが 1 になります。デバッグ DCU を有効にし、デバッガーでそのステートメントをステップ実行すると、それがどのように発生するかを確認できます。(ちなみに、型キャストは必要ありません。コンパイラは、オブジェクトがターゲット インターフェイスを実装していることを既に認識しており、それ自体で単純な代入を許可します。)

他の場所では、この DLL の消費側で、オブジェクトの最後のインターフェイス参照を保持する変数がクリアされます。参照カウントがゼロになり、オブジェクトはそれ自体を破棄します。

オブジェクトが破棄されると、グローバル変数はダングリング リファレンスになります。もう存在しないオブジェクトのアドレスを保持しています。それを呼び出すFreeと、デストラクタはアドレスをメモリ マネージャに渡しますが、メモリ マネージャはそのアドレスに (もう) 何もないことを認識しているため、例外が発生します。

これを修正するには、そのグローバル変数の型をインターフェイス型に変更してから、Free呼び出しを削除します。nil変数に代入するステートメントに置き換えます。これらの変更により、オブジェクトを作成し、インターフェイス参照を変数に格納すると、オブジェクトの参照カウントが 1 に設定され、それを呼び出し元に返すと 2 に設定されます。コンシューマーがその参照をクリアすると、カウントは 1 に下がり、新しいnil割り当てによってカウントが 0 に設定され、適切なタイミングでオブジェクト自体が解放されます。

インターフェイス参照を介してオブジェクトにアクセスし始めたら、通常のオブジェクト参照を介してそれを使用しないことをお勧めします。すでに破棄されたオブジェクトを誤って使用するリスクが大きすぎます。

于 2013-09-12T04:06:38.317 に答える
2

TInterfacedObject ベースのクラスで .Free を呼び出さないでください。インターフェイスへの最後の参照が nil されると、自動的に解放されます。

コード例は次のようになります。

var
  _MyObject: IUnknown;

function CreateMyObject: IMyInterface; stdcall;
begin
  // unified interface
  _MyObject:= TMyObject.Create as IUnknown;
  // cast to IMyInterface
  Result:= _MyObject as IMyInterface;
end;

function DestroyMyObject: Integer; stdcall;
begin
  _MyObject := nil; 
end;
于 2013-09-12T04:54:31.820 に答える