web-dev-qa-db-ja.com

基礎となるRCWから分離されたCOMオブジェクトは使用できません

OpcRcw.da.dllを使用しようとしています。テストコンソールプロジェクト内でこのdllを相互運用すると、すべてが機能しますが、dllプロジェクトをビルドして相互運用体操を行い、ライブラリをコンソールプロジェクトに参照すると、このエラーが発生します。

基礎となるRCWから分離されたCOMオブジェクトは使用できません。

RCW refを殺さないために、クラスlibプロジェクトに何をする必要がありますか?

45
kevin marchand

実際のアプリケーションが何をしているのかを伝えるのは少し難しいですが、COMオブジェクトをインスタンス化し、Timer.Elapsedイベントなどで別のスレッドからアクセスしようとしているようです。アプリケーションがマルチスレッドの場合、使用する各スレッド内でCOMオブジェクトをインスタンス化する必要があります。

35
AJ.

これにはいくつかの理由がありますが、私が知っている大きな理由は以下のとおりです。

デリゲートへの強い参照のないイベントハンドラ

呼び出し元は、コールバックデリゲートへの強い参照を保持せずに、comオブジェクトのイベントにサブスクライブします。これを正しく行う方法としない方法の例を次に示します。これは、デリゲートへの強い参照を保持する必要があるためです。スコープから外れると、ラッパーはインターフェイスの参照カウントを解放します悪いことが起こります。

public class SomeClass
{
    private Interop.ComObjectWrapper comObject;
    private event ComEventHandler comEventHandler;

    public SomeClass()
    {
        comObject = new Interop.ComObjectWrapper();

        // NO - BAD!
        comObject.SomeEvent += new ComEventHandler(EventCallback);

        // YES - GOOD!
        comEventHandler = new ComEventHandler(EventCallback);
        comObject.SomeEvent += comEventHandler
    }

    public void EventCallback()
    {
        // DO WORK
    }
}

破棄されたランタイム呼び出し可能ラッパーの呼び出し

ラッパーは破棄されており、破棄後に呼び出しが行われています。これが発生する一般的な方法は、コントロールがactivexコントロールまたはCOMオブジェクトを使用しており、コントロールのDispose()が順番どおりに呼び出されない場合です。

  • フォームがClose()を呼び出します。
  • System.Windows.Forms.Close()はDispose()を呼び出します
  • フォームの仮想Dispose()が呼び出され、どこかでbase.Dispose()が呼び出されることが期待されます。 Systems.Windows.Forms.Dispose()は、子コントロールからであっても、フォーム上のすべてのCOMオブジェクトとイベント同期を解放します。
  • Comオブジェクトを所有するコントロールがbase.Dispose()の後に明示的に破棄され、そのCOMオブジェクトのメソッドを呼び出すと、これらは失敗し、「基になるRCWから分離されたCOMオブジェクトは利用される"。

デバッグ手順

この問題をデバッグする良い方法は、次を実行することです。

  1. Interopクラス(別名、ランタイム呼び出し可能ラッパーまたはRCW)から継承するクラスを作成します。
  2. DetachEventSinkをオーバーライドする
  3. 破棄をオーバーライド
  4. 相互運用クラスを直接呼び出すのではなく、新しいクラスを呼び出します
  5. DetachEventSinkにブレークポイントを追加して破棄
  6. これらのメソッドを順番に呼び出しているのは誰ですか

もう1つ

これはこの問題とは関係ありませんが、このトピックについては、特にご存じない限り、COMオブジェクトが使用されているスレッドがSTAとしてマークされていることを必ず確認してください。これを行うには、デバッガーを中断し、次から返される値を確認します。

Thread.CurrentThread.GetApartmentState();
78
Steve Sheldon