Робота з Excel в .NET незалежно від встановленої версії Excel

Сторінка оновлюється :
Дата створення сторінки :

Робоче середовище

Візуальна студія
  • Visual Studio 2022
.МЕРЕЖІ
  • .NET 6.0
Вікна
  • Вікна 7
  • Вікна 11
Excel
  • Microsoft 365
  • Office 2007

Передумови

Вікна
  • Одна з версій
Excel
  • Одна з версій

Проблеми з бібліотеками, які працюють з Excel

Одним із способів програмної взаємодії з Excel є Microsoft.Office.Interop.Excel посилання . Це схоже на роботу безпосередньо з додатком Excel через COM, тому дещо відрізняється від роботи з даними у файлі Excel. Як передумова, Excel повинен бути встановлений в середовищі, що виконується, але багато функцій Excel доступні з боку програми.

Microsoft.Office.Interop.Excel Якщо ви скористаєтеся програмою, то подумаєте, що програма буде написана наступним чином.

// 実行プログラムの場所にある Excel ファイル
var excelFilePath = $@"{Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule?.FileName)}\Sample.xlsx";

// Excel のオブジェクトは参照したら必ず解放する必要があります。
// 解放しないと Excel のプロセスが残り続けます。
Microsoft.Office.Interop.Excel.Application? excel = null;
Microsoft.Office.Interop.Excel.Workbooks? books = null;
Microsoft.Office.Interop.Excel.Workbook? book = null;
Microsoft.Office.Interop.Excel.Sheets? sheets = null;
Microsoft.Office.Interop.Excel.Worksheet? sheet = null;
Microsoft.Office.Interop.Excel.Range? cells = null;
Microsoft.Office.Interop.Excel.Range? range1 = null;
Microsoft.Office.Interop.Excel.Range? range2 = null;
try
{
  // Excel アプリケーションを生成(起動)します
  excel = new Microsoft.Office.Interop.Excel.Application
  {
    DisplayAlerts = false
  };

  // ワークブック一覧を参照します。参照する場合は必ず変数に保持します
  books = excel.Workbooks;

  // Excel ファイルを開きます
  book = books.Open(excelFilePath);

  // シート一覧を参照するので変数に保持します
  sheets = book.Worksheets;

  // シートを参照します。最初のシートは 1 になります
  sheet = (Microsoft.Office.Interop.Excel.Worksheet)sheets[1];

  // セル一覧を参照します
  cells = sheet.Cells;

  // 左上のセルを参照します。一番左上は [1, 1] になります
  range1 = (Microsoft.Office.Interop.Excel.Range)cells[1, 1];

  // セルの値を取得します。値の取得なので後で解放する必要はありません
  var value = (int)range1.Value;

  // 下のセルを参照します
  range2 = (Microsoft.Office.Interop.Excel.Range)cells[2, 1];

  // 日付分足してセットします
  range2.Value = DateTime.Now.Day + value;

  // 保存して閉じます
  book.Close(SaveChanges: true);

  Console.WriteLine("処理が完了しました。");
}
catch (Exception ex)
{
  // 閉じていなければ保存せずに閉じます
  // 開きっぱなしだと Excel 起動時に保存されていないデータとして表示される場合があります
  if (book != null) book.Close(SaveChanges: false);

  Console.WriteLine("処理が失敗しました。");
  Console.WriteLine(ex);
}
finally
{
  // 終了していなければ終了します
  if (excel != null) excel.Quit();

  // 例外が発生した場合でも必ずリソースを解放するようにします
  if (range1 != null) System.Runtime.InteropServices.Marshal.FinalReleaseComObject(range1);
  if (range2 != null) System.Runtime.InteropServices.Marshal.FinalReleaseComObject(range2);
  if (cells != null) System.Runtime.InteropServices.Marshal.FinalReleaseComObject(cells);
  if (sheet != null) System.Runtime.InteropServices.Marshal.FinalReleaseComObject(sheet);
  if (sheets != null) System.Runtime.InteropServices.Marshal.FinalReleaseComObject(sheets);
  if (book != null) System.Runtime.InteropServices.Marshal.FinalReleaseComObject(book);
  if (books != null) System.Runtime.InteropServices.Marshal.FinalReleaseComObject(books);
  if (excel != null) System.Runtime.InteropServices.Marshal.FinalReleaseComObject(excel);
}

Доводилося щоразу звільняти ресурси, але оскільки ви можете використовувати підготовлені класи, ви можете виконувати обробку Excel відносно інтуїтивно.

Недоліком, однак, є те, що версія бібліотеки, на яку посилається програма, повинна збігатися або бути сумісною з версією Excel, встановленою в середовищі, в якому вона запущена. Наприклад, програма, створена за посиланням на внутрішню бібліотеку Excel версії 15.0 (2013), може запускатися лише за інсталяції Excel 2013. Якщо ви хочете, щоб ваша програма працювала в багатьох середовищах, можливо, вам доведеться підтримувати інсталяцію Excel на узгодженій версії.

Якщо така операція можлива, це добре, але якщо встановлена версія Excel інша, її не можна вмістити. Тому треба щось робити на програмній стороні.

Динамічні посилання на бібліотеки з динамічними замість прямих посилань на них

Проблема в цьому випадку виникає через те, що посилання на версію бібліотеки виправляється під час створення програми. Це означає, що замість того, щоб визначати версію в момент створення програми, необхідно визначити версію в момент виконання програми.

