以前、Rubyで以下のようなことを行いました。
RubyでFromTo表現のテキストファイルを、カウントアップ表現に変換する - メモ的な思考的な
そんな中、テキストファイルだけではなくExcel形式のものも出てきたので、同じように対応することにしました。
ただ、今回はRubyがない環境だったので、C#で作ることにします。
なお、ファイルの種類が2種類ほどあったので、「一般」「特殊」としてメニューと出力ファイル名を分けることにしました。
また、変換作業を行ったかどうかを忘れないために、Skypeへ通知するようにもしてみました。
■考えたこと
ExcelをC#から操作するライブラリの選択
選択肢としてはVBAやVSTO、NetOfficeやExcel-DNAなどがありましたが、配布を簡単にしたいと考え、Excel-DNAを選択しました。
アドインとして組み込むことができ、メニューも簡単に追加できたこともポイントでした。
Skypeを扱うには
公式ページにSkype4COMというライブラリがありましたので、そちらを利用することにしました。
■作業の流れ
2. C#ソリューションの作成
「クラスライブラリ」を選択して、ソリューションを作成します。
ソリューションのフォルダの中に、上記1.よりダウンロードしたライブラリを保存します。
3. 参照設定の追加
上記1.のライブラリをプロジェクトの参照設定に追加します。
- ExcelDna.Integration.dll
- ExcelDNASkype.dll
他、処理途中のメッセージを表示するため、以下も参照設定に追加します。
- System.Windows.Forms
ここまでの設定で、ソリューションエクスプローラーは以下のようになります。
4. 実装
ソースは後ほど記載します。
5. dnaファイルの編集
作成したクラスライブラリを参照するため、dnaファイルを編集します(内容は後述)。
6. ビルド
ビルドしたら、以下のファイルを任意の場所にコピーしておきます。
- ExcelDna.dna
- ExcelDna.xll
- ExcelDNASkype.dll
7. アドインの追加
対象のExcelファイルを開いて、
ツール > アドイン > 参照ボタン > 上記のExcelDnaファイルを選択
とすることで、有効なアドインに追加され、メニューにもAddInが追加されています。
以上で、仕様を満たすアドインを作ることができました。
配布するファイルも3つだけなので、かなりお手軽です。
■ソース
以下の2ファイルはgistにも上げてあります。
「FromTo表現のExcelをカウントアップ表現に変換してSkypeへ通知する」サンプル。
ExcelDna.dna
<DnaLibrary RuntimeVersion="v4.0" Name="ExcelDNASkype" Language="CS"> <ExternalLibrary Path="ExcelDNASkype.dll"/> </DnaLibrary>
ExcelDNASkype.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; // Add using System.Windows.Forms; using ExcelDna.Integration; using SKYPE4COMLib; namespace ExcelDNASkype { public class ExcelDNASkype : IExcelAddIn { const int START_COLUMN = 0; const int LAST_COLUMN = 1; const string MENU_NORMAL = "一般"; const string MENU_SPECIAL = "特殊"; // メッセージを送信する先のユーザーID const string SKYPE_USER = ""; /// <summary> /// アドインメニュー:一般 /// </summary> [ExcelCommand(MenuName = "AddIn", MenuText = "Normal")] public static void DisplayNormalMenu() { Run(MENU_NORMAL); } /// <summary> /// アドインメニュー:特殊 /// </summary> [ExcelCommand(MenuName = "AddIn", MenuText = "Special")] public static void DisplaySpecialMenu() { Run(MENU_SPECIAL); } /// <summary> /// メイン処理 /// </summary> /// <param name="menuName">メニュー名</param> private static void Run(string menuName) { // テキストファイル出力 var contents = CreateContents(); // .NET4 から登場した、Path.Combineのオーバーロードを利用 var txtpath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), menuName + "_content.txt"); MessageBox.Show(txtpath); if (ExportTextFile(contents, txtpath)) MessageBox.Show(contents.Count.ToString() + " 件、出力しました。"); // Excelファイル保存 if (SaveAsExcelFile(txtpath)) MessageBox.Show("Excelファイルを保存しました。"); // Skype通知 if (SendSkypeMessage(menuName)) MessageBox.Show("Skype通知をしました。"); } /// <summary> /// 出力するテキストファイルデータの作成 /// </summary> /// <returns>テキストファイルデータ</returns> private static List<string> CreateContents() { var contents = new List<string>(); // 1万行程度あれば十分かと for (int row = 1; row < 10000; row++) { var startNo = GetCellValue(row, START_COLUMN); var lastNo = GetCellValue(row, LAST_COLUMN); if (startNo == 0 && lastNo == 0) break; var amount = lastNo - startNo + 1; for (int i = 0; i < amount; i++) { contents.Add("x" + string.Format("{0:D4}", (startNo + i)) + "x"); } } return contents; } /// <summary> /// セルの値を取得する /// </summary> /// <param name="row">セルの行(0始まり)</param> /// <param name="column">セルの列(0始まり)</param> /// <returns>セルの値、取得できない場合は、セルの値は0を返す</returns> private static int GetCellValue(int row, int column) { var cell = new ExcelReference(row, column); var value = 0; if (int.TryParse(cell.GetValue().ToString(), out value)) return value; else return 0; } /// <summary> /// テキストファイルの出力 /// </summary> /// <param name="contents">テキストファイルの内容</param> /// <param name="fullpath">出力先のパス</param> /// <returns></returns> private static bool ExportTextFile(List<string> contents, string fullpath) { System.Text.Encoding encode = System.Text.Encoding.GetEncoding("SHIFT_JIS"); using (System.IO.StreamWriter sw = new System.IO.StreamWriter(fullpath, true, encode)) { foreach (var content in contents) { sw.WriteLine(content); } } return true; } /// <summary> /// Excelファイルとして保存 /// </summary> /// <param name="txtpath">テキストファイルの出力先</param> /// <returns></returns> private static bool SaveAsExcelFile(string txtpath) { var regex = new System.Text.RegularExpressions.Regex("txt$"); var xlspath = regex.Replace(txtpath, "xls"); // Excel-DNAの機能を使用 XlCall.Excel(XlCall.xlcSaveAs, xlspath); return true; } /// <summary> /// Skypeメッセージの送信 /// </summary> /// <returns></returns> private static bool SendSkypeMessage(string menu) { // Skype4COMを使用 SKYPE4COMLib.Skype skype = new Skype(); skype.SendMessage(SKYPE_USER, menu); return true; } // 以下、IExcelAddIn用で、今回は使用しない public void AutoOpen() { } public void AutoClose() { } } }