C#からExcelを操作する(PIAその1)

個人的な話ですが、ExcelVBAでお粗末なソフトに何度も苦しめられた経験があります。そんな折、Excelレガシーという言葉を知り、激しく共感したところです。Excelを操作するのにVBAだけじゃないんだよ、というメッセージを込めて今日はC#からExcelを操作してみます。

 

1.準備

C#のプロジェクトに参照設定を追加します、COMタブ内 [Microsoft Excel ** Object Library]です。**はバージョンですが、Excel2003ならOffice11、Excel2007ならOffice12、Excel2010ならOffice13を飛ばして14となります。(2003と2007では動作確認しましたが、2010ではできていませんが多分動くはず)

add_re_excel

 

2.起動しているExcelを操作する

まずはExcelが開いており、それに対してデータを書き込む想定です。説明上、1つ1つの操作をわかりやすくする目的でWindowsFormsアプリとしてイベントハンドラに処理を分けて記載してみます。

excel_operate_form

 ExcelOperateForm.cs

namespace ExcelOperation
{
    using System;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    using Excel = Microsoft.Office.Interop.Excel;

    public partial class ExcelOperateForm : Form
    {
        Excel._Application xlApp;
        Excel.Workbooks xlBooks;
        Excel.Workbook xlBook;
        Excel.Worksheet xlSheet;

        public ExcelOperateForm()
        {
            InitializeComponent();
        }

        private void getOpeningExcelButton_Click(object sender, EventArgs e)
        {
            // 開いているExcelを取得
            try
            {
                xlApp = (Excel.Application)Marshal.GetActiveObject("Excel.Application");
            }
            catch (COMException ex)
            {
                MessageBox.Show("Excelが起動されていません" + ex.Message);
                return;
            }

            // 開いているExcelのBookを取得
            // <MEMO>
            // Excel操作ソフトを実行中に強制終了などするとCOMオブジェクトの解放モレが発生し、
            // プロセスが残る状態になる。その状態で再度Excel操作をしようとすると、残った
            // プロセスの方を操作対象としてしまう。結果、新しいブックを開いていても以下の操作に
            // 失敗することがある。この場合、タスクマネージャーからEXCEL.EXEプロセスをキルする必要がある。
            xlBooks = xlApp.Workbooks;
            if (xlBooks.Count == 0)
            {
                MessageBox.Show("プロセスが残っていないか確認してください");
                return;
            }
            xlBook = xlBooks.get_Item(1);
            xlSheet = (Excel.Worksheet)xlBook.Sheets.get_Item(1);
        }

        private void setTextButton_Click(object sender, EventArgs e)
        {
            if (xlSheet == null)
            {
                return;
            }
            xlSheet.Cells[1, 1] = "a";
        }

        private void setNumberButton_Click(object sender, EventArgs e)
        {
            if (xlSheet == null)
            {
                return;
            }
            xlSheet.Cells[2, 1] = "10";
        }

        private void setFormulaButton_Click(object sender, EventArgs e)
        {
            if (xlSheet == null)
            {
                return;
            }
            xlSheet.Cells[2, 2] = "=A2*10";
        }

        private void setLineButton_Click(object sender, EventArgs e)
        {
            if (xlSheet == null)
            {
                return;
            }
            xlSheet.Shapes.AddLine(1, 1, 100, 100);
        }
    }
}

注意点としては複数のExcelが起動している場合、それを識別することはできません。プロセス一覧を取得して対応付けて…で可能かもしれませんが茨の道です。そもそもCOMのオートメ-ションなんかにそんな引数もなかったように思うし、そもそもムリかもしれません。

上記コードOffice2003, 2007で動作確認しましたが、一度トラブりました。VisualStudioから実行していて、GetActiveObjectでExcel.Aplicationが取得できないのです。32bit ⇔ 64bitの違いはない筈だし・・・等と考えましたが、答えはStackOverFlow先生にありました。原因はVisualStudioを管理者として実行していたためです。これは気付きづらいですね;

各コマンドを実行したExcelの様子はこちら。

run_excel

 

3.Excelファイルを新規作成して操作する

測定データのロギングなどでは上の方法で良いかとも思いますが、Excelファイルを作成したい場合はちょっとやり方が異なります。こちらも同じ要領でWindowsFormsアプリにしてみます。

excel_create_form

 ExcelCreateForm.cs

namespace ExcelOperation
{
    using System;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    using Excel = Microsoft.Office.Interop.Excel;

    public partial class ExcelCreateForm : Form
    {
        Excel._Application xlApp;
        Excel.Workbooks xlBooks;
        Excel.Workbook xlBook;
        Excel.Worksheet xlSheet;

        public ExcelCreateForm()
        {
            InitializeComponent();
        }

        private void createExcelFileButton_Click(object sender, EventArgs e)
        {
            xlApp = new Excel.Application();
            xlBook = xlApp.Workbooks.Add();
            xlSheet = (Excel.Worksheet)xlBook.Sheets.get_Item(1);
        }

        private void setTextButton_Click(object sender, EventArgs e)
        {
            if (xlSheet == null)
            {
                return;
            }
            xlSheet.Cells[1, 1] = "a";
        }

        private void setNumberButton_Click(object sender, EventArgs e)
        {
            if (xlSheet == null)
            {
                return;
            }
            xlSheet.Cells[2, 1] = "10";
        }

        private void setFormulaButton_Click(object sender, EventArgs e)
        {
            if (xlSheet == null)
            {
                return;
            }
            xlSheet.Cells[2, 2] = "=A2*10";
        }

        private void setLineButton_Click(object sender, EventArgs e)
        {
            if (xlSheet == null)
            {
                return;
            }
            xlSheet.Shapes.AddLine(1, 1, 100, 100);
        }

        private void ExcelCreateForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            xlBook.Close();
            xlApp.Quit();
            Marshal.ReleaseComObject(xlApp);
            xlApp = null;
        }
    }
}

これを実行して各ボタンを押下した後Formを閉じようとすると以下のようになります。

save_excel

この辺りはユーザ問い合わせなくファイル保存するとかできますので適宜ケアしてください。ちなみに内部オブジェクトとしてExcelを処理する方法では管理者権限でも問題なく動作します。動作結果は先ほどと同じなので省略。

Excelの操作は他にも色々あるのでExcelVBAを唯一神としないようご注意ください。

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中