C#でExcelを操作する

C#からExcelを操作する必要が生じたため,その辺をWebで調べてみました.
一番基本となる情報はこれらではないでしょうか.

COMといえば,"Release"なのですが,このサイトのサンプルには見あたりません.
調べてみると,やはり自分でやった方が良いようです(ガベージコレクタが回収するのはいつかわかりませんし).

いくつかの情報源で,「明示的にCOMオブジェクトを解放しないと,Excelプロセスが残る」とあったのですが,私の環境では,明示的に解放しなくてもプロセスは消滅していました.
(OS: WindowsXP Professional sp2, Visual Studio 2005)
だからといって,明示的に解放しなくても良い,ということにはならないと思いますが...(調査してみないと)


総合すると,

  • COMのMicrosoft Excel 11.0 Object Libraryを使用する
  • 省略可能なパラメータには,System.Reflection.Missing.ValueかType.Missingを使用する
  • COMなので参照カウントに注意する

ということです.Microsoftのサイトにあるサンプルそのままではいけないということか.
しかし,先に挙げたサイトのサンプルを見るとわかるのですが,try-finallyの深いネストができあがってしまいます(仕方がないのですが...).


とりあえず応急処置として,ComRefなるものを作成し,ほんの少しだけネストを解消してみました.

public sealed class ComRef<T> : IDisposable where T : class
{
    private T _obj;

    public T Obj
    {
        get { return this._obj; }
    }

    public ComRef(T obj)
    {
        this._obj = obj;
    }

    ~ComRef()
    {
        Dispose(false);
    }

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

    private void Dispose(bool disposing)
    {
        if (disposing)
        {
            // managed objects
        }

        // unmanaged objects
        if (this._obj != null)
        {
            if (System.Runtime.InteropServices.Marshal.IsComObject(this._obj))
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(this._obj);
            }
            this._obj = null;
        }
    }
}


じゃんぬ様のサイトより,
http://jeanne.wankuma.com/tips/programing/releasecom.html
このサンプルコードを,ComRefを使って書き直してみると,以下のようになります.

ComRef<Excel.Application> xlApplication = null;

using (xlApplication = new ComRef<Excel.Application>(new Excel.Application()))
{
    xlApplication.Obj.DisplayAlerts = false;

    using (ComRef<Excel.Workbooks> xlBooks = new ComRef<Excel.Workbooks>(xlApplication.Obj.Workbooks))
    using (ComRef<Excel.Workbook> xlBook = new ComRef<Excel.Workbook>(xlBooks.Obj.Add(string.Empty)))
    {
        using (ComRef<Excel.Sheets> xlSheets = new ComRef<Excel.Sheets>(xlBook.Obj.Worksheets))
        using (ComRef<Excel.Worksheet> xlSheet = new ComRef<Excel.Worksheet>( (Excel.Worksheet)xlSheets.Obj[1]) )
        using (ComRef<Excel.Range> xlCells = new ComRef<Excel.Range>(xlSheet.Obj.Cells))
        using (ComRef<Excel.Range> xlRange = new ComRef<Excel.Range>( (Excel.Range)xlCells.Obj[6, 4]) )
        {
            xlApplication.Obj.Visible = true;

            System.Threading.Thread.Sleep(1000);

            xlRange.Obj.Value2 = "あと 1 秒で終了します";

            System.Threading.Thread.Sleep(1000);
        }

        xlBook.Obj.Close(Missing.Value, Missing.Value, Missing.Value);
    }

    xlApplication.Obj.Quit();
}

うーん,".Obj"が嫌だなぁ...


しかし今回の内容,2年くらい前の話題のようで...
自分の鈍さに自己嫌悪...