複数のExcelブックのシートをマージする。
複数のブック上のシートを1つのファイルにマージさせる方法が探しても意外と見つからずとりあえず作ってみた。
VBAであればネットに少しあるみたい。
動作環境
Windows7
Excel 2007/2010(必須)
エクセルのAPIはCOM
C#でのCOM参照は使いづらい。本来、.NetFrameworkで使用されるのが前提になっていないからなのか、APIの情報が自動ででてこない。要は式がDynamicになってしまう。
例えば、全体をtry-catchで囲っているのに、APIがdynamic型なので、次の場合、個別に対応が必要になる。
try
{
int TabColor = 0; // int型
try
{
TabColor = ws.Tab.Color; // エクセルAPIは有効な場合は数値、無効値は"False"が入る
}
catch
{
TabColor = 0; // 無効値("False")がintに代入しようとしたため例外が発生する。その場合、0とする。
}
// 処理
}
catch
{
// 処理
}
コツは一つのApplicationインスタンス内で全て行う。
当初、ファイル単位でApplication(Microsoft.Office.Interop.Excel.Applicationクラス)を起こして、Workbook型のインスタンスを作って、Worksheetをcopyしていたが、うまくいかず、、、。エクセルは一つのアプリケーションが起動していると、そのエクセルで次のファイルを起動する、という仕様になっていて、このエクセル内で開いているファイルならコピーができる。ということは、ひとつのApplicationインスタンス内でやってみればうまくいくんじゃないかなぁって思って、やってみたらうまくいった。
エクセルAPIを使うときの注意点。
・シートやセルのはじめのインデックスは”1″、例えば次のソースは誤り。
// A1のセルを参照
var val = ws.Cells[0,0].Value; // [1,1]が正解
・エクセルがバックグランドで動作するので、APIを呼ぶ側のアプリケーションが異常終了するとプロセスが残る。
デバッグとかで途中で止めたり、異常終了したまま放置すると、EXCELという名前のゾンビプロセスが増殖する。
それを防ぐために、実行時に注意を促す。以下例。
if (Process.GetProcesses().Any(p => p.ProcessName.IndexOf("EXCEL") != -1))
{
MessageBox.Show("エクセルを閉じてください!!");
return;
}
動作画面
今回作成したソースはこちら。