2007年12月28日金曜日

基になる RCW から分割された COM オブジェクトを使うことはできません.

 と言う、非常に分かりにくいメッセージに出くわす事がたまにありますが、未だにこの辺の仕組みが理解出来ていません orz

 このメッセージ、ExcelやWordなんかのCOMオブジェクトを使っていて、書き方を間違えると発生するものですが、以下の様な書き方をしなければ、なかなかお目にかかれません.
 いや、お目にかかりたくは無いんですけどねw

#region " USING "
using System;
using System.Windows.Forms;
using Microsoft.Office.Interop.Excel;
#endregion


namespace Com
{
    /// <summary>
    /// CExcel の概要の説明です。
    /// </summary>
    public class CExcel
    {
        /// <summary>
        /// 引数無しコンストラクタ
        /// </summary>
        public CExcel()
        {
        }

        /// <summary>
        /// デストラクタ
        /// </summary>
        ~CExcel()
        {
        }

        /// <summary>
        /// エラーを発生させてみるテスト.
        /// </summary>
        public void test()
        {
            // Local variable.
            ApplicationClass objExcel = new ApplicationClass();
            Worksheet objWorksheet = new Worksheet();

            // Workbook 追加.
            objExcel.Workbooks.Add(Type.Missing);

            // 1ページ目の Worksheet を取得(非常識な取得の仕方ですが)
            objWorksheet = (Worksheet)objExcel.Workbooks.get_Item(1)
                .Worksheets.get_Item(1);

            // Worksheet の名前を表示してみる.
            MessageBox.Show(objWorksheet.Name); // Sheet1

            /**
             * 本来必要は無いが、Worksheet を COM オブジェクトとしてを解放し
             * てみる.
             * 更に解放した Worksheet の Delete メソッドを呼び出してみる.
             * この時点では何故かエラーが出ない.
             * そして、Worksheet の名前を参照しようとすると
             *
             *  基になる RCW から分割された COM オブジェクトを使うことはできません
             *
             * と言う、非常に分かりにくいエラーメッセージが発生 orz
             * しかも、COM オブジェクト解放のみを実行した場合は、
             *
             *  オブジェクト参照がオブジェクト インスタンスに設定されていません。
             *
             * と言う見慣れた「ああ、インスタンス化してねーじゃん」と言うメッ
             * セージになり、Delete メソッドだけ実行した場合は、
             *
             *  HRESULT からの例外です : 0x800401A8。
             *
             * と言うエラーが参照段階で発生します.
             */
            this.releaseComObject(objWorksheet);
            objWorksheet.Delete();
            MessageBox.Show(objWorksheet.Name); // ここで例のエラーが発生.

            // Excel 終了.
            objExcel.Quit();

            /**
             * ここの COM オブジェクトリリースは必要.
             * 解放しない場合、プログラムが終了するまで Excel プロセスが継続.
             */
            this.releaseComObject(objExcel);
        }

        /// <summary>
        /// すべてのCOMオブジェクトが解放されるまで、繰り返し ReleaseComObject を
        /// 実行します。
        /// </summary>
        /// <param name="objInput">COMオブジェクト</param>
        public void releaseComObject(Object objInput)
        {
            while (System.Runtime.InteropServices.Marshal.
                ReleaseComObject(objInput) > 0);
            objInput = null;
        }

    } // CExcel
} // namespace
 ※見た通り、全く意味の無い適当な書き方です.
 ※参照追加でExcelのObjectLibraryを追加しないと動きません(念のため).

 参考に「相互運用機能アセンブリを使った複雑な COM オブジェクトの処理 - Office のメニュー ボタンが機能停止した場合の対策」と言うページも見たのですが、未だにピンと来ません.
 …私のレベルでは理解不能な話の様です orz

 嗚呼低次元低次元...

0 件のコメント: