Práca s programom Excel v platforme .NET nezávisle od nainštalovanej verzie programu Excel

Stránka aktualizovaná :
Dátum vytvorenia strany :

Prevádzkové prostredie

Vizuálne štúdio
  • Visual Studio 2022
.SIEŤ
  • .NET 6.0
Windows
  • Windows 7
  • Windows 11
Vynikať
  • Microsoft 365
  • Balík Office 2007

Predpoklady

Windows
  • Jedna z verzií
Vynikať
  • Jedna z verzií

Problémy s knižnicami, ktoré fungujú s Excelom

Jedným zo spôsobov programovej interakcie s programom Excel je Microsoft.Office.Interop.Excel odkaz . Je to podobné ako pri práci priamo s aplikáciou Excel cez COM, takže sa mierne líši od práce s údajmi v súbore programu Excel. Predpokladom je, že Excel musí byť nainštalovaný v prostredí, ktoré sa má vykonať, ale mnohé funkcie programu Excel sú k dispozícii zo strany programu.

Microsoft.Office.Interop.Excel Ak používate program, budete si myslieť, že program bude napísaný nasledujúcim spôsobom.

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

Je nepríjemné zakaždým uvoľňovať zdroje, ale keďže môžete použiť pripravené triedy, môžete vykonávať spracovanie programu Excel relatívne intuitívne.

Nevýhodou však je, že verzia knižnice, na ktorú program odkazuje, sa musí zhodovať alebo byť kompatibilná s verziou programu Excel nainštalovanou v prostredí, v ktorom je spustená. Napríklad program vytvorený odkazom na internú knižnicu verzie 15.0 (2013) programu Excel sa môže spustiť len vtedy, keď je nainštalovaný Excel 2013. Ak chcete, aby bol program spustený v mnohých prostrediach, možno budete musieť ponechať inštaláciu programu Excel spustenú v konzistentnej verzii.

Ak je takáto operácia možná, je to v poriadku, ale ak je nainštalovaná verzia programu Excel iná, nie je možné ju prijať. Preto je potrebné urobiť niečo na strane programu.

Dynamické odkazovanie na knižnice dynamickým namiesto priameho odkazovania na knižnice

Problém v tomto prípade sa vyskytuje, pretože verzia knižnice je odkazovaná opravená pri vytváraní programu. To znamená, že namiesto určenia verzie v čase vytvorenia programu je potrebné určiť verziu v čase realizácie programu.

Existuje niekoľko spôsobov, ako to urobiť, ale tentoraz to vyriešime pomocou "" "" a "Type.GetTypeFromProgIDActivator.CreateInstance".dynamic

Normálne v C# je potrebné určiť informáciu o type v čase vytvorenia programu, ale ak je dynamic typ , typ môže byť určený v čase vykonania. object Na rozdiel od dynamic typov, ak je inštanciou nastavenou na premennú trieda, môžete tiež volať metódy a vlastnosti tejto triedy. Keďže však to, čo ide do premennej, je určené v čase vykonania, chyba sa vyskytne v čase vykonania, ak neexistuje žiadna špecifikovaná metóda.

Kód vytvorený pomocou nich je nasledujúci.

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

Typ deklarácie premennej je , a Excel.Application vytvorenie je mierne odlišné, ale zvyšok kódu je dynamic takmer znovu použitý.

Keďže vyššie uvedený kód neodkazuje na knižnicu programu Excel, nie je možné použiť cieľové informácie o triede súvisiace s programom Excel. Preto typ premennej je všetko dynamic . Keďže nemôžeme priamo odkazovať na triedu, namiesto toho Excel.Application Type.GetTypeFromProgID dostaneme informácie Activator.CreateInstance o type a odošleme ich,Excel.Application aby sme vytvorili Excel.Application inštanciu . Mimochodom, v prostredí, kde program Excel nie je nainštalovaný, ho nemôžete dostať dovnútra Type , takže je možné izolovať, Type.GetTypeFromProgID či je nainštalovaný alebo nie.

dynamic Nevýhodou použitia je, že triedu nie je možné určiť pri zostavovaní programu. Intellisense, čo je funkcia dokončenia pri zadávaní kódu, nezobrazuje názvy metód a vlastností. Môže byť menej ťažkopádne písať kód s odkazom na knižnicu na začiatku a nahradiť ho dynamic neskôr.

Keď je spustený v prostredí služby Microsoft 365, môžete potvrdiť, že sa prejaví nasledovne.

Nasleduje výsledok spustenia v oveľa staršej verzii balíka Office 2007. Ak nepoužívate funkciu, ktorá funguje iba v konkrétnej verzii, môžete ju nastaviť tak, aby fungovala pomerne široko.

Mimochodom, ak ho spustíte v prostredí, kde nie je nainštalovaný program Excel, môžete správne určiť, že ho nemožno spracovať.