Робота з 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.GetTypeFromProgID
Activator.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
.
Крім того, оскільки ми не можемо посилатися на клас безпосередньо,Excel.Application
замість цього Excel.Application
Type.GetTypeFromProgID
ми отримуємо інформацію Activator.CreateInstance
Type і передаємо її для створення Excel.Application
екземпляра .
До речі, в середовищі, де Excel не встановлений, його Type
не можна потрапити, тому можна ізолювати, Type.GetTypeFromProgID
встановлено він чи ні.
dynamic
Недоліком використання є те, що клас не може бути визначений при побудові програми.
Intellisense, який є функцією доповнення при введенні коду, не відображає імена методів та імена властивостей.
Може бути менш громіздким писати код з посиланням на бібліотеку на початку і замінювати його на dynamic
пізніший.
Під час роботи в середовищі Microsoft 365 ви можете підтвердити, що це відображається наступним чином.
Нижче наведено результат його запуску на набагато старішій версії Office 2007. Якщо ви не використовуєте функцію, яка працює лише в певній версії, ви можете змусити її працювати досить широко.
До речі, якщо запустити його в середовищі, де не встановлено Excel, можна правильно визначити, що він не підлягає обробці.