メソッドに貼り付けるだけで機能するソースコードを多数用意しています。
Excel操作クラス
クラスを作成する
このページでは、メソッドを登録するためのクラスを作成します。
クラス名はExcelManagerとなってますので、お好きな名前に変えて使用してください。
using Microsoft.Office.Interop.Excel; class ExcelManager : IDisposable { // Excel操作用オブジェクト private Application xlApp = null; private Workbooks xlBooks = null; private Workbook xlBook = null; private Sheets xlSheets = null; private Worksheet xlSheet = null; // ------------------------------------------------------ // コンストラクタ/デストラクタ・プロパティ・メソッド // ------------------------------------------------------ }
このクラスは、Excelファイルの1シートに対して様々な操作ができるように定義するクラスですので、クラス全体が参照できる変数としてExcelアプリケーション~WorkSheetまでを定義します。
また、VBやC#でコーディングをする際、私はUsing~を多用するので、最後にリソースの解放処理(Dispose)が必ず実行されるように、IDsiposableを実装しています。
COMオブジェクトの解放について、もうすこし詳しく知りたい方は「「「こちら」」」のページをご覧ください。
また、面倒なCOMオブジェクトの解放処理を簡略化するためのメソッドも「「「こちら」」」で公開していますので、よかったらあわせてご覧ください。
コンストラクタとデストラクタ
「クラスを作成する」のページにも記載しましたが、クラスを操作する際にUsingの使用を可能にするため、作成するクラスにはコンストラクタとDisposeを実装します。
コンストラクタとデストラクタの概要についてはこちらに記述しました。使い分け方や各メソッドの意味など確認したい方は一読ください。
#region "コンストラクタとデストラクタ" /// <summary> /// コンストラクタ /// </summary> public ExcelManager() { xlApp = new Application(); } /// <summary> /// 解放 /// </summary> public void Dispose() { // ExcelManagerが使われなくなったときに破棄する資源 ReleaseExcelComObject(EnumReleaseMode.App); // dispose判定フラグON isDispose = true; // Dispose()が明示的に呼ばれたときは、GCからFinalize()呼び出しをさせない。 GC.SuppressFinalize(this); } /// <summary> /// デストラクタ /// </summary> ~ExcelManager() { // 呼出し側がDispose()してなかったらここでDispose if (isDispose == false) { Dispose(); } } #endregion
■ コンストラクタ(public [クラス名]())
public [クラス名]() { 処理 } はコンストラクタといい、ほかのクラスでNew()を宣言したときに呼ばれる処理です。
コンストラクタの処理では、クラス変数で宣言したxlAppのインスタンスを生成します。
■ デストラクタ(~[クラス名]())
~[クラス名]() { 処理 } は、コンストラクタとは対象に、クラスが破棄されるときに実行される処理です。しかし、C#ではガベージコレクタが機能しているため、ほとんどのクラスで実装する必要はありません。
明示的に解放が必要なリソースや、.NET Frameworkで管理されていないアンマネージリソースを使用するときに実装することがあります。
■ Dispose
public void Dispose() { 処理 } は、クラスのインスタンスが破棄されるときに呼ばれる処理です。
デストラクタと混同してしまいがちですが、自動的に呼ばれる処理を記述するデストラクタに対して、Disposeは明示的に呼ぶ必要のある処理を記述します。
デストラクタはガベージコレクタが実行されたタイミングで呼ばれる処理のため実行されるタイミングが不明です。
通常はDisposeを実装して、ソースコード上では都度解放するようにしたほうが良いでしょう。
クラスの準備は以上で完了です。
あとはプロパティ・メソッドカテゴリーから、必要な処理を移植すれば、Excelの操作が可能になります。
COMプロセスが残る現象を防ぐためのクラス作成
このページで紹介するのは、単純に1つのExcelファイルを編集するときに、私がいつも使っているクラスの構成ルールです。
以下のルールに従って作成すれば、「COMの解放ができずにタスクマネージャにプロセスが残ってしまう。」という現象を軽減させることができるはずです。
1.クラスの構成
まず、Excelファイルの操作クラスを作成するときは、「クラスの作成」ページで公開しているように、クラス変数でWorksheetオブジェクトまでを宣言します。
2.メソッド・プロパティのルール
メソッドやプロパティを定義し、必要に応じてRangeやCellsを作って処理を作成します。
基本的にメソッド内で定義するオブジェクトはSheet以下のCellsやRangeのみとし、同じメソッド内で確実に解放します。
3.COM解放メソッド
これは私の中でいつも使っている手法ですので、上の2つとは少し違いますが・・・。
私がExcel操作クラスを作成するときに、いつも作成しているCOMオブジェクト解放用のメソッドです。
「2.メソッド・プロパティのルール」に書いた通り、メソッドやプロパティ内はCellsやRangeをメインで操作しますが、
シート切り替えやファイルを閉じるときなどの処理では、WorkSheetより上位のオブジェクトを操作することがあります。
例えばWorkBookを閉じる処理であれば、
WorkSheet → Sheets → WorkBook → WorkBooks
の順でオブジェクトを解放し、WorkBookを開くときは逆の順番でインスタンスを生成してあげる必要があります。
そこで私は、以下のようなEnumとメソッドを用意して、解放処理を1行で済ませられるように作成しています。
<Enum>
クラス変数で定義したオブジェクトに対応させて宣言しておきます。
/// <summary> /// リリース対象 /// </summary> private enum EnumReleaseMode { Sheet, Sheets, Book, Books, App }
<メソッド>
定義したEnumを引数に設定し、呼び出し側では「どこまで解放すればいいか」を指定できるようにしておきます。
/// <summary> /// Excelリソース解放 /// </summary> /// <param name="ReleaseMode">リリース対象Enum</param> private void ReleaseExcelComObject(EnumReleaseMode ReleaseMode) { try { // xlSheet解放 if (xlSheet != null) { Marshal.ReleaseComObject(xlSheet); xlSheet = null; } if (ReleaseMode == EnumReleaseMode.Sheet) return; // xlSheets解放 if (xlSheets != null) { Marshal.ReleaseComObject(xlSheets); xlSheets = null; } if (ReleaseMode == EnumReleaseMode.Sheets) return; // xlBook解放 if (xlBook != null) { try { xlBook.Close(); } finally { Marshal.ReleaseComObject(xlBook); xlBook = null; } } if (ReleaseMode == EnumReleaseMode.Book) return; // xlBooks解放 if (xlBooks != null) { Marshal.ReleaseComObject(xlBooks); xlBooks = null; } if (ReleaseMode == EnumReleaseMode.Books) return; // xlApp解放 if (xlApp != null) { try { // アラートを戻して終了 xlApp.DisplayAlerts = true; xlApp.Quit(); } finally { Marshal.ReleaseComObject(xlApp); } } } catch (Exception) { throw; } }
デストラクタとコンストラクタの使い方
このページでは、コンストラクタとデストラクタ、そしてデストラクタと混同してしまいがちなDisposeについて説明します。
● Dispose
まず初めに、Disposeとデストラクタは違うものです。よく混同して説明が書かれたページを目にするので、ここで説明を記載しておきます。
Disposeは、解放するクラスの中で使用していたリソースの破棄をするために実行する処理です。
これだけ書くとデストラクタと同じような処理内容ですが、明確な違いは、Disposeは明示的に呼び出さないと処理をしてくれないということです。
このDisposeを用意しないといけない場面は、クラス内で使用するリソースに.NET Frameworkの管理下にないもの(COMオブジェクトなどのアンマネージドリソース)が含まれている場合です。
このサイトでいうとExcelオブジェクトがそれに該当するため、ExcelオブジェクトはかならずDisposeで解放してあげる必要があるのです。これを忘れると「exeを終了したのに、exeから呼び出していたExcelアプリケーションがタスクマネージャに残ってる!」なんてことがよく起こります。
<宣言方法>
/// <summary> /// 解放 /// </summary> public void Dispose() { // ExcelManagerが使われなくなったときに破棄する資源 ReleaseExcelComObject(EnumReleaseMode.App); // dispose判定フラグON isDispose = true; // Dispose()が明示的に呼ばれたときは、GCからFinalize()呼び出しをさせない。 GC.SuppressFinalize(this); }
● コンストラクタ
動的クラスのインスタンスが生成されるときに実行される処理で、『public [クラス名]()』で宣言されます。
コンストラクタについてはお馴染みの処理なので、ここでは省略します。
<宣言方法>
/// <summary> /// コンストラクタ /// </summary> public ExcelManager() { xlApp = new Application(); }
● デストラクタ
コンストラクタと正反対の処理をします。クラスのインスタンスが破棄されると実行される処理で、記述方法は『~ [クラス名]()』です。
Disposeとの違いは、Disposeは明示的に呼び出したタイミングで実行する処理なのに対し、デストラクタはガベージコレクタが回収に来たタイミングで実行される処理です。
使い分けは、Finalizeは.NET Frameworkの管理下にあるリソースの解放処理を実装し、Disposeは.NET Frameworkの管理下に無いリソースの解放処理をすることが多いです。
<宣言方法>
/// <summary> /// デストラクタ /// </summary> ~ExcelManager() { // マネージドリソースの解放処理 }