Excelの遅延バインディングで「DisconnectedContext 検出」エラー

<私の別ブログ 2014年01月19日 からの引っ越し記事です>

訳あってC#でExcelの遅延バインディングを調査中、こんなエラーに遭遇した。

DisconnectedContext が検出されました。
この RuntimeCallableWrapper の COM コンテキスト 0x4474b0 への変換に失敗して、エラー 要求されたオブジェクトが存在しません。 (HRESULT からの例外: 0x80010114) が発生しました。これは、通常この RuntimeCallableWrapper が作成された COM コンテキスト 0x4474b0 が切断されたか、他の処理を実行していてビジーのためコンテキスト変換を処理できないことが原因です。COM コンポーネント上の要求のサービスに使用されるプロキシは存在しないため、呼び出しは COM コンポーネントに対して直接行われます。これは、破損またはデータの損失を発生させる可能性があります。この問題を回避するには、その内部に存在する COM コンポーネントを示す RuntimeCallableWrappers の使用が完了するまで、すべての COM コンテキスト、アパートメント、およびスレッドが完全に有効であり、コンテキスト変換に使用できることを確認してください。

ソースファイルは以下の通り。
エラーはワークブック解放時に発生する。

// using System.Runtime.InteropServices; が必要
        private void button1_Click(object sender, EventArgs e)
        {
            dynamic xlApp;
            dynamic xlWorkbooks;
            dynamic xlWorbook;

            // App生成
            Type type = Type.GetTypeFromProgID("Excel.Application");
            xlApp = Activator.CreateInstance(type);

            // Workbooks取得
            xlWorkbooks = xlApp.Workbooks;

            // Workbook取得
            xlWorbook = xlWorkbooks.Add();

            // Excel表示状態で3秒待ち(本来の処理の代わり)
            xlApp.Visible = true;
            System.Threading.Thread.Sleep(3000);

            // ワークブックを閉じて解放
            xlWorbook.Close();
            xlApp.Quit();

            // COMオブジェクト解放
            Marshal.ReleaseComObject(xlWorbook);  // ERROR
            Marshal.ReleaseComObject(xlWorkbooks);
            Marshal.ReleaseComObject(xlApp);
        }

上記と同じ処理を事前バインディングで行った場合は、問題無くオブジェクトは解放され、Excel.exeのプロセスも残らなかったので、処理順等には問題はないと考えている。

このため、遅延バインディング特有の問題かと思い色々調べたが原因は見つからず。しかし、以下のように、解放するオブジェクトを明示的にobject型にキャストすれば問題を回避でき、Excel.exeのプロセスも残らないことが分かった。

 // using System.Runtime.InteropServices; が必要
        private void button1_Click(object sender, EventArgs e)
        {
            dynamic xlApp;
            dynamic xlWorkbooks;
            dynamic xlWorbook;

            // App生成
            Type type = Type.GetTypeFromProgID("Excel.Application");
            xlApp = Activator.CreateInstance(type);

            // Workbooks取得
            xlWorkbooks = xlApp.Workbooks;

            // Workbook取得
            xlWorbook = xlWorkbooks.Add();

            // Excel表示状態で3秒待ち(本来の処理の代わり)
            xlApp.Visible = true;
            System.Threading.Thread.Sleep(3000);

            // ワークブックを閉じて解放
            xlWorbook.Close();
            xlApp.Quit();

            // COMオブジェクト解放
            Marshal.ReleaseComObject((object)xlWorbook);  // OK
            Marshal.ReleaseComObject(xlWorkbooks);
            Marshal.ReleaseComObject(xlApp);
        }

とりあえず現状はこれで良しとしよう。

■備考
Marshal.ReleaseComObject(xlWorbook); 
上記処理を、自作したメソッドの呼び出しに置き換えてみたら、同じエラーが発生した。
 例 : MyReleaseComObject(xlWorbook); 
このとき、エラーはMyReleaseComObject()の入り口(呼び出し元)で発生しており、メソッド内の処理は実行されなかった。

このため、実際にCOMオブジェクトを解放する際にエラーが発生するわけではなく、このオブジェクトをメソッドの引数に設定する際、内部で行われる何らかの処理でエラーとなっていると思われる。もしかしたら、このソースのコンパイル結果のリストファイルを見れば手がかりがあるかもしれない。

追記
結論出ました。私は解放の処理は object 型にキャストします。
コンパイル結果を確認してみた結果

タイトルとURLをコピーしました