Работа с Excel в .NET независимо от инсталираната версия на Excel

Страницата се актуализира :
Дата на създаване на страница :

Работна среда

Визуално студио
  • Визуално студио 2022
.НЕТЕН
  • .НЕТ 6.0
Уиндоус
  • Прозорци 7
  • Прозорци 11
Превъзхождам
  • Майкрософт 365
  • Офис 2007

Предпоставки

Уиндоус
  • Една от версиите
Превъзхождам
  • Една от версиите

Проблеми с библиотеки, които работят с 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, инсталирана в средата, в която се изпълнява. Например програма, създадена чрез препращане към вътрешната библиотека на версия 15.0 (2013) на Excel, може да се изпълнява само когато е инсталиран Excel 2013. Ако искате вашата програма да се изпълнява в много среди, може да се наложи да поддържате инсталацията на Excel работеща на съгласувана версия.

Ако такава операция е възможна, това е добре, но ако инсталираната версия на Excel е различна, тя не може да бъде настанена. Ето защо е необходимо да се направи нещо от страна на програмата.

Динамично препращане към библиотеки с динамични, вместо директно препращане към тях

Проблемът в този случай възниква, защото версията на библиотеката е фиксирана при създаването на програмата. Това означава, че вместо да се определя версията по време на създаването на програмата, е необходимо да се определи версията по време на изпълнението на програмата.

Има няколко начина да го направим, но този път ще го решим с помощта на "" "" и "Type.GetTypeFromProgIDActivator.CreateInstance".dynamic

Обикновено в 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 . Също така, тъй като не можем да се позоваваме директно на класа, вместо това Type.GetTypeFromProgID Excel.Application получаваме информацията Activator.CreateInstance за Type в и я предаваме,Excel.Application за да създадем Excel.Application екземпляр на . Между другото, в среда, в която Excel не е инсталиран, не можете да го получите в Type , Type.GetTypeFromProgID така че е възможно да се изолира дали е инсталиран или не.

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

Когато работите в среда на Microsoft 365, можете да потвърдите, че тя е отразена по следния начин.

Следното е резултат от изпълнението му на много по-стара версия на Office 2007. Ако не използвате функция, която работи само в определена версия, можете да я накарате да работи доста широко.

Между другото, ако го изпълните в среда, в която Excel не е инсталиран, можете правилно да определите, че не може да бъде обработен.