こんにちは、Office 開発系サポート 森 健吾 (kenmori) です。
今回の投稿では、Office オートメーションの実装コードで割り当てたオブジェクトを解放するというテーマにて記載いたします。.NET Framework 上で動作するカスタム アプリケーションにおいて、Office オートメーションで処理を実装する場合には割り当てたオブジェクトを確実に解放することをお勧めします。
これは、Office が内部的に OLE や DDE などを通じて実施するオブジェクト インスタンス制御 (※) によって CLR 上で解放漏れ (または解放待ち) のオブジェクトが誤って参照されてしまい、カスタム アプリケーション側の動作に様々な予期せぬ影響を与えることにあります。
※ OLE や DDE などを通じて実施するオブジェクト インスタンス制御や、解放漏れによる影響等については、別途記載を予定しております。
今回は、詳細な上記の詳細な理由や解放しない際の影響等は省略し、オブジェクト解放の正しい実装方法について記載します。
1. 割り当てたオブジェクトを解放する
Office オートメーションで割り当てたオブジェクトについては、自分で解放処理を記載して必ず破棄される必要があります。
例えば、以下の Windows アプリケーションにおけるサンプル コードでは VB.NET で Dim 宣言を使用して Application, Workbook, Worksheet オブジェクトを割り当てております。割り当てたオブジェクトは Marshal.ReleaseComObject メソッドを使用して必ず解放するようにしております。
また、Application インスタンスを解放する際なのですが、Marshal.ReleaseComObject メソッドを使用して参照カウンタをデクリメントしただけでは、プロセスが終了することを保証できませんので、GC.Collect メソッドでガベージコレクトを強制してオブジェクトを解放しています。
注意 : なお、サンプル コードの簡素化のため、敢えて例外処理等は記載しておりません。
VB.NET
Imports Excel = Microsoft.Office.Interop.ExcelImports System.Runtime.InteropServices
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim app As Excel.Application = New Excel.Application() Dim workbook As Excel.Workbook = app.Workbooks.Add() Dim worksheet As Excel.Worksheet = workbook.Sheets(1)
worksheet.Range("A1").Value = TextBox1.Text workbook.SaveAs("C:\Out\a.xlsx") workbook.Close() app.Quit()
' オブジェクトを破棄します。
Marshal.ReleaseComObject(worksheet) worksheet = Nothing
Marshal.ReleaseComObject(workbook) workbook = Nothing
Marshal.ReleaseComObject(app) app = Nothing
' ガベージ コレクトを強制します。
GC.Collect() GC.WaitForPendingFinalizers() GC.Collect()
End Sub
End Class
なお、ガベージ コレクトの処理は若干冗長に見えますが、上記がベスト プラクティスになります。詳細は以下のサイトをご確認ください。
タイトル : 第 5 章 「マネージ コード パフォーマンスの向上」アドレス : http://msdn.microsoft.com/ja-jp/library/ms998547.aspx参考箇所 : GC.Collect の呼び出しを避ける
以下に C# の場合のコーディングも併記します。
C#
using Excel = Microsoft.Office.Interop.Excel;using System.Runtime.InteropServices;
namespace winExcelSample{ public partial class Form1 : Form {
public Form1() { InitializeComponent(); }
private void button1_Click(object sender, EventArgs e) {
Excel.Application app = new Excel.Application(); Excel.Workbook workbook = app.Workbooks.Add(Type.Missing); Excel.Worksheet worksheet = (Excel.Worksheet)workbook.Sheets[1];
worksheet.get_Range("A1", Type.Missing).set_Value(Type.Missing, textBox1.Text); workbook.SaveAs(@"C:\Out\a.xlsx", Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Excel.XlSaveAsAccessMode.xlExclusive, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); workbook.Close(Type.Missing, Type.Missing, Type.Missing); app.Quit();
// オブジェクトを破棄します。
Marshal.ReleaseComObject(worksheet); worksheet = null;
Marshal.ReleaseComObject(workbook); workbook = null;
Marshal.ReleaseComObject(app); app = null;
// ガベージコレクトを強制します。
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
} }}
なお、コンソール アプリケーション等では、処理が一通り実行された後、プロセスが終了する際に CLR ランタイムが終了するのに合わせて、Application オブジェクトの解放および EXCEL.EXE プロセスの終了が実施されます。そのため、解放漏れがあったとしても、プログラム終了時に解放されるため、ほとんどの場合大きな影響がない傾向があります。
2. イベントで取得したオブジェクトを解放する
Office オートメーションでイベント ハンドラを追加した場合においても、引数として取得されるオブジェクトは解放する必要があります。上述の例と同様に Marshal.ReleaseComObject メソッドで解放を実施しております。
Public Sub Workbook_Open(ByVal Wb As Excel.Workbook)
Try ' 処理 Finally
Marshal.ReleaseComObject(Wb) Wb = Nothing
End Try
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim app As Excel.Application = New Excel.Application() AddHandler app.WorkbookOpen, AddressOf Workbook_Open
' 処理
public void app_WorkbookOpen(Excel.Workbook Wb){
try { // 処理 } finally {
// オブジェクトを破棄します。 Marshal.ReleaseComObject(Wb); Wb = null; }}
private void button2_Click(object sender, EventArgs e){
Excel.Application app = new Excel.Application(); app.WorkbookOpen += new Microsoft.Office.Interop.Excel.AppEvents_WorkbookOpenEventHandler(app_WorkbookOpen);
// 処理
}
以上となります。
一般的には、上記の内容をお約束事と覚えていただいて実装を検討することで問題ないと思われます。
なお、ガベージ コレクトの強制など、一般的な開発では推奨されないとされる実装に踏み切るのは極めて大きな抵抗があるという開発者様のために、次回の投稿 (Office オートメーションで割り当てたオブジェクトを解放する - Part2) ではオブジェクト解放漏れが即時解放されない場合に具体的にどのように影響するかについて例を挙げて記載し、いわゆる「特別な事情がある場合」に該当することをご説明します。