C#2.0:エクセルの操作クラス

エクセルのファイルを開き、操作するためのクラス。
IDisposableなのでusingでの使用を想定。
Excel.Subtotalメソッドは、複数回呼ぶことで小計、中計を作成することができる。
Excelのバージョンは2007を想定。
リソースリークは完全には無くなっていないかもしれない。
完全を求めるならば計測する必要があるかも。
.Net Framework 2.0用

2011/6/22: COMObjectのスイーパーつけたり、CSV,タブ区切りファイルを読み込めるようにしたり色々強化

using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
using System.Data;
using System.IO;

namespace ComboAndTextBoxBinder
{
    /// <summary>
    /// COMObjectを保管しておき、dispose(usingの終了)時に解放する。
    /// </summary>
    public class COMObjSweeper : IDisposable
    {
        protected Queue<object> queue = new Queue<object>();
        protected object queueLock = new object();
        protected object refLock = new object();

        int refCount = 0;

        /// <summary>
        /// usingするときに取得するオブジェクト(Reference Counter付き)
        /// </summary>
        public COMObjSweeper Ref
        {
            get
            {
                lock (this.refLock)
                {
                    refCount++;
                }                
                return this;
            }

        }

        /// <summary>
        /// COMオブジェクトをAddする
        /// </summary>
        /// <param name="obj">COMObject</param>
        public void Add(object obj)
        {
            lock (this.queueLock)
            {
                this.queue.Enqueue(obj);
            }
        }

        /// <summary>
        /// オブジェクトの開放
        /// </summary>
        /// <param name="obj"></param>
        public static void ReleaseComObject(object obj)
        {
            try
            {
                if (obj != null)
                {
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
                }
            }
            catch
            {
            }
        }

        /// <summary>
        /// COMオブジェクトを解放する。
        /// </summary>
        public void Sweep()
        {
            lock (this.queueLock)
            {
                while (this.queue.Count > 0)
                {
                    object obj = this.queue.Dequeue();
                    ReleaseComObject(obj);
                }
            }
        }

        #region IDisposable メンバ

        public void Dispose()
        {
            lock (this.refLock)
            {
                refCount--;
                if (refCount <= 0)
                {
                    Sweep();
                    refCount = 0;
                }
            }
        }

        #endregion
    }
        

    /// <summary>
    /// エクセルを開き、データ集計などを行うためのクラス
    /// </summary>
    public class ExcelUtil : IDisposable
    {
        protected Excel.Application oXls = null;
        protected Excel.Workbook oWBook = null; // workbookオブジェクト
        protected Excel.Worksheet oSheet; // Worksheetオブジェクト
        protected bool quitWhenDispose = true;

        protected COMObjSweeper localSwp = new COMObjSweeper();
        protected COMObjSweeper clsSwp = new COMObjSweeper();

        /// <summary>
        /// エクセルを表示するか否か?
        /// </summary>
        public bool Visible
        {
            set
            {
                this.oXls.Visible = value;
            }
            get
            {
                return this.oXls.Visible;
            }
        }

        /// <summary>
        /// Dispose時にエクセルを終了するか?
        /// </summary>
        public bool QuitWhenDispose
        {
            set
            {
                this.quitWhenDispose = value;
            }
            get
            {
                return this.quitWhenDispose;
            }
        }

        /// <summary>
        /// エクセルファイルのオープン
        /// </summary>
        /// <param name="excelFileName">開くファイル名</param>
        /// <param name="readOnly">リードオンリーで開くか?</param>
        /// <param name="displayAlarts">終了時のセーブしますか?などのアラートを表示するかどうか?</param>
        public void OpenBook(string excelFileName,bool visible,bool readOnly,bool displayAlarts)
        {
            this.oXls = new Excel.Application();
            this.Visible = visible;

            this.oXls.DisplayAlerts = displayAlarts;

            this.oWBook = (Excel.Workbook)(oXls.Workbooks.Open(
              excelFileName, Type.Missing, readOnly, Type.Missing,
                Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                Type.Missing, Type.Missing, Type.Missing,
                Type.Missing, Type.Missing, Type.Missing, Type.Missing
            ));

            // アクティブシートがあることが前提
            this.oSheet = (Excel.Worksheet)oWBook.ActiveSheet;

            this.clsSwp.Add(this.oSheet);
            this.clsSwp.Add(this.oWBook);
            this.clsSwp.Add(this.oXls);
        }

