C#からExcelを操作するライブラリ
はじめに。
C#からPIAでExcelCOM使うとリークしてしまうのが難点。
ExcelCOMに1枚クラスを被せることで
使いにくいExcelCOMの存在をできるだけ隠蔽することを試みます。
- クラスを被せることのメリット
- COMの解放(Marshal.ReleaseComObject)のことを考えなくてよくなる。
- わずらわしいリークに悩まされることが激減する。
- COMの戻り値を適切な型にキャストしたりしなくてよい。
- キャストミスによるトラブルに悩まされることが激減する。
- インテリセンス(入力支援)が効くので作業効率アップ。
- COMの解放(Marshal.ReleaseComObject)のことを考えなくてよくなる。
- クラスを被せることのデメリット
- ExcelのCOMオブジェクト、メソッド、プロパティを1つずつラッピングする必要がある。
- 膨大なので全てをラッピングするのは不可能なので、よく使いそうなものだけ対応。
- ExcelのCOMオブジェクト、メソッド、プロパティを1つずつラッピングする必要がある。
ライブラリ仕様
- 開発環境
- .NET Framework 1.1 以上、C#
- Excel2000 or ExcelXP(2002) PIA
- 特徴
- Marshal.ReleaseComObject はライブラリ側で自動で行う。
- Excel2000、ExcelXP(2002) PIA 対応。(#defineで指定)
- その他
- Excel2003、2007 PIA でも使えるかもしれません。
- PIAは.NETとCOMのI/Fなので、開発をExcel2000PIAで行っていれば、(COMレベルの互換性によりますが)ExcelXP以上でも動くはず。
Visual C# Express 2008 で作ったもの一式はこちら。
http://goungoun.dip.jp/app/upload/libSimpleExcel/libSimpleExcel20090210.zip
対応してるExcelオブジェクト、メソッド、プロパティ一覧
対応しているのは次のクラス(Excelオブジェクト)、メソッド、プロパティ
のみです。足りないものは既存のものを参考に追加してください。
- class Application
- bool Visible
- bool DisplayAlerts
- Workbooks Workbooks
- void Quit()
- class Workbooks
- Workbook Open(string filename_,bool read_only_,Excel.XlFileFormat format_,string delimiter_)
- Workbook Open(string filename_, bool read_only_)
- Workbook Add()
- int Count
- Workbook this[int idx_]
- Workbook this[string name_]
- class Workbook
- Sheets Sheets
- class Sheets
- int Count
- Worksheet this[int idx_]
- Worksheet this[string name_]
- class Worksheet
- string Name
- void Delete()
- void CopyAfter(Worksheet aft_)
- void CopyBefore(Worksheet bef_)
- Range Cells
- Range Range
- Range Rows(int row_)
- Range Columns(int col_)
- class Range
- class Font
- int ColorIndex
- bool Bold
- class Interior
- int ColorIndex
- Excel.XlPattern Pattern
PIAの入手方法
Office2000なら、Interop.Excel.dll
OfficeXPなら、Microsoft.Office.Interop.Excel.dll
入手先は、
Office2000なら。
プロジェクト→参照の追加→COMタブ→
Microsoft Excel 9.0 Object Livraly 1.3
X:\Program Files\Microsoft Office\Office\EXCEL9.OLB
Office XP PIAなら。
Microsoft Office XP 用の PIA (Primary Interop Assemblies) のダウンロード
http://support.microsoft.com/kb/328912/ja
ソース
lib.SimpleExcel.cs(ライブラリ側)
使用するPIAの種類に応じて先頭のdefineを切り替える。
//#define PIA_OFFICE_2000 #define PIA_OFFICE_XP using System; using System.Collections; using System.Runtime.InteropServices; #if PIA_OFFICE_XP using Excel = Microsoft.Office.Interop.Excel; #endif namespace lib.SimpleExcel { /// <summary> /// Excel関連のオブジェクトを分類する為の独自クラス /// </summary> public class Object : Base { private string _type_name = ""; public Object(Base parent_, object comobj_) : base(parent_, comobj_) { if (comobj_ == null) { this._type_name = "null"; } else if (Marshal.IsComObject(comobj_)) { if ((comobj_ as Excel.Range) != null) { this._type_name = "Excel.Range"; } else if ((comobj_ as Excel.Range) != null) { this._type_name = "Excel.Range"; } } if (this._type_name == "") { Type t = comobj_.GetType(); this._type_name = t.FullName; } } public string TypeName { get { return this._type_name; } } } /// <summary> /// Base /// </summary> public abstract class Base : IDisposable { private static int _com_object_counter = 0; private Base _parent = null; private ArrayList _childs = null; protected object _comobj = null; /// <summary> /// 現在使用中の Excel COM の数 /// 0 でない場合、Excelプロセス が開放されていないことになるので、 /// 本ライブラリでExcel操作後は、必ず0になっている*はず* /// </summary> public static int ComObjectCounter { get { return Base._com_object_counter; } } /// <summary> /// Excelのプロセスを取得 /// IDとウィンドウタイトルから素性を判断できる。 /// </summary> /// <param name="msg_"></param> /// <returns></returns> public static System.Diagnostics.Process[] GetExcelProcess(ref string msg_) { System.Diagnostics.Process[] ps = System.Diagnostics.Process.GetProcessesByName("Excel"); msg_ = ""; foreach (System.Diagnostics.Process p in ps) { msg_ += string.Format("Id:{0} Title:{1} \r\n", p.Id, p.MainWindowTitle); } return ps; } public Base(Base parent_, object comobj_) { // if (parent_ == null) // { // System.Windows.Forms.MessageBox.Show("null"); // } this._parent = parent_; if (parent_ != null) { parent_.AddChild(this); } this._childs = new ArrayList(); this._comobj = comobj_; if (Marshal.IsComObject(this._comobj)) { Base._com_object_counter++; } } public object ComObject { get { return this._comobj; } } protected void AddChild(Base obj_) { this._childs.Add(obj_); } public void Dispose() { // 子を破棄 if (this._childs != null) { for (int idx = 0; idx < this._childs.Count; idx++) { Base obj = (Base)this._childs[idx]; obj.Dispose(); } this._childs = null; } // 自分を破棄 if (this._comobj != null && Marshal.IsComObject(this._comobj)) { Marshal.ReleaseComObject(this._comobj); Base._com_object_counter--; } this._comobj = null; // 親情報を削除 this._parent = null; GC.Collect(); // 一応と思っていたら、これがないとExcelで一般保護違反がでることがあった。 } } /// <summary> /// Excel.Application /// </summary> public class Application : Base { public Application() : base(null, new Excel.Application()) { } private Excel.Application _application { get { return (Excel.Application)this._comobj; } } public bool Visible { get { return this._application.Visible; } set { this._application.Visible = value; } } public bool DisplayAlerts { get { return this._application.DisplayAlerts; } set { this._application.DisplayAlerts = value; } } public Workbooks Workbooks { get { return new Workbooks(this, this._application.Workbooks); } } public void Quit() { this._application.Quit(); } } /// <summary> /// Excel.Workbooks /// </summary> public class Workbooks : Base { public Workbooks(Base parent_, object comobj_) : base(parent_, comobj_) { } private Excel.Workbooks _workbooks { get { return (Excel.Workbooks)this._comobj; } } private object _workbooks_open(string Filename, object UpdateLinks, object ReadOnly, object Format, object Password, object WriteResPassword, object IgnoreReadOnlyRecommended, object Origin, object Delimiter, object Editable, object Notify, object Converter, object AddToMru, object Local, object CorruptLoad) { #if PIA_OFFICE_2000 object ret = this._workbooks.Open(Filename, UpdateLinks, ReadOnly, Format, Password, WriteResPassword, IgnoreReadOnlyRecommended, Origin, Delimiter, Editable, Notify, Converter, AddToMru); #endif #if PIA_OFFICE_XP object ret = this._workbooks.Open(Filename, UpdateLinks, ReadOnly, Format, Password, WriteResPassword, IgnoreReadOnlyRecommended, Origin, Delimiter, Editable, Notify, Converter, AddToMru, Local, CorruptLoad); #endif return ret; } public Workbook Open( string filename_, bool read_only_, Excel.XlFileFormat format_, string delimiter_ ) { return new Workbook(this, this._workbooks_open( filename_, Type.Missing, read_only_, format_, Type.Missing, Type.Missing, Type.Missing, Type.Missing, delimiter_, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing)); } public Workbook Open(string filename_, bool read_only_) { return new Workbook(this, this._workbooks_open( filename_, Type.Missing, read_only_, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing)); } public Workbook Add() { return new Workbook(this, this._workbooks.Add(Type.Missing)); } public int Count { get { return this._workbooks.Count; } } public Workbook this[int idx_] { get { return new Workbook(this, this._workbooks.get_Item(idx_)); } } public Workbook this[string name_] { get { return new Workbook(this, this._workbooks.get_Item(name_)); } } } /// <summary> /// Excel.Workbook /// </summary> public class Workbook : Base { public Workbook(Base parent_, object comobj_) : base(parent_, comobj_) { } private Excel.Workbook _workbook { get { return (Excel.Workbook)this._comobj; } } public Sheets Sheets { get { return new Sheets(this, this._workbook.Sheets); } } } /// <summary> /// Excel.Sheets /// </summary> public class Sheets : Base { public Sheets(Base parent_, object comobj_) : base(parent_, comobj_) { } private Excel.Sheets _sheets { get { return (Excel.Sheets)this._comobj; } } public int Count { get { return this._sheets.Count; } } public Worksheet this[int idx_] { get { return new Worksheet(this, this._sheets.get_Item(idx_)); } } public Worksheet this[string name_] { get { return new Worksheet(this, this._sheets.get_Item(name_)); } } } /// <summary> /// Excel.Worksheet /// </summary> public class Worksheet : Base { public Worksheet(Base parent_, object comobj_) : base(parent_, comobj_) { } private Excel.Worksheet _worksheet { get { return (Excel.Worksheet)this._comobj; } } public string Name { get { return this._worksheet.Name; } set { this._worksheet.Name = value; } } public void Delete() { this._worksheet.Delete(); } public void CopyAfter(Worksheet aft_) { // 指定したワークシートの右側へコピー // this._worksheet を aft_ の 右側へコピー this._worksheet.Copy(Type.Missing, aft_._comobj); } public void CopyBefore(Worksheet bef_) { // 指定したワークシートの左側へコピー // this._worksheet を bef_ の 左側へコピー this._worksheet.Copy(bef_._comobj, Type.Missing); } public Range Cells { get { return new Range(this, this._worksheet.Cells); } } public Range Range { get { return new Range(this, this._worksheet.Cells); } } public Range Rows(int row_) { return new Range(this, this._worksheet.Rows[row_, Type.Missing]); } public Range Columns(int col_) { return new Range(this, this._worksheet.Columns[Type.Missing, col_]); } } /// <summary> /// Excel.Range /// </summary> public class Range : Base { public Range(Base parent_, object comobj_) : base(parent_, comobj_) { } private Excel.Range _range { get { return (Excel.Range)this._comobj; } } public Range this[int row_, int col_] { get { return new Range(this, this._range.get_Item(row_, col_)); } } public Range this[Range cell1_, Range cell2_] { get { return new Range(this, this._range.get_Range(cell1_._comobj, cell2_._comobj)); } } public Range Resize(int row_, int col_) { return new Range(this, this._range.get_Resize(row_, col_)); } public bool Delete() { return (bool)this._range.Delete(Type.Missing); } public bool Delete(Excel.XlDeleteShiftDirection direction_) { return (bool)this._range.Delete(direction_); } public object Value { #if PIA_OFFICE_2000 get { return this._range.Value; } set { this._range.Value = value; } #endif #if PIA_OFFICE_XP get { return this._range.get_Value(Excel.XlRangeValueDataType.xlRangeValueDefault); } set { this._range.set_Value(Excel.XlRangeValueDataType.xlRangeValueDefault, value); } #endif } public Font Font { get { return new Font(this, this._range.Font); } } public Interior Interior { get { return new Interior(this, this._range.Interior); } } } /// <summary> /// Excel.Font /// </summary> public class Font : Base { public Font(Base parent_, object comobj_) : base(parent_, comobj_) { } private Excel.Font _font { get { return (Excel.Font)this._comobj; } } public int ColorIndex { get { return (int)this._font.ColorIndex; } set { this._font.ColorIndex = value; } } public bool Bold { get { return (bool)this._font.Bold; } set { this._font.Bold = value; } } } /// <summary> /// Excel.Interior /// </summary> public class Interior : Base { public Interior(Base parent_, object comobj_) : base(parent_, comobj_) { } private Excel.Interior _interior { get { return (Excel.Interior)this._comobj; } } public int ColorIndex { get { return (int)this._interior.ColorIndex; } set { this._interior.ColorIndex = value; } } public Excel.XlPattern Pattern { get { return (Excel.XlPattern)this._interior.Pattern; } set { this._interior.Pattern = value; } } } }
Form1.cs(使用例)
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { lib.SimpleExcel.Application obj_app = new lib.SimpleExcel.Application(); // 確実にオブジェクトを開放する為、usingは必須 using (obj_app) { obj_app.DisplayAlerts = false; obj_app.Visible = true; // 新規Workbookを開く lib.SimpleExcel.Workbook obj_workbook = obj_app.Workbooks.Add(); // 不要なSheetを削除 // MessageBox.Show(obj_workbook.Sheets.Count.ToString()); while (obj_workbook.Sheets.Count != 1) { obj_workbook.Sheets[2].Delete(); } // MessageBox.Show(obj_workbook.Sheets.Count.ToString()); // Sheet名付け lib.SimpleExcel.Worksheet obj_sheet = obj_workbook.Sheets[1]; obj_sheet.Name = "data"; // 配列データをシートへ複写 object[,] data = { { "データ0-0", "データ0-1" }, { "データ1-0", "データ1-1" }, }; obj_sheet.Cells[1, 1].Value = data; // これでは1セルしかコピーされない。 obj_sheet.Range[obj_sheet.Cells[3, 1], obj_sheet.Cells[4, 2]].Value = data; // 明示的に範囲指定 obj_sheet.Cells[5, 1].Resize(data.GetLength(1), data.GetLength(0)).Value = data; // 配列データをGetLengthして範囲指定 // セル値を読む object val_obj = obj_sheet.Cells[4, 2].Value; if ((val_obj as string) != null) { MessageBox.Show((string)val_obj); } // 取得した型を読む lib.SimpleExcel.Object obj1 = new lib.SimpleExcel.Object(null, val_obj); // MessageBox.Show(obj1.TypeName); // 範囲でセル値を読む val_obj = obj_sheet.Range[obj_sheet.Cells[3, 1], obj_sheet.Cells[3, 2]].Value; string str = ""; if ((val_obj as object[,]) != null) { object[,] obj2 = (object[,])val_obj; for (int row = 1; row <= obj2.GetLength(0); row++) { for (int col = 1; col <= obj2.GetLength(1); col++) { if ((obj2[row, col] as string) != null) { str += (string)obj2[row, col] + ","; } } str += "\n"; } } // MessageBox.Show(str); // 行削除 obj_sheet.Rows(1).Delete(); obj_sheet.Rows(1).Delete(); obj_sheet.Rows(1).Delete(); obj_sheet.Rows(1).Delete(); obj_app.Quit(); // MessageBox.Show("ComObjectCounter=" + lib.SimpleExcel.Base.ComObjectCounter.ToString()); } // MessageBox.Show("ComObjectCounter=" + lib.SimpleExcel.Base.ComObjectCounter.ToString()); obj_app = null; } } }