Arbejde med Excel i .NET uafhængigt af den installerede version af Excel

Side opdateret :
Dato for oprettelse af side :

Driftsmiljø

Visual Studio
  • Visual Studio 2022
.NET
  • .NET 6.0
Windows
  • Windows 7
  • Windows 11
Excel
  • Microsoft 365
  • Office 2007

Forudsætninger

Windows
  • En af versionerne
Excel
  • En af versionerne

Problemer med biblioteker, der fungerer sammen med Excel

En måde at programmere interagere med Excel på er Microsoft.Office.Interop.Excel at henvise til . Dette svarer til at arbejde direkte med Excel-applikationen via COM, så det er lidt anderledes end at arbejde med data i en Excel-fil. Som en forudsætning skal Excel installeres i det miljø, der skal udføres, men mange funktioner i Excel er tilgængelige fra programsiden.

Microsoft.Office.Interop.Excel Hvis du bruger programmet, vil du tro, at programmet vil blive skrevet på følgende måde.

// 実行プログラムの場所にある 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 er besværligt at skulle frigive ressourcer hver gang, men da du kan bruge de forberedte klasser, kan du udføre Excel-behandling relativt intuitivt.

Ulempen er imidlertid, at den version af biblioteket, der henvises til af programmet, skal matche eller være kompatibel med den version af Excel, der er installeret i det miljø, hvor det kører. For eksempel kan et program, der er oprettet ved at referere til det interne version 15.0 (2013) bibliotek i Excel, kun køre, når Excel 2013 er installeret. Hvis programmet skal køre i mange miljøer, skal du muligvis lade Excel-installationen køre på en ensartet version.

Hvis en sådan operation er mulig, er det fint, men hvis den installerede version af Excel er anderledes, kan den ikke rummes. Derfor er det nødvendigt at gøre noget på programsiden.

Referere til biblioteker dynamisk med dynamisk i stedet for direkte at referere til dem

Problemet i dette tilfælde opstår, fordi biblioteksversionen refereres til rettet, når programmet oprettes. Det betyder, at i stedet for at bestemme versionen på tidspunktet for programoprettelsen, er det nødvendigt at bestemme versionen på tidspunktet for programudførelsen.

Der er flere måder at gøre det på, men denne gang løser vi det ved hjælp af "" "" og "Type.GetTypeFromProgIDActivator.CreateInstance".dynamic

Normalt er det i C # nødvendigt at bestemme typeoplysningerne på tidspunktet for programoprettelsen, men hvis typen er dynamic , kan typen bestemmes på udførelsestidspunktet. object I modsætning til typer kan du også kalde metoderne og egenskaberne for den pågældende klasse, hvis forekomsten, der er angivet til dynamic en variabel, er en klasse. Men da det, der går ind i variablen, bestemmes på udførelsestidspunktet, vil der opstå en fejl på udførelsestidspunktet, hvis der ikke er nogen specificeret metode.

Koden oprettet ved hjælp af disse er som følger.

// 実行プログラムの場所にある 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 af erklæringen af variablen er , og Excel.Application oprettelsen af er dynamic lidt anderledes, men resten af koden genbruges næsten.

Da ovenstående kode ikke refererer til Excel-biblioteket, er det ikke muligt at bruge Excel-relaterede klasseoplysninger. Derfor er typen af variablen alle dynamic . Da vi ikke kan referere direkte til klassen,Excel.Application får vi i stedet Excel.Application Type.GetTypeFromProgID Typeoplysningerne Activator.CreateInstance ind og sender dem til for at oprette Excel.Application en forekomst af . Forresten, i et miljø, hvor Excel ikke er installeret, kan du ikke få det ind Type , så det er muligt at isolere, Type.GetTypeFromProgID om det er installeret eller ej.

dynamic Ulempen ved at bruge er, at klassen ikke kan bestemmes, når programmet bygges. Intellisense, som er en fuldførelsesfunktion ved indtastning af kode, viser ikke metodenavne og egenskabsnavne. Det kan være mindre besværligt at skrive kode med henvisning til biblioteket i begyndelsen og erstatte det med dynamic senere.

Når du kører i et Microsoft 365-miljø, kan du bekræfte, at det afspejles som følger.

Følgende er resultatet af at køre det på en meget ældre version af Office 2007. Hvis du ikke bruger en funktion, der kun fungerer i en bestemt version, kan du få den til at fungere ret bredt.

Forresten, hvis du kører det i et miljø, hvor Excel ikke er installeret, kan du korrekt bestemme, at det ikke kan behandles.