        /// <summary>
        /// データテーブルのRowをエクセルの行に設定
        /// </summary>
        /// <param name="startRow">エクセルへの設定開始行</param>
        /// <param name="startCol">エクセルへの設定開始列</param>
        /// <param name="endCol">エクセルへの設定終了列</param>
        /// <param name="dr">設定するデータテーブル</param>
        public void SetFromDataRow(int startRow, int startCol,int endCol, DataRow dr)
        {
            foreach (object obj in dr.ItemArray)
            {
                SetCell(startRow, startCol, obj);
                startCol++;
                if (startCol >= endCol)
                {
                    return;
                }
            }
        }

        /// <summary>
        /// データテーブルをエクセルに設定
        /// </summary>
        /// <param name="startRow">1から始まる行</param>
        /// <param name="startCol">1から始まる列</param>
        /// <param name="dt">データテーブル</param>
        public int SetFromDataTable(int startRow,int startCol,int endCol, DataTable dt)
        {
            foreach (DataRow dr in dt.Rows)
            {
                SetFromDataRow(startRow, startCol, endCol, dr);
                startRow++;
            }

            return startRow;
        }

        /// <summary>
        /// データテーブルをエクセルに設定
        /// </summary>
        /// <param name="startRow">1から始まる行</param>
        /// <param name="startCol">1から始まる列</param>
        /// <param name="dt">データテーブル</param>
        public int SetFromDataTable(int startRow, int startCol, DataTable dt)
        {
            foreach (DataRow dr in dt.Rows)
            {
                SetFromDataRow(startRow, startCol,Int32.MaxValue, dr);
                startRow++;
            }

            return startRow;
        }

        /// <summary>
        /// 1行分のテキストファイルデータをエクセルに設定する。
        /// </summary>
        /// <param name="startRow">開始行</param>
        /// <param name="startCol">開始列</param>
        /// <param name="endCol">終了列(Int32.MaxValueでテキストデータの最後の列まで)</param>
        /// <param name="textRowData">1行分のデータ</param>
        /// <param name="delimiterChars">デリミタとなる文字群(タブ、カンマなど)</param>
        /// <param name="trimChars">1列分のデータの最初と最後で取り除く文字(ダブルクオーテーションなど)</param>
        public void SetFromTextLine(int startRow, int startCol, int endCol, string textRowData, string delimiterChars,string trimChars)
        {
            char[] delimiters = delimiterChars.ToCharArray();
            string[] split = textRowData.Split(delimiters);
            char[] trims = (trimChars == null)?null:trimChars.ToCharArray();

            foreach (string data in split)
            {
                string srcdata = data;
                if ((trims != null) && (trims.Length > 0))
                {
                    srcdata = srcdata.TrimStart(trims);
                    srcdata = srcdata.TrimEnd(trims);
                }

                SetCell(startRow, startCol, srcdata);
                startCol++;
                if (startCol >= endCol)
                {
                    return;
                }
            }
        }


        /// <summary>
        /// textfileの内容をエクセルに設定
        /// </summary>
        /// <param name="startRow">1から始まる行</param>
        /// <param name="startCol">1から始まる列</param>
        /// <param name="endCol">終了の列(Int32.MaxValueでテキストデータの最後の列まで)</param>
        /// <param name="textFileData">タブ、カンマ区切りのテキストファイル</param>
        /// <param name="delimiterChars">デリミタとなる文字群(タブ、カンマなど)</param>
        /// <param name="trimChars">1列分の最初と最後で取り除く文字(ダブルクオーテーションなど)</param>
        public int SetFromTextFile(int startRow, int startCol, int endCol, string textFileData, string delimiterChars, string trimChars)
        {            
            using(StringReader sr = new StringReader(textFileData))
            {
                while (sr.Peek() >= 0)
                {
                    string line = sr.ReadLine();
                    SetFromTextLine(startRow, startCol, endCol, line, delimiterChars,trimChars);
                    startRow++;
                }
            }

            return startRow;
        }

