Bekerja dengan Excel di .NET secara independen dari versi Excel yang diinstal

Halaman Diperbarui :
Tanggal pembuatan halaman :

Lingkungan operasi

Visual Studio
  • Visual Studio 2022
.JARING
  • .NET 6.0
Windows
  • jendela 7
  • jendela 11
Melampaui
  • Microsoft 365
  • Kantor 2007

Prasyarat

Windows
  • Salah satu versi
Melampaui
  • Salah satu versi

Masalah dengan pustaka yang berfungsi dengan Excel

Salah satu cara untuk berinteraksi secara terprogram dengan Excel adalah Microsoft.Office.Interop.Excel dengan referensi . Ini mirip dengan bekerja secara langsung dengan aplikasi Excel melalui COM, jadi sedikit berbeda dari bekerja dengan data dalam file Excel. Sebagai premis, Excel harus diinstal di lingkungan yang akan dijalankan, tetapi banyak fungsi Excel tersedia dari sisi program.

Microsoft.Office.Interop.Excel Jika Anda menggunakan program ini, Anda akan berpikir bahwa program tersebut akan ditulis dengan cara berikut.

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

Sulit untuk melepaskan sumber daya setiap kali, tetapi karena Anda dapat menggunakan kelas yang disiapkan, Anda dapat melakukan pemrosesan Excel secara relatif intuitif.

Kekurangannya, bagaimanapun, adalah bahwa versi perpustakaan yang dirujuk oleh program harus cocok atau kompatibel dengan versi Excel yang diinstal di lingkungan di mana ia berjalan. Misalnya, program yang dibuat dengan merujuk pustaka Excel versi internal 15.0 (2013) hanya dapat berjalan saat Excel 2013 diinstal. Jika Anda ingin program Anda berjalan di banyak lingkungan, Anda mungkin perlu untuk menjaga instalasi Excel Anda berjalan pada versi yang konsisten.

Jika operasi seperti itu dimungkinkan, tidak apa-apa, tetapi jika versi Excel yang diinstal berbeda, itu tidak dapat ditampung. Karena itu, perlu dilakukan sesuatu di sisi program.

Mereferensikan pustaka secara dinamis dengan dinamis, bukan merujuknya secara langsung

Masalah dalam hal ini terjadi karena versi perpustakaan dirujuk tetap ketika program dibuat. Ini berarti bahwa alih-alih menentukan versi pada saat pembuatan program, perlu untuk menentukan versi pada saat eksekusi program.

Ada beberapa cara untuk melakukannya, tetapi kali ini kita akan menyelesaikannya menggunakan "" "" dan ""Type.GetTypeFromProgIDActivator.CreateInstance.dynamic

Biasanya, dalam C #, perlu untuk menentukan informasi tipe pada saat pembuatan program, tetapi jika jenisnya , dynamic jenisnya dapat ditentukan pada saat eksekusi. object Tidak seperti dynamic type, jika instance yang diatur ke variabel adalah kelas, Anda juga dapat memanggil metode dan properti kelas itu. Namun, karena apa yang masuk ke variabel ditentukan pada waktu eksekusi, kesalahan akan terjadi pada waktu eksekusi jika tidak ada metode yang ditentukan.

Kode yang dibuat menggunakan ini adalah sebagai berikut.

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

Jenis deklarasi variabel adalah dynamic , dan Excel.Application pembuatannya sedikit berbeda, tetapi sisa kode hampir digunakan kembali.

Karena kode di atas tidak merujuk ke pustaka Excel, tidak mungkin menggunakan informasi kelas terkait Excel target. Oleh karena itu, jenis variabelnya adalah semua dynamic . Juga, karena kita tidak dapat mereferensikan kelas secara langsung,Excel.Application kita malah Excel.Application Type.GetTypeFromProgID mendapatkan informasi Activator.CreateInstance Type dan meneruskannya untuk membuat Excel.Application instance . Omong-omong, di lingkungan di mana Excel tidak diinstal, Anda tidak bisa mendapatkannya di Type , Type.GetTypeFromProgID sehingga dimungkinkan untuk mengisolasi apakah itu diinstal atau tidak.

dynamic Kerugian menggunakan adalah bahwa kelas tidak dapat ditentukan ketika membangun program. Intellisense, yang merupakan fungsi penyelesaian saat memasukkan kode, tidak menampilkan nama metode dan nama properti. Mungkin kurang rumit untuk menulis kode dengan mengacu pada perpustakaan di awal dan menggantinya dengan dynamic nanti.

Saat berjalan di lingkungan Microsoft 365, Anda dapat mengonfirmasi bahwa lingkungan tersebut tercermin sebagai berikut.

Berikut ini adalah hasil dari menjalankannya pada versi Office 2007 yang jauh lebih lama. Jika Anda tidak menggunakan fungsi yang hanya berfungsi dalam versi tertentu, Anda dapat membuatnya bekerja cukup luas.

By the way, jika Anda menjalankannya di lingkungan di mana Excel tidak diinstal, Anda dapat dengan benar menentukan bahwa itu tidak dapat diproses.