Arbeta med Excel i .NET oberoende av den installerade versionen av Excel

Sidan uppdaterad :
Datum för skapande av sida :

Omvärld

Visuell studio
  • Visual Studio 2022
.NÄT
  • .NET 6.0
Windows
  • Windows 7 (på engelska)
  • Windows 11 (på engelska)
Överträffa
  • Microsoft 365 (på engelska)
  • Kontor 2007

Förutsättningar

Windows
  • En av versionerna
Överträffa
  • En av versionerna

Problem med bibliotek som fungerar med Excel

Ett sätt att programmatiskt interagera med Excel är Microsoft.Office.Interop.Excel att referera till . Detta liknar att arbeta direkt med Excel-programmet via COM, så det skiljer sig något från att arbeta med data i en Excel-fil. Som en förutsättning måste Excel installeras i miljön för att kunna köras, men många funktioner i Excel är tillgängliga från programsidan.

Microsoft.Office.Interop.Excel Om du använder programmet kommer du att tro att programmet kommer att skrivas på följande sätt.

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

Det är besvärligt att behöva frigöra resurser varje gång, men eftersom du kan använda de förberedda klasserna kan du utföra Excel-bearbetning relativt intuitivt.

Nackdelen är dock att den version av biblioteket som programmet refererar till måste matcha eller vara kompatibel med den version av Excel som är installerad i den miljö där det körs. Ett program som skapats genom att referera till det interna biblioteket för version 15.0 (2013) i Excel kan till exempel bara köras när Excel 2013 är installerat. Om du vill att programmet ska köras i många miljöer kan du behöva hålla Excel-installationen igång på en konsekvent version.

Om en sådan operation är möjlig är det bra, men om den installerade versionen av Excel är annorlunda kan den inte tillgodoses. Därför är det nödvändigt att göra något på programsidan.

Referera till bibliotek dynamiskt med dynamic i stället för att referera direkt till dem

Problemet i det här fallet beror på att biblioteksversionen refereras till fast när programmet skapas. Detta innebär att i stället för att fastställa versionen när programmet skapas är det nödvändigt att fastställa versionen när programmet körs.

Det finns flera sätt att göra det, men den här gången kommer vi att lösa det med "" "" och "".Type.GetTypeFromProgIDActivator.CreateInstancedynamic

Normalt, i C#, är det nödvändigt att fastställa typinformationen när programmet skapas, men om typen är dynamic kan typen bestämmas vid tidpunkten för körningen. object Till skillnad från dynamic typer, om instansen som är inställd på en variabel är en klass, kan du också anropa metoderna och egenskaperna för den klassen. Men eftersom vad som går in i variabeln bestäms vid tidpunkten för körningen, kommer ett fel att uppstå vid körningstillfället om det inte finns någon specificerad metod.

Koden som skapas med hjälp av dessa är följande.

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

Typen av deklaration av variabeln är , och Excel.Application skapandet av är dynamic något annorlunda, men resten av koden återanvänds nästan.

Eftersom ovanstående kod inte refererar till Excel-biblioteket är det inte möjligt att använda målinformationen för Excel-relaterad klass. Därför är variabelns typ all dynamic . Dessutom, eftersom vi inte kan referera till klassen direkt,Excel.Application hämtar vi istället Excel.Application Type.GetTypeFromProgID in typinformationen Activator.CreateInstance och skickar den till för att skapa Excel.Application en instans av . Förresten, i en miljö där Excel inte är installerat kan du inte få in det , Type så det är möjligt att isolera om det är installerat Type.GetTypeFromProgID eller inte.

dynamic Nackdelen med att använda är att klassen inte kan bestämmas när programmet byggs. IntelliSense, som är en kompletteringsfunktion när du anger kod, visar inte metodnamn och egenskapsnamn. Det kan vara mindre besvärligt att skriva kod med hänvisning till biblioteket i början och ersätta det med dynamic senare.

När du kör i en Microsoft 365-miljö kan du bekräfta att den återspeglas på följande sätt.

Följande är resultatet av att köra det på en mycket äldre version av Office 2007. Om du inte använder en funktion som bara fungerar i en specifik version kan du få den att fungera ganska brett.

Förresten, om du kör den i en miljö där Excel inte är installerat kan du korrekt bestämma att den inte kan bearbetas.