        /// <summary>
        /// textfileの内容をエクセルに設定
        /// </summary>
        /// <param name="startRow">1から始まる行</param>
        /// <param name="startCol">1から始まる列</param>
        /// <param name="textFileData">タブ、カンマ区切りのテキストファイル</param>
        /// <param name="delimiterChars">デリミタとなる文字群(タブ、カンマなど)</param>
        /// <param name="trimChars">1列分の最初と最後で取り除く文字(ダブルクオーテーションなど)</param>
        public int SetFromTextFile(int startRow, int startCol, string textFileData, string delimiterChars,string trimChars)
        {
            return SetFromTextFile(startRow, startCol,Int32.MaxValue, textFileData, delimiterChars,trimChars);
        }


        // カラムの値はExcelではアルファベットで表されるので、整数のカラム値をアルファベットのカラム値に変換する。
        public static string ToAlphabetCol(int colNum)
        {
            string alphaStr = "";
            //カラムは1オリジンなので引いておく
            // アルファベットの数で割る。
            for (int i = colNum; i > 0; i /= 26)
            {
                // Aが最初の位置なので、それを値で足し込むことでアルファベットの値を作成
                char modChar = Convert.ToChar('A' + ((i % 26) - 1));
                // 小さい桁から挿入するので、0の位置にインサート
                alphaStr = alphaStr.Insert(0, new string(modChar, 1));
            }

            return alphaStr;
        }

        // 数値の行、桁の値からアルファベット+数値の値に変更
        public static string ToExcelRowCol(int rowNum, int colNum)
        {
            return ToAlphabetCol(colNum) + rowNum.ToString();
        }

        public Excel.Range GetRange(int row, int col)
        {
            string rngStr = ToExcelRowCol(row, col);
            return GetRange(rngStr, rngStr);
        }

        /// <summary>
        /// Rangeオブジェクトの取得
        /// </summary>
        /// <param name="startRange">取得開始列行("A1"形式) nullでA1を選択</param>
        /// <param name="endRange">取得終了列行("A1"形式) nullで最終列行を選択</param>
        /// <returns>取得したRange</returns>
        public Excel.Range GetRange(string startRange, string endRange)
        {
            if (String.IsNullOrEmpty(startRange) == true)
            {
                startRange = "A1";
            }

            Excel.Range rng;
            if (String.IsNullOrEmpty(endRange) == true)
            {
                Excel.Range endRng = oSheet.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell, Type.Missing);
                rng = this.oSheet.get_Range(startRange, endRng);
                this.localSwp.Add(endRng);
                this.localSwp.Add(rng);
                return rng;
            }

            rng = this.oSheet.get_Range(startRange, endRange);
            this.localSwp.Add(rng);
            return rng;
        }

        /// <summary>
        /// 1行分のRangeオブジェクトを取得する。
        /// </summary>
        /// <param name="startRange">取得開始列行("A1"形式) nullで自動的に"A1"になる</param>
        /// <param name="endRange">取得終了列行("A1"形式) nullで自動的に取得開始行の最終列</param>
        /// <returns>取得したRange</returns>
        public Excel.Range GetOneLineRange(string startRange, string endRange)
        {
            if (String.IsNullOrEmpty(startRange) == true)
            {
                startRange = "A1";
            }

            Excel.Range startRng = this.GetRange(startRange, startRange);
            Excel.Range rng;
            if (String.IsNullOrEmpty(endRange) == true)
            {
                Excel.Range endRng = oSheet.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell, Type.Missing);
                rng = this.oSheet.get_Range(startRng, ExcelUtil.ToExcelRowCol(startRng.Row,endRng.Column));
                this.localSwp.Add(endRng);
                this.localSwp.Add(rng);
                return rng;
            }