Зробити це можна кількома способами, але цього разу ми вирішимо його за допомогою "" "" і "".Type.GetTypeFromProgIDActivator.CreateInstancedynamic

Зазвичай, у мові C# необхідно визначити інформацію про тип під час створення програми, але якщо тип , dynamic то тип може бути визначений під час виконання. object На відміну від dynamic типів, якщо екземпляр, встановлений у змінну, є класом, ви також можете викликати методи та властивості цього класу. Однак, оскільки те, що входить у змінну, визначається в момент виконання, помилка виникне під час виконання, якщо немає вказаного методу.

Код, створений з їх допомогою, виглядає наступним чином.

// 実行プログラムの場所にある Excel ファイル
var excelFilePath = $@"{Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule?.FileName)}\Sample.xlsx";

// Excel のオブジェクトは参照したら必ず解放する必要があります。
// 解放しないと Excel のプロセスが残り続けます。
dynamic? excel = null;
dynamic? books = null;
dynamic? book = null;
dynamic? sheets = null;
dynamic? sheet = null;
dynamic? cells = null;
dynamic? range1 = null;
dynamic? range2 = null;
try
{
  // Excel.Application の Type を取得
  var type = Type.GetTypeFromProgID("Excel.Application");
  if (type == null)
  {
    Console.WriteLine("Excel がインストールされていません。");
    return;
  }

  // Excel アプリケーションを生成(起動)します
  excel = Activator.CreateInstance(type);
  if (excel == null)
  {
    Console.WriteLine("Excel.Application のインスタンスの生成に失敗しました。");
    return;
  }
  excel.DisplayAlerts = false;

  // ワークブック一覧を参照します。参照する場合は必ず変数に保持します
  books = excel.Workbooks;

  // Excel ファイルを開きます
  book = books.Open(excelFilePath);

  // シート一覧を参照するので変数に保持します
  sheets = book.Worksheets;

  // シートを参照します。最初のシートは 1 になります
  sheet = (Microsoft.Office.Interop.Excel.Worksheet)sheets[1];

  // セル一覧を参照します
  cells = sheet.Cells;

  // 左上のセルを参照します。一番左上は [1, 1] になります
  range1 = (Microsoft.Office.Interop.Excel.Range)cells[1, 1];

  // セルの値を取得します。値の取得なので後で解放する必要はありません
  var value = (int)range1.Value;

  // 下のセルを参照します
  range2 = (Microsoft.Office.Interop.Excel.Range)cells[2, 1];

  // 日付分足してセットします
  range2.Value = DateTime.Now.Day + value;

  // 保存して閉じます
  book.Close(SaveChanges: true);

  Console.WriteLine("処理が完了しました。");
}
catch (Exception ex)
{
  // 閉じていなければ保存せずに閉じます
  // 開きっぱなしだと Excel 起動時に保存されていないデータとして表示される場合があります
  if (book != null) book.Close(SaveChanges: false);

  Console.WriteLine("処理が失敗しました。");
  Console.WriteLine(ex);
}
finally
{
  // 終了していなければ終了します
  if (excel != null) excel.Quit();

  // 例外が発生した場合でも必ずリソースを解放するようにします
  if (range1 != null) System.Runtime.InteropServices.Marshal.FinalReleaseComObject(range1);
  if (range2 != null) System.Runtime.InteropServices.Marshal.FinalReleaseComObject(range2);
  if (cells != null) System.Runtime.InteropServices.Marshal.FinalReleaseComObject(cells);
  if (sheet != null) System.Runtime.InteropServices.Marshal.FinalReleaseComObject(sheet);
  if (sheets != null) System.Runtime.InteropServices.Marshal.FinalReleaseComObject(sheets);
  if (book != null) System.Runtime.InteropServices.Marshal.FinalReleaseComObject(book);
  if (books != null) System.Runtime.InteropServices.Marshal.FinalReleaseComObject(books);
  if (excel != null) System.Runtime.InteropServices.Marshal.FinalReleaseComObject(excel);
}

Тип оголошення змінної , а Excel.Application створення трохи відрізняється, dynamic але решта коду використовується майже повторно.

Оскільки наведений вище код не посилається на бібліотеку Excel, неможливо використовувати цільову інформацію про клас, пов'язану з Excel. Отже, тип змінної - все dynamic . Крім того, оскільки ми не можемо посилатися на клас безпосередньо,Excel.Application замість цього Excel.Application Type.GetTypeFromProgID ми отримуємо інформацію Activator.CreateInstance Type і передаємо її для створення Excel.Application екземпляра . До речі, в середовищі, де Excel не встановлений, його Type не можна потрапити, тому можна ізолювати, Type.GetTypeFromProgID встановлено він чи ні.

dynamic Недоліком використання є те, що клас не може бути визначений при побудові програми. Intellisense, який є функцією доповнення при введенні коду, не відображає імена методів та імена властивостей. Може бути менш громіздким писати код з посиланням на бібліотеку на початку і замінювати його на dynamic пізніший.

Під час роботи в середовищі Microsoft 365 ви можете підтвердити, що це відображається наступним чином.

Нижче наведено результат його запуску на набагато старішій версії Office 2007. Якщо ви не використовуєте функцію, яка працює лише в певній версії, ви можете змусити її працювати досить широко.

До речі, якщо запустити його в середовищі, де не встановлено Excel, можна правильно визначити, що він не підлягає обробці.