18

COM サーバーを呼び出してメモリを割り当てるマネージ オブジェクトがあります。マネージ オブジェクトは、メモリ リークを回避するために、マネージ オブジェクトがなくなる前にそのメモリを解放するために、再度 COM サーバーを呼び出す必要があります。このオブジェクトIDisposableは、正しいメモリ解放 COM 呼び出しが確実に行われるようにするために実装されます。

Disposeメソッドが呼び出されない場合は、オブジェクトのファイナライザーにメモリを解放してもらいたいです。問題は、ファイナライズの規則では、参照にアクセスしてはならないということです。これは、他のどのオブジェクトが既に GC および/またはファイナライズされているかわからないためです。これにより、タッチ可能なオブジェクトの状態はフィールドのみになります (ハンドルが最も一般的です)。

しかし、COM サーバーを呼び出すには、フィールドに格納する Cookie のメモリを解放するために、ランタイム呼び出し可能ラッパー (RCW) を経由する必要があります。 そのRCWはファイナライザーから安全に呼び出すことができますか(この時点でGCまたはファイナライズされていないことが保証されていますか)?

ファイナライズに慣れていない方のために説明すると、ファイナライザー スレッドはマネージド アプリドメインの実行中にバックグラウンドで実行されますが、参照に触れることは理論的には問題ありません。参照関係の順序で。これにより、ファイナライザーから触れても安全であると想定できるものが制限されます。管理対象オブジェクトへの参照は、参照が null でない場合でも、「不良」(収集されたメモリ) である可能性があります。

更新:試してみたところ、これが得られました:

タイプ 'System.Runtime.InteropServices.InvalidComObjectException' の未処理の例外が myassembly.dll で発生しました

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

4

2 に答える 2

16

CLRチーム自身から、実際には安全ではないことがわかりました-RCWにGCHandleを割り当てても安全なうちに(RCWを最初に取得したとき)。これにより、呼び出す必要がある管理対象オブジェクトがファイナライズされる前に、GC とファイナライザーが RCW を合計していないことが保証されます。

class MyManagedObject : IDisposable
{
    private ISomeObject comServer;
    private GCHandle rcwHandle;
    private IServiceProvider serviceProvider;
    private uint cookie;

    public MyManagedObject(IServiceProvider serviceProvider)
    {
        this.serviceProvider = serviceProvider;
        this.comServer = this. serviceProvider.GetService(/*some service*/) as ISomeObject;
        this.rcwHandle = GCHandle.Alloc(this.comServer, GCHandleType.Normal);
        this.cookie = comServer.GetCookie();
    }

    ~MyManagedObject()
    {
        this.Dispose(false);
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // dispose owned managed objects here.
        }

        if (this.rcwHandle.IsAllocated)
        {
            // calling this RCW is safe because we have a GC handle to it.
            this.comServer.ReleaseCookie(this.cookie);

            // Now release the GC handle on the RCW so it can be freed as well
            this.rcwHandle.Free();
        }
    }
}

私の特定のケースでは、私のアプリは CLR 自体をホストしています。したがって、ファイナライザー スレッドが実行される前に mscoree!CoEEShutdownCOM を呼び出すと、RCW が強制終了され、表示されていたInvalidComObjectExceptionエラーが発生します。

しかし、CLR 自体がホストされていない通常のケースでは、これでうまくいくはずだと言われています。

于 2009-10-15T22:04:17.197 に答える
7

いいえ、ファイナライザ スレッドから RCW にアクセスするのは安全ではありません。ファイナライザー スレッドに到達すると、RCW がまだ有効であるという保証はありません。ファイナライザー キューでオブジェクトよりも前にある可能性があり、デストラクタがファイナライザー スレッドで実行されるまでに解放される可能性があります。

于 2009-10-15T17:54:02.777 に答える