【C#】Excelファイルを編集する(COM相互運用、遅延バインディング編)

C#

この記事では、Microsoft Office Interop Excelを遅延バインディング(Late Binding)で使用し、Excelファイルに対して以下の操作をする方法を紹介します。

  • セルの数値の取得と設定
  • テキストの取得と設定
  • セルの背景色の設定
  • セルのフォーマットの設定

早期バインディングに対する遅延バインディングの利点

バージョンの互換性

遅延バインディングは、特にアプリケーションが異なるバージョンの外部ライブラリやコンポーネント(例: Microsoft Office)と互換性を持つ必要がある場合に有用です。遅延バインディングを使用すると、コンポーネントの具体的なバージョンに依存せずにコードを記述できるため、異なる環境でのアプリケーションの動作が容易になります。

参照が不要

遅延バインディングを使用する場合、コンパイル時に外部ライブラリへの参照を追加する必要がありません。これは、特に配布やデプロイメントを簡素化する際に有利です。コンポーネントのインストールやバージョンの違いによる問題を避けることができます。

サンプルコード実行の準備

準備1:フォームの設定

フォームにボタンをひとつ配置します。名前はbutton1とします。デザイナ上でボタンをダブルクリックしておき、事前にイベントハンドラを生成します。

準備2:Excelファイルの作成

編集対象のExcelファイルを準備してください。対象のファイルには “Sheet1” が存在している必要があります。

準備3:Excelの参照設定は不要

遅延バインディング(Late Binding)、プロジェクトの参照におけるExcelの参照は不要です。

サンプルコード:Excel操作クラスを使用する側

ボタン押下時のイベントハンドラにExcelファイル操作処理を実装しています。Excelファイルの実際の操作は、MyExcel名前空間のMyWorkbookクラスで行います。

using System;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
using MyExcel;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            MyWorkbook workbook = null;

            try
            {
                workbook = new MyWorkbook(@"c:\path\to\your\file.xlsx");
                MyWorksheet worksheet = workbook.GetSheet("Sheet1");

                // セルから数値を取得
                int value = worksheet.GetValue<int>(1, 1);

                // セルに数値を設定
                worksheet.SetValue(1, 1, value + 1);

                // セルからテキストを取得
                string text = worksheet.GetValue<string>(2, 1);

                // セルにテキストを設定
                worksheet.SetValue(2, 1, text + "a");

                // セルの背景色を黄色に設定
                worksheet.SetColor(3, 1, Color.Yellow);

                // セルのフォーマットをテキスト形式に設定して、値を設定
                worksheet.SetFormat(4, 1, "@");
                worksheet.SetValue(4, 1, "1234");

                // 変更を保存
                workbook.Save();

                // ワークブックを閉じる
                workbook.Close();

                MessageBox.Show("Excelファイルの操作が完了しました。");
            }
            catch (Exception ex)
            {
                // ワークブックを閉じる
                if (workbook != null)
                {
                    workbook.Close();
                }

                MessageBox.Show("エラーが発生しました: " + ex.Message);
            }
        }
    }
}

サンプルコード:Excel操作クラス

こちらは、MyWorkbookクラスの実装です。COMオブジェクトを使用する際のオブジェクト解放などの複雑な処理を、こちらのクラスで隠蔽します。

