C#からExcelを操作する必要が生じたため,その辺をWebで調べてみました.
一番基本となる情報はこれらではないでしょうか.
- Visual C# で Excel を自動化して、配列による範囲内へのデータ入力および範囲内からのデータ取得を行う方法
- Visual C# を使用した Office アプリケーションのプログラミング
- ご指定のページが見つかりません
COMといえば,"Release"なのですが,このサイトのサンプルには見あたりません.
調べてみると,やはり自分でやった方が良いようです(ガベージコレクタが回収するのはいつかわかりませんし).
- @IT:特集 .NET言語による次世代Officeソリューションの開発
- C# と VB.NET のサンプル
- C# と VB.NET のサンプル
- Excelオブジェクトの解放 − Insider.NET − @IT
- sheetのコピー − Insider.NET − @IT
- C#でエクセル操作 − Insider.NET − @IT
- シートのコピーでExcelのプロセスが残る − Insider.NET − @IT
- VB.NETでエクセルエグゼが残ってしまいます。 − VB業務アプリケーション開発研究室 − @IT
- C#, VB.NET で Excel が終了しない
いくつかの情報源で,「明示的に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年くらい前の話題のようで...
自分の鈍さに自己嫌悪...