c#でcom interopを使用する場合のRCW&参照カウント
-
15-10-2019 - |
質問
Office Interopアセンブリを使用するアプリケーションがあります。ランタイムが管理する「ランタイムコール可能なラッパー(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
. 。著者は、コンパイラが個別の変数を作成して、参照カウンターを増加させる値を保持すると主張しています。私見、これは間違っており、最初の例はこれを証明しています。あれは正しいですか?
どんな助けも素晴らしいでしょう!
解決
私もこの質問を研究しており、com/.net-interop中心のアプリケーションに取り組んでおり、漏れ、ハング、クラッシュと戦っています。
短い答え:COMオブジェクトがCOM環境から.NETに渡されるたびに。
長い答え:
- 各comオブジェクトに1つのRCWオブジェクトがあります[テスト1] [Ref 4
- 参照カウントは、comオブジェクト内からオブジェクトが要求されるたびに増加します(comオブジェクトを返すcomオブジェクトのプロパティまたはメソッドを呼び出します。返されたcomオブジェクト参照カウントは1つで増加します)[テスト1
- 参照カウントは、オブジェクトの他のcomインターフェイスにキャストするか、RCWリファレンスを移動することによって増加しません[テスト2
- 参照カウントは、com [ref 1]によって提起されたイベントでオブジェクトがパラメーターとして渡されるたびに増加します[Ref 1
サイドノート:あなたはすべきです いつも 使用が完了したらすぐにcomオブジェクトをリリースします。この作業をGCに任せると、漏れ、予期しない行動、イベントの行き詰まりにつながる可能性があります。これは、作成されたSTAスレッド上ではなくオブジェクトにアクセスする場合、10倍重要です。 [Ref 2] [Ref 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-参照カウントCont。
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リファレンスカウントルール!= comリファレンスカウントルール
2. Eran Sandler-ランタイムコール可能なラッパーの内部と一般的な落とし穴
他のヒント
RCWのコードを見たことがありません - それがSSCLIの一部であるかどうかさえわかりません - しかし、私はSLIMDXでCOMオブジェクトの寿命を追跡するために同様のシステムを実装する必要があり、RCWのかなりの研究をしなければなりませんでした。これは私が覚えていることです。うまくいけば、合理的に正確であるが、塩のタッチでそれを取ります。
システムが最初にCOMインターフェイスポインターを表示すると、キャッシュに移動して、そのインターフェイスポインターにRCWがあるかどうかを確認します。おそらく、RCWの最終化と収集を防止しないように、キャッシュは弱い参照を使用しているでしょう。
そのポインターのライブラッパーがある場合、システムはラッパーを返します - インターフェイスがインターフェイスの参照カウントを増加させるファッションで取得された場合、おそらくRCWシステムはこの時点でリリース()を呼び出します。ライブラッパーが見つかったため、ラッパーは単一の参照であり、正確に1つの参照を維持したいことがわかっています。キャッシュにライブラッパーがない場合、新しいラッパーを作成して返します。
ラッパーは、ファイナルライザーから基礎となるcomインターフェイスポインターのリリースを呼び出します。
ラッパーはあなたとcomオブジェクトの間にあり、すべてのパラメーターマーシャリングを処理します。また、これにより、別のインターフェイスポインターであるインターフェイスメソッドの生の結果を取得し、RCWキャッシュシステムを介してそのポインターを実行して、ラップインターフェイスポインターを返す前にまだ存在するかどうかを確認できます。
残念ながら、RCWシステムがアプリケーションドメインやスレッドアパートメント全体で物を送信するためにプロキシオブジェクトの生成をどのように処理しているかをよく理解していません。 Slimdxのコピーに必要なシステムの側面ではありませんでした。
特別な治療は必要ないはずです。ランタイムは、comオブジェクトへの1つの参照のみを保持します。その理由は、GCがすべての管理された参照を追跡するため、RCWが範囲外に出て収集されると、comリファレンスがリリースされるためです。管理されたリファレンスを渡すと、GCはそれを追跡しています。これは、古いAddRef/リリーススキームよりもGCベースのランタイムの最大の利点の1つです。
より決定的なリリースを必要としない限り、Marshal.releaseComobjectに手動で電話をかける必要はありません。
受け入れられたソリューション 有効ですが、ここにいくつかの追加の背景情報があります。
RCWには、COMオブジェクトの内部的に1つ以上のネイティブCOMオブジェクトインターフェイス参照が含まれています。
RCWが、ガベージを収集したため、または Marshal.ReleaseComObject()
それを呼び出すと、内部に保持されているすべてのCOMオブジェクトインターフェイスをすべてリリースします。
ここには実際に多くの参照カウントがあります - .NETのRCWがその基礎となるCOMオブジェクトインターフェイスをいつリリースするかを決定するものがあり、それらのRAW COMインターフェイスのそれぞれは、通常のCOMのように独自の参照カウントを持っています。
RAW COMを取得するコードは次のとおりです IUnknown
インターフェイスリファレンスカウント:
int getIUnknownReferenceCount(object comobject)
{
var iUnknown = Marshal.GetIUnknownForObject(comObject);
return Marshal.Release(iUnknown);
}
そして、あなたはオブジェクトの他のcomインターフェイスについても同じものを取得できます Marshal.GetComInterfaceForObject()
.
にリストされている方法に加えて 受け入れられたソリューション, 、.NET RCWリファレンスカウントを人為的に、次のように呼び出すことで増やすこともできます Marshal.GetObjectForIUnknown()
.
その手法を使用して、特定のCOMオブジェクトのRCWリファレンスカウントを取得する例は次のとおりです。
int comObjectReferenceCount(object comObject)
{
var iUnknown = Marshal.GetIUnknownForObject(comObject);
Marshal.GetObjectForIUnknown(iUnknown);
Marshal.Release(iUnknown);
return Marshal.ReleaseComObject(comObject);
}
あなたは電話する必要があります Marshal.ReleaseComObject
WRD変数で、Wordアプリケーションへの参照をリリースします。
このようにして、単語が表示されず、アプリケーションを閉じた場合、ユーザーに表示されない限り、EXEも降ろされます。