Usare Excel in .NET indipendentemente dalla versione installata di Excel

Pagina aggiornata :
Data di creazione della pagina :

Ambiente operativo

Visual Studio
  • Visual Studio 2022
.RETE
  • .NET 6.0
Finestre
  • finestre 7
  • finestre 11
Eccellere
  • Microsoft 365
  • Office 2007

Prerequisiti

Finestre
  • Una delle versioni
Eccellere
  • Una delle versioni

Problemi con le librerie che funzionano con Excel

Un modo per interagire a livello di codice con Excel consiste nel Microsoft.Office.Interop.Excel fare riferimento a . Questa operazione è simile all'utilizzo diretto dell'applicazione Excel tramite COM, quindi è leggermente diverso dall'utilizzo dei dati in un file Excel. Come premessa, Excel deve essere installato nell'ambiente per essere eseguito, ma molte funzioni di Excel sono disponibili dal lato del programma.

Microsoft.Office.Interop.Excel Se si utilizza il programma, si penserà che il programma sarà scritto nel modo seguente.

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

È problematico dover rilasciare risorse ogni volta, ma poiché è possibile utilizzare le classi preparate, è possibile eseguire l'elaborazione di Excel in modo relativamente intuitivo.

Lo svantaggio, tuttavia, è che la versione della libreria a cui fa riferimento il programma deve corrispondere o essere compatibile con la versione di Excel installata nell'ambiente in cui è in esecuzione. Ad esempio, un programma creato facendo riferimento alla libreria interna versione 15.0 (2013) di Excel può essere eseguito solo quando Excel 2013 è installato. Se si desidera che il programma venga eseguito in molti ambienti, potrebbe essere necessario mantenere l'installazione di Excel in esecuzione su una versione coerente.

Se tale operazione è possibile, va bene, ma se la versione installata di Excel è diversa, non può essere adattata. Pertanto, è necessario fare qualcosa sul lato del programma.

Fare riferimento alle librerie in modo dinamico con dynamic invece di fare riferimento direttamente ad esse

Il problema in questo caso si verifica perché la versione della libreria viene referenziata in modo fisso al momento della creazione del programma. Ciò significa che invece di determinare la versione al momento della creazione del programma, è necessario determinare la versione al momento dell'esecuzione del programma.

Ci sono diversi modi per farlo, ma questa volta lo risolveremo usando "" "" e "Type.GetTypeFromProgIDActivator.CreateInstance".dynamic

In genere, in C#, è necessario determinare le informazioni sul tipo al momento della creazione del programma, ma se il tipo è dynamic , il tipo può essere determinato al momento dell'esecuzione. object A differenza dynamic dei tipi, se l'istanza impostata su una variabile è una classe, è anche possibile chiamare i metodi e le proprietà di tale classe. Tuttavia, poiché ciò che entra nella variabile è determinato al momento dell'esecuzione, si verificherà un errore al momento dell'esecuzione se non è presente alcun metodo specificato.

Il codice creato con questi è il seguente.

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

Il tipo di dichiarazione della variabile è , e Excel.Application la creazione di è leggermente diversa, ma il resto del codice è dynamic quasi riutilizzato.

Poiché il codice precedente non fa riferimento alla libreria di Excel, non è possibile utilizzare le informazioni sulla classe di Excel di destinazione. Pertanto, il tipo della variabile è all dynamic . Inoltre, poiché non è possibile fare riferimento direttamente alla classe,Excel.Application si ottengono le Excel.Application Type.GetTypeFromProgID informazioni Activator.CreateInstance Type e le si passa a per creare Excel.Application un'istanza di . A proposito, in un ambiente in cui Excel non è installato, non è possibile ottenerlo in Type , Type.GetTypeFromProgID quindi è possibile isolare se è installato o meno.

dynamic Lo svantaggio dell'utilizzo è che la classe non può essere determinata durante la compilazione del programma. IntelliSense, che è una funzione di completamento durante l'immissione di codice, non visualizza i nomi dei metodi e i nomi delle proprietà. Potrebbe essere meno complicato scrivere codice con riferimento alla libreria all'inizio e sostituirlo con dynamic successivo.

Quando si esegue in un ambiente Microsoft 365, è possibile verificare che si rifletta come segue.

Di seguito è riportato il risultato dell'esecuzione in una versione molto precedente di Office 2007. Se non si utilizza una funzione che funziona solo in una versione specifica, è possibile farla funzionare in modo abbastanza ampio.

A proposito, se lo si esegue in un ambiente in cui Excel non è installato, è possibile determinare correttamente che non può essere elaborato.