Werken met Excel in .NET onafhankelijk van de geïnstalleerde versie van Excel

Pagina bijgewerkt :
Aanmaakdatum van pagina :

Werkomgeving

Visual Studio
  • Visual Studio 2022
.NET
  • .NET 6.0
Ramen
  • Vensters 7
  • Vensters 11
Uitblinken
  • Microsoft 365
  • Office 2007

Voorwaarden

Ramen
  • Een van de versies
Uitblinken
  • Een van de versies

Problemen met bibliotheken die werken met Excel

Een manier om programmatisch met Excel te werken, is Microsoft.Office.Interop.Excel door te verwijzen naar . Dit is vergelijkbaar met het direct werken met de Excel-applicatie via COM, dus het is iets anders dan het werken met gegevens in een Excel-bestand. Als uitgangspunt moet Excel worden geïnstalleerd in de uit te voeren omgeving, maar veel functies van Excel zijn beschikbaar vanaf de programmakant.

Microsoft.Office.Interop.Excel Als u het programma gebruikt, zult u denken dat het programma op de volgende manier zal worden geschreven.

// 実行プログラムの場所にある 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);
}

Het is lastig om elke keer bronnen vrij te geven, maar omdat je de voorbereide klassen kunt gebruiken, kun je Excel-verwerking relatief intuïtief uitvoeren.

Het nadeel is echter dat de versie van de bibliotheek waarnaar het programma verwijst, moet overeenkomen met of compatibel moet zijn met de versie van Excel die is geïnstalleerd in de omgeving waarin het wordt uitgevoerd. Een programma dat is gemaakt door te verwijzen naar de interne bibliotheek van versie 15.0 (2013) van Excel, kan bijvoorbeeld alleen worden uitgevoerd wanneer Excel 2013 is geïnstalleerd. Als u wilt dat uw programma in veel omgevingen wordt uitgevoerd, moet u uw Excel-installatie mogelijk op een consistente versie laten draaien.

Als een dergelijke bewerking mogelijk is, is het prima, maar als de geïnstalleerde versie van Excel anders is, kan deze niet worden ondergebracht. Daarom is het noodzakelijk om iets aan de programmakant te doen.

Dynamisch verwijzen naar bibliotheken met dynamisch in plaats van er rechtstreeks naar te verwijzen

Het probleem doet zich in dit geval voor omdat er naar de bibliotheekversie wordt verwezen wanneer het programma wordt gemaakt. Dit betekent dat in plaats van de versie te bepalen op het moment dat het programma wordt gemaakt, het noodzakelijk is om de versie te bepalen op het moment dat het programma wordt uitgevoerd.

Er zijn verschillende manieren om het te doen, maar deze keer zullen we het oplossen met behulp van "" "" en "Type.GetTypeFromProgIDActivator.CreateInstance".dynamic

Normaal gesproken is het in C# nodig om de type-informatie te bepalen op het moment dat het programma wordt gemaakt, maar als het type is dynamic , kan het type worden bepaald op het moment van uitvoering. object In tegenstelling tot dynamic typen kunt u, als de instantie die is ingesteld op een variabele een klasse is, ook de methoden en eigenschappen van die klasse aanroepen. Aangezien echter wordt bepaald wat er in de variabele wordt opgenomen op het moment van uitvoering, zal er een fout optreden tijdens de uitvoering als er geen gespecificeerde methode is.

De code die hiermee wordt gemaakt, is als volgt.

// 実行プログラムの場所にある 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);
}

Het type declaratie van de variabele is , en Excel.Application de creatie van is dynamic iets anders, maar de rest van de code wordt bijna hergebruikt.

Aangezien de bovenstaande code niet verwijst naar de Excel-bibliotheek, is het niet mogelijk om de doel-Excel-gerelateerde klasse-informatie te gebruiken. Daarom is het type van de variabele alles dynamic . Omdat we niet rechtstreeks naar de klasse kunnen verwijzen,Excel.Application halen we in plaats daarvan Excel.Application Type.GetTypeFromProgID de Type-informatie Activator.CreateInstance binnen en geven we deze door aan om Excel.Application een instantie van . Trouwens, in een omgeving waar Excel niet is geïnstalleerd, kun je het er niet in Type krijgen , Type.GetTypeFromProgID dus het is mogelijk om te isoleren of het is geïnstalleerd of niet.

dynamic Het nadeel van het gebruik is dat de klasse niet kan worden bepaald bij het bouwen van het programma. Intellisense, dat een voltooiingsfunctie is bij het invoeren van code, geeft geen methodenamen en eigenschapsnamen weer. Het kan minder omslachtig zijn om in het begin code te schrijven met verwijzing naar de bibliotheek en deze later te dynamic vervangen.

Wanneer u in een Microsoft 365-omgeving wordt uitgevoerd, kunt u bevestigen dat dit als volgt wordt weergegeven.

Het volgende is het resultaat van het uitvoeren op een veel oudere versie van Office 2007. Als je geen functie gebruikt die alleen in een specifieke versie werkt, kun je deze vrij breed laten werken.

Trouwens, als je het uitvoert in een omgeving waar Excel niet is geïnstalleerd, kun je correct vaststellen dat het niet kan worden verwerkt.