Excel-DNAを使いFromTo表現のExcelをカウントアップ表現に変換、Skype4COMでSkypeへ通知する

以前、Rubyで以下のようなことを行いました。
RubyでFromTo表現のテキストファイルを、カウントアップ表現に変換する - メモ的な思考的な


そんな中、テキストファイルだけではなくExcel形式のものも出てきたので、同じように対応することにしました。


ただ、今回はRubyがない環境だったので、C#で作ることにします。
なお、ファイルの種類が2種類ほどあったので、「一般」「特殊」としてメニューと出力ファイル名を分けることにしました。
また、変換作業を行ったかどうかを忘れないために、Skypeへ通知するようにもしてみました。

■環境

  • Windows7 x64
  • VisualStudio 2010 SP1
  • Skypeはインストール・アカウント設定済
  • C# 4.0
  • Excel-DNA 0.30
  • Skype4COM 1.0.38

■考えたこと

ExcelC#から操作するライブラリの選択

選択肢としてはVBAVSTO、NetOfficeやExcel-DNAなどがありましたが、配布を簡単にしたいと考え、Excel-DNAを選択しました。
アドインとして組み込むことができ、メニューも簡単に追加できたこともポイントでした。

Skypeを扱うには

公式ページにSkype4COMというライブラリがありましたので、そちらを利用することにしました。

仕様

以下の流れで動くようにします。

  1. Excelファイルのメニューから「Normal」「Special」を選択する
  2. テキストファイルをデスクトップに出力する
  3. 処理を行ったExcelファイルに名前を付けて保存する
  4. Skypeにメッセージを飛ばす

■作業の流れ

1. 必要なライブラリのダウンロード

以下よりダウンロードします。

2. C#ソリューションの作成

「クラスライブラリ」を選択して、ソリューションを作成します。
ソリューションのフォルダの中に、上記1.よりダウンロードしたライブラリを保存します。

3. 参照設定の追加

上記1.のライブラリをプロジェクトの参照設定に追加します。

  • ExcelDna.Integration.dll
  • ExcelDNASkype.dll


他、処理途中のメッセージを表示するため、以下も参照設定に追加します。


ここまでの設定で、ソリューションエクスプローラーは以下のようになります。

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() { }

    }
}