// MyExcel.cs
using System;
using System.IO;
using System.Drawing;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace MyExcel
{
    // ワークブック
    class MyWorkbook
    {
        dynamic m_xlApp;
        dynamic m_xlWorkbooks;
        dynamic m_xlWorbook;
        List<MyWorksheet> m_WorkSheetList = new List<MyWorksheet>();

        // コンストラクタ
        public MyWorkbook(string filename)
        {
            // Excelアプリケーション生成
            m_xlApp = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));
            m_xlApp.Visible = false;   // 非表示で動作

            // エラーチェック
            if (!File.Exists(filename))
            {
                throw new FileNotFoundException("ファイルが見つかりません。" + Environment.NewLine +
                                                 filename);
            }

            // ワークブック取得
            m_xlWorkbooks = m_xlApp.Workbooks;
            m_xlWorbook = m_xlWorkbooks.Open(filename);

            // ブック内のシート取得
            dynamic sheets = m_xlWorbook.Worksheets;
            foreach (dynamic xlWorkSheet in sheets)
            {
                MyWorksheet sheet = new MyWorksheet(xlWorkSheet);
                m_WorkSheetList.Add(sheet);
            }
            Marshal.ReleaseComObject((object)sheets);
        }
        // シート取得(見つからない場合null)
        public MyWorksheet GetSheet(string name)
        {
            return m_WorkSheetList.Find(sheet => sheet.Name == name);
        }
        // 上書き保存
        public void Save()
        {
            m_xlWorbook.Save();
        }
        // ワークブックを閉じる
        public void Close()
        {
            // シートの後始末
            foreach (MyWorksheet sheet in m_WorkSheetList)
            {
                sheet.Close();
            }
            // ワークブック
            if (m_xlWorbook != null)
            {
                m_xlWorbook.Close();
                Marshal.ReleaseComObject((object)m_xlWorbook);
                m_xlWorbook = null;
            }
            // ワークブックス
            if (m_xlWorkbooks != null)
            {
                m_xlWorkbooks.Close();
                Marshal.ReleaseComObject((object)m_xlWorkbooks);
                m_xlWorkbooks = null;
            }
            // アプリケーション
            if (m_xlApp != null)
            {
                m_xlApp.Quit();
                Marshal.ReleaseComObject((object)m_xlApp);
                m_xlApp = null;
            }

            // ガベージコレクタを強制的に実行
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }
    // ワークシート
    class MyWorksheet
    {
        dynamic m_xlWorkSheet;
        // シート名
        public string Name
        {
            get { return m_xlWorkSheet.Name; }
        }
        // コンストラクタ
        public MyWorksheet(dynamic xlWorkSheet)
        {
            m_xlWorkSheet = xlWorkSheet;
        }
        // クローズ
        public void Close()
        {
            Marshal.ReleaseComObject((object)m_xlWorkSheet);
        }
        // 値設定
        public void SetValue<T>(int row, int col, T value)
        {
            dynamic cells = m_xlWorkSheet.Cells;
            dynamic range = cells[row, col];
            range.Value = value.ToString();
            Marshal.ReleaseComObject((object)range);
            Marshal.ReleaseComObject((object)cells);
        }
        // 値取得
        public T GetValue<T>(int row, int col)
        {
            dynamic cells = m_xlWorkSheet.Cells;
            dynamic range = cells[row, col];
            string value = range.Value != null ? range.Value.ToString() : "";
            Marshal.ReleaseComObject((object)range);
            Marshal.ReleaseComObject((object)cells);

            // 数値の場合は空文字列をゼロにする
            if (typeof(T) != typeof(string))
            {
                value = string.IsNullOrWhiteSpace(value) ? "0" : value;
            }
            return (T)Convert.ChangeType(value, typeof(T));
        }
        // 色設定
        public void SetColor(int row, int col, Color color)
        {
            dynamic cells = m_xlWorkSheet.Cells;
            dynamic range = cells[row, col];
            dynamic interior = range.Interior;
            interior.Color = ColorTranslator.ToOle(color);
            Marshal.ReleaseComObject((object)interior);
            Marshal.ReleaseComObject((object)range);
            Marshal.ReleaseComObject((object)cells);
        }
        // 色取得
        public Color GetColor(int row, int col)
        {
            dynamic cells = m_xlWorkSheet.Cells;
            dynamic range = cells[row, col];
            dynamic interior = range.Interior;
            Color color = ColorTranslator.FromOle(interior.Color);
            Marshal.ReleaseComObject((object)interior);
            Marshal.ReleaseComObject((object)range);
            Marshal.ReleaseComObject((object)cells);
            return color;
        }
        // フォーマット設定
        public void SetFormat(int row, int col, string format)
        {
            dynamic cells = m_xlWorkSheet.Cells;
            dynamic range = cells[row, col];
            range.NumberFormatLocal = format;
            Marshal.ReleaseComObject((object)range);
            Marshal.ReleaseComObject((object)cells);
        }
        // フォーマット取得
        public string GetFormat(int row, int col)
        {
            dynamic cells = m_xlWorkSheet.Cells;
            dynamic range = cells[row, col];
            string format = range.NumberFormatLocal.ToString();
            Marshal.ReleaseComObject((object)range);
            Marshal.ReleaseComObject((object)cells);
            return format;
        }
    }
}

COMオブジェクト関連の内容は、以下の記事を参照してください。

本記事では、遅延バインディング(Late Binding)についての情報を記載します。

遅延バインディングにおけるCOMオブジェクトの解放

早期バインディング(Early Binding)に対して、遅延バインディングではCOMオブジェクト解放処理に、object型へのキャストを追加しています。

Marshal.ReleaseComObject((object)interior);

この理由は、dynamic型に格納したCOMオブジェクトをMarshal.ReleaseComObjectで解放する際、早期バインディング(Early Binding)に対して、アセンブリ言語レベルで追加の命令が出力され、これが例外を引き起こしていた経験からです。

詳細は、下記リンク記事を参照ください。

注意点

  • 本記事のサンプルコードを使用する場合、お使いのPCにExcelがインストールされている必要があります。
  • 本記事のサンプルコードは、Microsoft Office Interop ExcelによるExcelファイル操作に焦点をあてるため、それ以外の部分の実装は簡略化しています。実際にアプリケーションに組み込む際は、各種の例外処理を実装する必要があります。
  • COMオブジェクトの解放漏れがあると、プログラム上でExcelファイルを閉じたとしても、Excelプロセスが生き残り続けます。このような現象を防ぐため、Disposeパターンなどを取り入れ、リソースの解放漏れを防いでください。
  • 遅延バインディングは便利な反面、いくつかのデメリットも存在します。コンパイル時の型チェックが行われないため、存在しないメソッドやプロパティへのアクセスによって実行時エラーが発生するリスクがあります。また、早期バインディングに比べて実行速度が若干遅くなる可能性があります。

まとめ

Microsoft Office Interop Excelを使用する主な利点は、Excelアプリケーションとの高い互換性と、高度な機能へのアクセス可能性にあります。さらに、遅延バインディング(Late Binding)を使用することで、Officeのバージョンに依存しない実装が可能です。

関連記事

タイトルとURLをコピーしました