Treballar amb Excel en .NET independentment de la versió instal·lada d'Excel

Pàgina actualitzada :
Data de creació de la pàgina :

Entorn operatiu

Estudi visual
  • Estudi visual 2022
.XARXA
  • .NET 6.0
Windows
  • Finestres 7
  • Finestres 11
Excel·lir
  • Microsoft 365
  • Despatx 2007

Prerequisits

Windows
  • Una de les versions
Excel·lir
  • Una de les versions

Problemes amb biblioteques que funcionen amb l'Excel

Una manera d'interactuar programàticament amb Excel és Microsoft.Office.Interop.Excel fer referència a . Això és similar a treballar directament amb l'aplicació Excel mitjançant COM, de manera que és lleugerament diferent de treballar amb dades en un fitxer Excel. Com a premissa, Excel s'ha d'instal·lar a l'entorn a executar, però moltes funcions d'Excel estan disponibles des del costat del programa.

Microsoft.Office.Interop.Excel Si utilitzeu el programa, pensareu que el programa s'escriurà de la següent manera.

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

És problemàtic haver d'alliberar recursos cada vegada, però com que podeu utilitzar les classes preparades, podeu realitzar el processament d'Excel de manera relativament intuïtiva.

L'inconvenient, però, és que la versió de la llibreria a la qual fa referència el programa ha de coincidir o ser compatible amb la versió d'Excel instal·lada a l'entorn en què s'està executant. Per exemple, un programa creat fent referència a la versió interna 15.0 (2013) biblioteca d'Excel només es pot executar quan Excel 2013 està instal·lat. Si voleu que el vostre programa s'executi en molts entorns, potser haureu de mantenir la instal·lació de l'Excel executant-se en una versió coherent.

Si aquesta operació és possible, està bé, però si la versió instal·lada d'Excel és diferent, no es pot acomodar. Per tant, cal fer alguna cosa al costat del programa.

Referenciar les biblioteques dinàmicament amb dinàmiques en lloc de referenciar-les directament

El problema en aquest cas es produeix perquè es fa referència a la versió de la llibreria fixa quan es crea el programa. Això significa que en lloc de determinar la versió en el moment de la creació del programa, cal determinar la versió en el moment de l'execució del programa.

Hi ha diverses maneres de fer-ho, però aquesta vegada ho solucionarem utilitzant "" "" i "Type.GetTypeFromProgIDActivator.CreateInstance".dynamic

Normalment, en C#, cal determinar la informació del tipus en el moment de la creació del programa, però si el tipus és dynamic , el tipus es pot determinar en el moment de l'execució. object A diferència dynamic dels tipus, si la instància definida en una variable és una classe, també podeu cridar els mètodes i propietats d'aquesta classe. No obstant això, com que el que entra en la variable es determina en el moment de l'execució, es produirà un error en el moment de l'execució si no hi ha un mètode especificat.

El codi creat amb aquests és el següent.

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

El tipus de declaració de la variable és , i Excel.Application la creació de és lleugerament diferent, però la resta del codi és dynamic gairebé reutilitzat.

Atès que el codi anterior no fa referència a la biblioteca d'Excel, no és possible utilitzar la informació de classe relacionada amb Excel de destinació. Per tant, el tipus de variable és tot dynamic . A més, com que no podem referenciar la classe directament,Excel.Application obtenim la informació Activator.CreateInstance del tipus i la passem Excel.Application Type.GetTypeFromProgID a crear Excel.Application una instància de . Per cert, en un entorn on Excel no està instal·lat, Type.GetTypeFromProgID no el podeu aconseguir , Type de manera que és possible aïllar si està instal·lat o no.

dynamic El desavantatge d'utilitzar és que la classe no es pot determinar a l'hora de construir el programa. Intellisense, que és una funció de compleció quan s'introdueix codi, no mostra noms de mètodes ni noms de propietats. Pot ser menys feixuc escriure codi fent referència a la biblioteca al principi i substituir-lo per dynamic més endavant.

Quan s'executa en un entorn Microsoft 365, podeu confirmar que es reflecteix de la manera següent.

El següent és el resultat d'executar-lo en una versió molt més antiga d'Office 2007. Si no utilitzeu una funció que només funcioni en una versió específica, podeu fer que funcioni força àmpliament.

Per cert, si l'executeu en un entorn on Excel no està instal·lat, podeu determinar correctament que no es pot processar.