複数のExcelのバージョンに対応する ~遅延バインディング①~

はじめに

業務用のシステムを担当したことがある人は利用者からExcelを利用したいという要望に出会ったことがあることが多いのではないかと思います。
その際にVisualStudioで作業する場合は、COMコンポーネントの参照追加で”Microsoft Excel *.* Object Library”を追加。
※*.*は利用したいExcelのバージョンによって異なります。
そしてリリースする際にdllを配布するというのが方法として出てくると思います。

しかし、開発環境と利用者でインストールされているバージョンが異なったり、複数の利用者同士でインストールされているバージョンが異なったりすると、dllを配布するだけではうまくいかないといったことが発生したことがないでしょうか?

本来は少なくとも複数の利用者間ではバージョンを統一してもらえばよいのですが、バージョンアップにかかる費用、利用者の抵抗などによって統一が出来なかったときの回避策として遅延バインディングを利用する方法があります。

遅延バインディングとは

細かな解説は他のサイトを参照していただけたらと思いますが、
私個人のイメージとしては、利用したいアプリケーションを操作してプロパティを取得したり、設定したりといった感じかと思っています。
※利用しなくていいなら、利用しないのが最善だと思いますが。。。
※速度面については参照設定でやるよりも遅くなると思いますが、どれほど遅くなるかは検証していません。

ソースコード(読み取り)

次のような環境で実行しています。
・Visual Studio 2019
・Excel 2019

次のようなファイルを読み込ませています。

まずはヘッダに以下を追加します。

using System.Runtime.InteropServices;
using System.Reflection;
using System.Collections;

メインソースコード
※① 1シート目の値を3カ所読み取り
 ② “Sheet2″シートの値を1カ所読み取り
 ③MessageBoxで読み取った値を表示

Type typProgID;
object oExcel;
object oWBooks;
object oWBook;
object oWSheets;
object oSheet;
object oCells;
object oCell;
object oValue;

string FilePath = @"C:\tmp\Excel遅延バインド.xlsx";  //読み込みたいファイルのパス

object[] Params;    //いろいろな場所で使うが、一時的なのでパラメータとして宣言しておく

string result = ""; //読み取り結果表示用

//Excelを利用する
typProgID = Type.GetTypeFromProgID("Excel.Application");
oExcel = Activator.CreateInstance(typProgID);

oWBooks = oExcel.GetType().InvokeMember("Workbooks", BindingFlags.GetProperty, null, oExcel, null);

//ファイルパスを配列の頭に指定する。何故これだけの配列にするかは調べて下さい。
Params = new object[15] {FilePath, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                     Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                     Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing };

//ファイルを開いて、シートを開く準備
oWBook = oWBooks.GetType().InvokeMember("Open", BindingFlags.GetProperty, null, oWBooks, Params);
oWSheets = oWBook.GetType().InvokeMember("Worksheets", BindingFlags.GetProperty, null, oWBook, null);

//シートの番号を指定してシートを開いて、セルを読み込む準備 ※シート番号は0からではなく、1から
Params = new object[1] { 1 };   //1シート目を指定
oSheet = oWSheets.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, oWSheets, Params);
oCells = oSheet.GetType().InvokeMember("Cells", BindingFlags.GetProperty, null, oSheet, null);

//セルを指定して、値を取得する ※セルの番号は"R1C1"形式で指定する
//セルA1の値を取得
Params = new object[2];
Params[0] = 1;  //行番号
Params[1] = 1;  //列番号
oCell = oCells.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, oCells, Params);
oValue = oCell.GetType().InvokeMember("Text", BindingFlags.GetProperty, null, oCell, null);

result = "1シート目" + Environment.NewLine;
result += "A1セルの値:" + oValue.ToString() + Environment.NewLine;

//セルB2の値を取得する
Params[0] = 2;  //2行
Params[1] = 2;  //B列
oCell = oCells.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, oCells, Params);
oValue = oCell.GetType().InvokeMember("Text", BindingFlags.GetProperty, null, oCell, null);