            rng = this.oSheet.get_Range(startRange, endRange);
            this.localSwp.Add(rng);
            return rng;
        }

        /// <summary>
        /// セルの上下左右と間に罫線を設定する。
        /// </summary>
        /// <param name="startColRow">罫線の設定を開始する列行の文字列("A1"形式)</param>
        /// <param name="endColRow">罫線の設定を終了する列行の文字列("A1"形式)</param>
        /// <param name="aroundLineStyle">周りの罫線のラインスタイル
        /// xlLineStyleNone = -4142,
        /// xlDouble = -4119,
        /// xlDot = -4118,
        /// xlDash = -4115,
        /// xlContinuous = 1,
        /// xlDashDot = 4,
        /// xlDashDotDot = 5,
        /// xlSlantDashDot = 13, 
        /// </param>
        public void SetAllBorderLine(string startColRow, string endColRow, int aroundLineStyle, int betweenLineStyle)
        {
            using (this.localSwp.Ref)
            {
                Excel.Range rng = this.GetRange(startColRow, endColRow);

                rng.Borders.get_Item(Excel.XlBordersIndex.xlEdgeBottom).LineStyle = aroundLineStyle;
                rng.Borders.get_Item(Excel.XlBordersIndex.xlEdgeTop).LineStyle = aroundLineStyle;
                rng.Borders.get_Item(Excel.XlBordersIndex.xlInsideHorizontal).LineStyle = betweenLineStyle;

                rng.Borders.get_Item(Excel.XlBordersIndex.xlEdgeLeft).LineStyle = aroundLineStyle;
                rng.Borders.get_Item(Excel.XlBordersIndex.xlEdgeRight).LineStyle = aroundLineStyle;
                rng.Borders.get_Item(Excel.XlBordersIndex.xlInsideVertical).LineStyle = betweenLineStyle;
            }

        }

        /// <summary>
        /// 特定行のセルの下に罫線を設定する。
        /// </summary>
        /// <param name="startColRow">罫線の設定を開始する列行の文字列("A1"形式)</param>
        /// <param name="endColRow">罫線の設定を終了する列行の文字列("A1"形式) nullで開始行の最後まで罫線を表示</param>
        /// <param name="lineStyle">罫線のラインスタイル(Excel.Borders.LineStyle)</param>
        public void SetBottomBorderLine(string startColRow, string endColRow, int lineStyle)
        {
            using (this.localSwp.Ref)
            {
                Excel.Range rng = this.GetOneLineRange(startColRow, endColRow); // 引数は"A1"形式で指定
                rng.Borders.get_Item(Excel.XlBordersIndex.xlEdgeBottom).LineStyle = lineStyle;
            }
        }

        /// <summary>
        /// ヘッダーのメッセージを変更(中央ヘッダー)
        /// </summary>
        /// <param name="message"></param>
        public void SetPageCenterHeader(string message)
        {
            this.oSheet.PageSetup.CenterHeader = message;
        }

        /// <summary>
        /// フッターのメッセージを変更(右フッター)
        /// </summary>
        /// <param name="message"></param>
        public void SetPageRightFooter(string message)
        {
            this.oSheet.PageSetup.RightFooter = message;
        }

        /// <summary>
        /// 印刷時に必ず表示する行を設定($1:$2のような形で設定)
        /// </summary>
        /// <param name="rowAndColumns"></param>
        public void SetPageTitleRows(string rows)
        {
            this.oSheet.PageSetup.PrintTitleRows = rows;
        }

        /// <summary>
        /// セルを設定(引数は数値)
        /// </summary>
        /// <param name="row">設定する行(1オリジン)</param>
        /// <param name="col">設定する列(1オリジン)</param>
        /// <param name="message">設定する文字列</param>
        public void SetCell(int row, int col, object val)
        {
            SetCell(ToExcelRowCol(row, col),val);
        }

        /// <summary>
        /// セルを設定(引数はA1のような形式)
        /// </summary>
        /// <param name="colRow">設定する列行("A1"形式)</param>
        /// <param name="message">設定する文字列</param>
        public void SetCell(string colRow, object val)
        {
            using(this.localSwp.Ref)
            {
                Excel.Range rng = this.GetRange(colRow, colRow);
                rng.Value2 = val;
            }
        }

        /// <summary>
        /// セルに一度に設定(引数はA1のような形式)
        /// </summary>
        /// <param name="startColRow">設定する列行の開始位置("A1"形式)</param>
        /// <param name="startColRow">設定する列行の終了位置("A1"形式)</param>
        /// <param name="message">設定する文字列</param>
        public void SetCells(string startColRow,string endColRow, object val)
        {
            using (this.localSwp.Ref)
            {
                Excel.Range rng = this.GetRange(startColRow, endColRow);
                rng.Value2 = val;
            }
        }

        /// <summary>
        /// ソート処理を行う。
        /// </summary>
        /// <param name="startRowCol">ソート範囲の開始("A1"形式)</param>
        /// <param name="endRowCol">ソート範囲の終了("A1"形式)</param>
        /// <param name="firstKeyCol">第1キーの列の位置(1オリジン)</param>
        /// <param name="secondKeyCol">第2キーの列の位置(1オリジン)</param>
        /// <param name="thirdKeyCol">第3キーの列の位置(1オリジン)</param>
        /// <param name="ascending">昇順・降順(全てのキーに適用)</param>
        public void Sort(string startRowCol,string endRowCol,int firstKeyCol, int secondKeyCol, int thirdKeyCol,bool ascending)
        {
            using (this.localSwp.Ref)
            {
                Excel.Range rng = this.GetRange(startRowCol, endRowCol);
                object key1 = (firstKeyCol > 0) ? rng.Columns[firstKeyCol, Type.Missing] : Type.Missing;
                object key2 = (secondKeyCol > 0) ? rng.Columns[secondKeyCol, Type.Missing] : Type.Missing;
                object key3 = (thirdKeyCol > 0) ? rng.Columns[thirdKeyCol, Type.Missing] : Type.Missing;

                this.localSwp.Add(key1);
                this.localSwp.Add(key2);
                this.localSwp.Add(key3);

                Excel.XlSortOrder asc = Excel.XlSortOrder.xlAscending;
                if (ascending == false)
                {
                    asc = Excel.XlSortOrder.xlDescending;
                }

                rng.Sort(key1,
                    asc,
                    key2,
                    Type.Missing,
                    asc,
                    key3,
                    asc,
                    Excel.XlYesNoGuess.xlNo,
                    Type.Missing,
                    Type.Missing,
                    Excel.XlSortOrientation.xlSortColumns,
                    Excel.XlSortMethod.xlPinYin,
                    Excel.XlSortDataOption.xlSortNormal,
                    Excel.XlSortDataOption.xlSortNormal,
                    Excel.XlSortDataOption.xlSortNormal);
            }

        }

        /// <summary>
        /// エクセルファイルの集計を行う
        /// </summary>
        /// <param name="startColRow">開始列行文字列("A1"形式)</param>
        /// <param name="endColRow">終了列行文字列("A1"形式)</param>
        /// <param name="totalList">集計を行う列の値</param>
        /// <param name="groupByField">同じ値のものを集計して表示するための列の値</param>
        public void AggregateTotal(string startColRow,string endColRow, int[] totalList, int groupByField)
        {
            using (this.localSwp.Ref)
            {
                Excel.Range rng = this.GetRange(startColRow, endColRow);

                // 集計を行う。groupByFieldが同じ値のモノについて、totalListの値を集計して表示する。集計と総計を表示
                rng.Subtotal(groupByField, Excel.XlConsolidationFunction.xlSum, totalList,
                    false, Type.Missing, Excel.XlSummaryRow.xlSummaryBelow);
            }
        }

        /// <summary>
        /// セルを取得
        /// </summary>
        /// <param name="row">行の値(1オリジン)</param>
        /// <param name="col">列の値(1オリジン)</param>
        /// <returns></returns>
        public string GetCell(int row, int col)
        {
            return GetCell(ToExcelRowCol(row, col));
        }
        
        /// <summary>
        /// セルを取得(引数はA1のような形式)
        /// </summary>
        /// <param name="colRow">取得するセルの列行("A1"形式)</param>
        /// <returns>取得した値</returns>
        public string GetCell(string colRow)
        {
            using (this.localSwp.Ref)
            {
                Excel.Range rng = this.GetRange(colRow, colRow);
                string str = rng.Value2 as string;
                return str;
            }
        }



        #region IDisposable メンバ
        /// <summary>
        /// エクセルプログラムの終了を行いリソースを解放する。
        /// </summary>
        public void Dispose()
        {
            try
            {
                if ((this.quitWhenDispose == true) && (this.oWBook != null))
                {
                    this.oWBook.Close(false, Type.Missing, Type.Missing);
                }
            }
            catch
            {
            }

            try
            {
                if ((this.quitWhenDispose == true) && (this.oXls != null))
                {
                    this.oXls.Quit();
                }
            }
            catch
            {
            }

            this.localSwp.Sweep();
            this.clsSwp.Sweep();
        }

        #endregion

    }
}