Darbs ar Excel .NET tīklā neatkarīgi no instalētās Excel versijas

Lapa atjaunota :
Lapas izveides datums :

Darbības vide

Visual Studio
  • Visual Studio 2022
.NETO
  • .NET 6.0
Windows
  • Operētājsistēmā Windows 7
  • Operētājsistēmā Windows 11
Excel
  • Microsoft 365
  • Office 2007

Priekšnoteikumi

Windows
  • Viena no versijām
Excel
  • Viena no versijām

Problēmas ar bibliotēkām, kas darbojas programmā Excel

Viens no veidiem, kā programmiski mijiedarboties ar Excel, ir Microsoft.Office.Interop.Excel atsauce . Tas ir līdzīgs darbam tieši ar Excel lietojumprogrammu, izmantojot COM, tāpēc tas nedaudz atšķiras no darba ar datiem Excel failā. Kā priekšnoteikums Excel ir jāinstalē izpildāmajā vidē, bet daudzas Excel funkcijas ir pieejamas no programmas puses.

Microsoft.Office.Interop.Excel Ja izmantojat programmu, jūs domājat, ka programma tiks rakstīta šādā veidā.

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

Ir apgrūtinoši katru reizi atbrīvot resursus, bet, tā kā jūs varat izmantot sagatavotās klases, jūs varat veikt Excel apstrādi salīdzinoši intuitīvi.

Tomēr trūkums ir tāds, ka bibliotēkas versijai, uz kuru atsaucas programma, ir jāatbilst vai jābūt saderīgai ar Excel versiju, kas instalēta vidē, kurā tā darbojas. Piemēram, programma, kas izveidota, atsaucoties uz programmas Excel iekšējās versijas 15.0 (2013) bibliotēku, var darboties tikai tad, ja ir instalēta programma Excel 2013. Ja vēlaties, lai programma darbotos daudzās vidēs, iespējams, excel instalācija jādarbina konsekventā versijā.

Ja šāda darbība ir iespējama, tas ir labi, bet, ja instalētā Excel versija ir atšķirīga, to nevar pielāgot. Tāpēc ir nepieciešams kaut ko darīt programmas pusē.

Atsauču veidošana bibliotēkās dinamiski, izmantojot dinamisku, nevis tiešas atsauces uz tām

Šajā gadījumā problēma rodas, jo, veidojot programmu, tiek fiksēta atsauce uz bibliotēkas versiju. Tas nozīmē, ka tā vietā, lai noteiktu versiju programmas izveides laikā, ir nepieciešams noteikt versiju programmas izpildes laikā.

Ir vairāki veidi, kā to izdarīt, bet šoreiz mēs to atrisināsim, izmantojot "" "" un ""Type.GetTypeFromProgIDActivator.CreateInstance.dynamic

Parasti C# ir jānosaka tipa informācija programmas izveides laikā, bet, ja tips ir dynamic , tipu var noteikt izpildes laikā. object Atšķirībā no dynamic tipiem, ja gadījumam, kas iestatīts uz mainīgo, ir klase, varat arī izsaukt šīs klases metodes un īpašības. Tomēr, tā kā tas, kas nonāk mainīgajā lielumā, tiek noteikts izpildes laikā, izpildes laikā radīsies kļūda, ja nav noteiktas metodes.

Kods, kas izveidots, izmantojot tos, ir šāds.

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

Mainīgā deklarācijas veids ir , un Excel.Application izveide ir dynamic nedaudz atšķirīga, bet pārējais kods tiek gandrīz atkārtoti izmantots.

Tā kā iepriekš minētais kods neattiecas uz Excel bibliotēku, nav iespējams izmantot mērķa ar Excel saistīto klases informāciju. Tāpēc mainīgā lieluma veids ir viss dynamic . Turklāt, tā kā mēs nevaram tieši atsaukties uz klasi, mēs tā vietā Excel.Application Type.GetTypeFromProgID saņemam informāciju Activator.CreateInstance Tips un nododam to,Excel.Application lai izveidotu Excel.Application instanci . Starp citu, vidē, kur Excel nav instalēts, jūs to Type nevarat iegūt , tāpēc ir iespējams izolēt, Type.GetTypeFromProgID vai tas ir instalēts vai nē.

dynamic Izmantošanas trūkums ir tāds, ka, veidojot programmu, klasi nevar noteikt. Intellisense, kas ir pabeigšanas funkcija, ievadot kodu, neparāda metodes nosaukumus un rekvizītu nosaukumus. Var būt mazāk apgrūtinoši sākumā rakstīt kodu ar atsauci uz bibliotēku un aizstāt to ar dynamic vēlāku.

Palaižot Microsoft 365 vidē, varat apstiprināt, ka tas tiek atspoguļots šādi.

Tālāk ir norādīts, palaižot to daudz vecākā Office 2007 versijā. Ja neizmantojat funkciju, kas darbojas tikai noteiktā versijā, varat likt tai darboties diezgan plaši.

Starp citu, ja palaižat to vidē, kur Excel nav instalēts, varat pareizi noteikt, ka to nevar apstrādāt.