result += "B2セルの値:" + oValue.ToString() + Environment.NewLine;

//行番号を文字列で指定してセルC2の値を取得する
int col = Convert.ToInt32(Convert.ToChar("C")) - 64;  //ASCIIコードから算出する ※ASCIIコードで"A"は65
Params[0] = 2;
Params[1] = col;    //3
oCell = oCells.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, oCells, Params);
oValue = oCell.GetType().InvokeMember("Text", BindingFlags.GetProperty, null, oCell, null);

result += "C2セルの値:" + oValue.ToString() + Environment.NewLine;

//読み込むシートを変更
//シート名を指定して読み込む
string SheetName = "Sheet2";
object Name;
int SheetNo = 0;
//各シートのシート名を比較して対象を探す
foreach (object sheet in (IEnumerable)oWSheets)
{
    SheetNo++;
    Name = sheet.GetType().InvokeMember("Name", BindingFlags.GetProperty, null, sheet, null);
    if (SheetName == Name.ToString())
    {
        break;
    }
}

Params = new object[1] { SheetNo };   //2シート目を指定
oSheet = oWSheets.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, oWSheets, Params);
oCells = oSheet.GetType().InvokeMember("Cells", BindingFlags.GetProperty, null, oSheet, null);

result += "Sheet2" + Environment.NewLine;

//1行目、1列目の値を取得する
Params = new object[2];
Params[0] = 1;
Params[1] = 1;
oCell = oCells.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, oCells, Params);
oValue = oCell.GetType().InvokeMember("Text", BindingFlags.GetProperty, null, oCell, null);

result += "A1セルの値:" + oValue.ToString() + Environment.NewLine;

//最終処理
//これを忘れると、プロセス上にいつまでもExcelのプロセスが残ります
Params = new object[2];
Params[0] = false;      //保存するかどうかを設定します。 true:保存、false:保存しない
Params[1] = FilePath;
oWBook.GetType().InvokeMember("Close", BindingFlags.InvokeMethod, null, oWBook, Params);      //WorkBookのClose
oWBooks.GetType().InvokeMember("Close", BindingFlags.InvokeMethod, null, oWBooks, null);    //WorkBooksのClose
oExcel.GetType().InvokeMember("Quit", BindingFlags.InvokeMethod, null, oExcel, null);       //Excel.ApplicationのClose

//使ったものを解放
Marshal.ReleaseComObject(oCell);
oCell = null;
Marshal.ReleaseComObject(oCells);
oCells = null;
Marshal.ReleaseComObject(oSheet);
oSheet = null;
Marshal.ReleaseComObject(oWSheets);
oWSheets = null;
Marshal.ReleaseComObject(oWBook);
oWBook = null;
Marshal.ReleaseComObject(oWBooks);
oWBooks = null;
Marshal.ReleaseComObject(oExcel);
oExcel = null;

GC.Collect();

MessageBox.Show(result);

※このソースコードはご自由にご利用していただいて問題ありませんが、各自の責任でお願いします。問題や不利益等が発生したとしても当サイトは関知しません。

結果

読み込みだけでしたら、これくらいできれば大体は事足りるかと思います。
遅延バインディングを利用した書き込みについては次に書きたいと思います。

書き込み編へ >

 複数のExcelのバージョンに対応する ~遅延バインディング①~ このページ
 複数のExcelのバージョンに対応する ~遅延バインディング②~
 複数のExcelのバージョンに対応する ~遅延バインディング③~
 複数のExcelのバージョンに対応する ~遅延バインディングまとめ~

投稿日:2020年1月21日 更新日:

執筆者:

このサイトの管理者について

1980年代生まれ
石川県在住

メーカー2社経験後に退社

1年程度自由に在職中はできなかったことにチャレンジ

その後コロナの影響でなかなか仕事が見つかりませんでしたが、無事就職

現在はシステム会社に勤務中

PAGE TOP