Работа с Excel в .NET независимо от установленной версии Excel
Условия эксплуатации
- Визуальная студия
-
- Visual Studio 2022
- .СЕТЬ
-
- .NET 6.0
- Виндоус
-
- Windows 7
- Windows 11
- Превосходить
-
- Microsoft 365
- Office 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, установленной в среде, в которой она выполняется. Например, программа, созданная со ссылкой на внутреннюю библиотеку Excel версии 15.0 (2013), может запускаться только в том случае, если установлен Excel 2013. Если вы хотите, чтобы ваша программа работала в нескольких средах, вам может потребоваться поддерживать установку Excel в согласованной версии.
Если такая операция возможна, это нормально, но если установленная версия Excel другая, ее нельзя приспособить. Поэтому надо что-то делать на стороне программы.
Динамическая ссылка на библиотеки с помощью dynamic вместо прямой ссылки на них
Проблема в этом случае возникает из-за того, что при создании программы ссылка на версию библиотеки фиксируется. Это означает, что вместо того, чтобы определять версию на момент создания программы, необходимо определить версию на момент выполнения программы.
Сделать это можно несколькими способами, но в этот раз мы решим ее с помощью """ и ""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);
}
Тип объявления переменной — dynamic
, и Excel.Application
создание переменной немного отличается, но остальной код используется практически повторно.
Поскольку приведенный выше код не ссылается на библиотеку Excel, невозможно использовать сведения о целевом классе, связанные с Excel. Таким образом, тип переменной — all dynamic
.
Кроме того, поскольку мы не можем ссылаться на класс напрямую,Excel.Application
вместо этого Excel.Application
Type.GetTypeFromProgID
мы получаем информацию Activator.CreateInstance
о типе и передаем ее для создания Excel.Application
экземпляра .
Кстати, в среде, где Excel не установлен, вы не можете получить его в Type
, поэтому можно изолировать, Type.GetTypeFromProgID
установлен он или нет.
dynamic
Недостатком использования является то, что класс не может быть определен при сборке программы.
Intellisense, которая является функцией завершения при вводе кода, не отображает имена методов и свойств.
Возможно, будет менее громоздким написать код со ссылкой на библиотеку в начале и заменить ее dynamic
на более позднюю.
При работе в среде Microsoft 365 вы можете убедиться, что это отражается следующим образом.
Ниже приведен результат его запуска в гораздо более старой версии Office 2007. Если вы не используете функцию, которая работает только в определенной версии, вы можете заставить ее работать довольно широко.
Кстати, если запустить его в среде, где не установлен Excel, можно правильно определить, что он не поддается обработке.