Excelを自動化しよう

#はじめに
仕事でExcelを使う方は多いかと思います。
100ファイルも200ファイルも手作業でデータ整理なんてやってられるか！と思ったあなたはNetOfficeを使って自動化に挑戦してみるべきです。

#NetOfficeとは？
NetOfficeは Open Source Development Network 内のプロジェクトでMS Office のアドイン開発を行うことができるパッケージです。
コードはVBAと同じプロパティ名やメソッド名が使われ、不明な点はVBAのヘルプサイトでだいたい補完できます。

###VBAと何が違うの？
決定的な違いは実行速度の差です。C#の場合マルチスレッドで処理を実装すれば飛躍的に効率が上がります。(ただし行儀が悪いコードを書くとプロセスが山のように残ります。)

#使ってみましょう
###前提条件
Visual Studio がインストールされていること(個人向け無償ライセンスがあるのでこの機会に是非)
Excelがインストールされていること

###インストール方法
NuGet で NetOffice を検索してインストール。以上！

###とりあえずワークブックを開くだけ
```ruby:quiita.rb
using System;
using NetOffice.ExcelApi;

namespace NetOfficeTest
{
    class Excel
    {
        /*ファイルを開くだけ*/
        public void Open()
        {
            using (NetOffice.ExcelApi.Application app = new NetOffice.ExcelApi.Application())
            {
                try
                {
                    app.GetOpenFilename();
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    app.Dispose();
                }
            }
        }
    }
}
```
NetOfffice.ExcelApi.Application クラスは単に Application と書いても問題ありませんが、フォームアプリケーションを作る場合 System.Windows.Forms 名前空間にも Application クラスがあるため明示的に宣言しておいたほうが面倒も少なくなります。

ファイルが開けないとクラッシュするので try 文で例外処理をするようにしましょう。
usingから出ればappは開放されるはずなのでDisposeは必要ないのでしょうが、ゴミプロセスを積み上げた過去のトラウマがあり念の為。

~~自宅環境だとExcelがなくデバッグできないのでまた後日更新します~~

###より実践的なコーディング

上のコードだとただファイルを開いて閉じるだけなので、下記に特定のセルからデータを吸い出すコードを例示します。

```ruby:quiita.rb
using System;
using System.Text;
using System.IO;
using NetOffice.ExcelApi;
using System.Windows.Forms;

namespace ExcelTest
{
    class Excel
    {
        public void RangeRead(string[] str, string range)
        {
            StringBuilder @string = new StringBuilder(); /*CSV組立用*/
            NetOffice.ExcelApi.Application app = new NetOffice.ExcelApi.Application
            {
                DisplayAlerts = false /*ディスプレイアラートを非表示に*/
            };
            
            foreach (string file in str)
            {
                using (Workbook book = app.Workbooks.Open(file))
                {
                    @string.Append(file + ","); 
                    Worksheet worksheet = (Worksheet)book.Worksheets[1];
                    try
                    {
                        var obj = worksheet.Range(range).Value;
                        /*objの戻り値が単一セルと複数セルの場合で違うので場合分け*/
                        if(obj.GetType() == typeof(object[,]))
                        {
                            foreach (object o in (object[,])obj)
                            {
                                if (o != null) @string.Append(o.ToString() + ",");
                                else @string.Append(","); 
                            }     
                        }
                        if(obj.GetType() == typeof(string) || obj.GetType() == typeof(double))
                        {
                            @string.Append(obj.ToString());
                        }
                    }
                    catch (Exception e)
                    {
                        MessageBox.Show(e.Message);
                    }
                    /*finallyで必ずWorkbookオブジェクトを開放すること*/
                    finally
                    {
                        book.Close();
                        book.Dispose();
                    }
                }
                @string.Append("\n");
            } 
            /*最後にCSVに吐き出す*/
            StreamWriter writer = new StreamWriter(System.Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\result.csv", false);
            writer.WriteLine(@string);
            writer.Close();
            writer.Dispose();
            app.Quit();
            MessageBox.Show("完了");
        }
    }
}
```

Range.Valueプロパティの戻り値は範囲指定した場合はobject[,]型、単一セルの場合数値の場合はdouble型(整数が入っていてもdouble型)、
文字列の場合string型で返ってきます。
範囲指定の場合セルの中身が空っぽの場合があるためその場合カンマだけをAppendする処理を入れます。入れないとデータが抜け落ちます。データの格納順はR1C1,R1C2…R1C *n* ,R2C1,R2C2…R2C *n* ……R *n* C *n* です。