21

Office 相互運用アセンブリを使用するアプリケーションがあります。ランタイムによって管理される「Runtime Callable Wrapper (RCW)」について認識しています。しかし、参照カウントがどのようにインクリメントされるかはよくわかりません。MSDN によると、

RCW は、それを呼び出すマネージ クライアントの数に関係なく、ラップされた COM オブジェクトへの参照を 1 つだけ保持します。

私が正しく理解している場合、次の例では、

using Microsoft.Office.Interop.Word;

static void Foo(Application wrd)
{
    /* .... */
}

static void Main(string[] args)
{
    var wrd = new Application();
    Foo(wrd);
    /* .... */
}

wrdインスタンスを別のメソッドに渡しています。しかし、これは内部参照カウントをインクリメントしません。では、参照カウントがインクリメントされるシナリオについて疑問に思っていますか? 参照カウントがインクリメントされるシナリオを指摘できる人はいますか?

また、COM オブジェクトを使用してプログラミングするときは二重ドットを使用しないというブログを読みました。のようなものwrd.ActiveDocument.ActiveWindow。著者は、参照カウンターをインクリメントする値を保持するために、コンパイラーが個別の変数を作成すると主張しています。私見、これは間違っており、最初の例がこれを証明しています。あれは正しいですか?

どんな助けでも素晴らしいでしょう!

4

5 に答える 5

48

私もこの質問を調査しており、COM/.Net-Interop 中心のアプリケーションに取り組み、リーク、ハング、クラッシュと戦っています。

簡単な答え: COM オブジェクトが COM 環境から .NET に渡されるたびに。

長い答え:

  1. 各 COM オブジェクトに対して 1 つの RCW オブジェクトがあります [テスト 1] [参照 4]
  2. COM オブジェクト内からオブジェクトが要求されるたびに参照カウントがインクリメントされる (COM オブジェクトを返す COM オブジェクトのプロパティまたはメソッドを呼び出すと、返される COM オブジェクトの参照カウントが 1 つインクリメントされる) [テスト 1]
  3. オブジェクトの他の COM インターフェイスにキャストしたり、RCW 参照を移動したりしても、参照カウントがインクリメントされない [テスト 2]
  4. COM によって発生したイベントでオブジェクトがパラメーターとして渡されるたびに、参照カウントがインクリメントされます [参照 1]

補足: COM オブジェクトは、使い終わったらすぐに解放する必要がありますこの作業を GC に任せると、リーク、予期しない動作、およびイベントのデッドロックが発生する可能性があります。オブジェクトが作成された STA スレッド以外でオブジェクトにアクセスする場合、これは 10 倍重要になります。[参照 2] [参照 3] [つらい個人的な経験]

すべてのケースをカバーできたことを願っていますが、COM は難しいクッキーです。乾杯。

テスト 1 - 参照カウント

private void Test1( _Application outlookApp )
{
    var explorer1 = outlookApp.ActiveExplorer();
    var count1 = Marshal.ReleaseComObject(explorer1);
    MessageBox.Show("Count 1:" + count1);

    var explorer2 = outlookApp.ActiveExplorer();
    var explorer3 = outlookApp.ActiveExplorer();
    var explorer4 = outlookApp.ActiveExplorer();

    var equals = explorer2 == explorer3 && ReferenceEquals(explorer2, explorer4);
    var count2 = Marshal.ReleaseComObject(explorer4);
    MessageBox.Show("Count 2:" + count2 + ", Equals: " + equals);
}
Output:
Count 1: 4
Count 2: 6, Equals: True

テスト 2 - 参照カウントの続き。

private static void Test2(_Application outlookApp)
{
    var explorer1 = outlookApp.ActiveExplorer();
    var count1 = Marshal.ReleaseComObject(explorer1);
    MessageBox.Show("Count 1:" + count1);

    var explorer2 = outlookApp.ActiveExplorer();

    var explorer3 = explorer2 as _Explorer;
    var explorer4 = (ExplorerEvents_10_Event)explorer2;
    var explorerObject = (object)explorer2;
    var explorer5 = (Explorer)explorerObject;

    var equals = explorer2 == explorer3 && ReferenceEquals(explorer2, explorer5);
    var count2 = Marshal.ReleaseComObject(explorer4);
    MessageBox.Show("Count 2:" + count2 + ", Equals: " + equals);
}
Output:
Count 1: 4
Count 2: 4, Equals: True

私の経験とテストに加えて、私が中継している情報源:

1. Johannes Passing's - RCW Reference Counting Rules != COM Reference Counting Rules

2. Eran Sandler - ランタイム呼び出し可能ラッパーの内部構造とよくある落とし穴

3. Eran Sandler - Marshal.ReleaseComObject と CPU の回転

4. MSDN - ランタイム呼び出し可能ラッパー

于 2011-04-24T15:08:22.167 に答える
3

RCW のコードは見たことがありません (SSCLI の一部であるかどうかもわかりません) が、SlimDX で COM オブジェクトの有効期間を追跡するために同様のシステムを実装する必要があり、RCW についてかなりの調査を行う必要がありました。これは私が覚えていることです。うまくいけば、それはかなり正確ですが、少し塩味で受け取ってください。

システムが最初に COM インターフェイス ポインターを確認すると、キャッシュに移動して、そのインターフェイス ポインターの RCW があるかどうかを確認します。おそらく、RCW のファイナライズとコレクションを妨げないように、キャッシュは弱参照を使用していると思われます。

そのポインタのライブ ラッパーがある場合、システムはラッパーを返します。インターフェイスの参照カウントをインクリメントする方法でインターフェイスが取得された場合、おそらく RCW システムはこの時点で Release() を呼び出します。ライブ ラッパーが見つかったので、ラッパーが単一の参照であることを認識し、正確に 1 つの参照を維持する必要があります。キャッシュにライブ ラッパーがない場合は、新しいラッパーを作成して返します。

ラッパーは、ファイナライザーから基になる COM インターフェイス ポインターで Release を呼び出します。

ラッパーはユーザーと COM オブジェクトの間に位置し、すべてのパラメーターのマーシャリングを処理します。これにより、それ自体が別のインターフェイス ポインターである任意のインターフェイス メソッドの生の結果を取得し、RCW キャッシュ システムを介してそのポインターを実行して、ラップされたインターフェイス ポインターを返す前に存在するかどうかを確認することもできます。

残念ながら、RCW システムがアプリケーション ドメインまたはスレッド アパートメント間でデータを送信するためのプロキシ オブジェクトの生成をどのように処理するかについて、私はよく理解していません。これは、SlimDX 用にコピーする必要のあるシステムの側面ではありませんでした。

于 2011-01-11T16:56:14.067 に答える
1

特別な治療は必要ありません。ランタイムは、COM オブジェクトへの参照を 1 つだけ保持します。これは、GC がすべてのマネージ参照を追跡するためです。そのため、RCW がスコープから外れて収集されると、COM 参照が解放されます。管理された参照を渡すと、GC がそれを追跡します。これは、古い AddRef/Release スキームに対する GC ベースのランタイムの最大の利点の 1 つです。

より決定論的なリリースが必要でない限り、手動で Marshal.ReleaseComObject を呼び出す必要はありません。

于 2011-01-04T08:30:20.457 に答える
-2

Marshal.ReleaseComObjectアプリケーションという単語への参照を解放するには、wrd 変数を呼び出す必要があります。

そうすれば、Word が表示されていないときにアプリケーションを閉じると、ユーザーに表示されていない限り、exe もアンロードされます。

于 2011-01-04T08:24:39.497 に答える