Bekerja dengan Excel dalam .NET secara bebas daripada versi Excel yang dipasang

Laman dikemaskini :
Tarikh penciptaan halaman :

Persekitaran operasi

Visual Studio
  • Visual Studio 2022
.BERSIH
  • .NET 6.0
Windows
  • Windows 7
  • Windows 11
Excel
  • Microsoft 365
  • Office 2007

Prasyarat

Windows
  • Salah satu versi
Excel
  • Salah satu versi

Masalah dengan pustaka yang berfungsi dengan Excel

Salah satu cara untuk berinteraksi secara programatik dengan Excel adalah Microsoft.Office.Interop.Excel dengan merujuk . Ini serupa dengan bekerja secara langsung dengan aplikasi Excel melalui COM, jadi ia sedikit berbeza daripada bekerja dengan data dalam fail Excel. Sebagai premis, Excel mesti dipasang di persekitaran yang akan dilaksanakan, tetapi banyak fungsi Excel tersedia dari sisi program.

Microsoft.Office.Interop.Excel Jika anda menggunakan program ini, anda akan berfikir bahawa program ini 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);
}

Adalah menyusahkan untuk melepaskan sumber setiap kali, tetapi kerana anda boleh menggunakan kelas yang disediakan, anda boleh melakukan pemprosesan Excel secara intuitif.

Walau bagaimanapun, kelemahannya ialah versi pustaka yang dirujuk oleh program mesti sepadan atau serasi dengan versi Excel yang dipasang dalam persekitaran di mana ia berjalan. Contohnya, program yang dicipta dengan merujuk pustaka Excel versi dalaman 15.0 (2013) hanya boleh berjalan apabila Excel 2013 dipasang. Jika anda ingin program anda berjalan dalam banyak persekitaran, anda mungkin perlu memastikan pemasangan Excel anda berjalan pada versi yang konsisten.

Sekiranya operasi sedemikian mungkin, tidak mengapa, tetapi jika versi Excel yang dipasang berbeza, ia tidak dapat ditempatkan. Oleh itu, adalah perlu untuk melakukan sesuatu di bahagian program.

Merujuk pustaka secara dinamik dengan dinamik dan bukannya merujuknya secara langsung

Masalah dalam kes ini berlaku kerana versi perpustakaan dirujuk tetap apabila program dicipta. Ini bermakna bahawa bukannya menentukan versi pada masa penciptaan program, adalah perlu untuk menentukan versi pada masa pelaksanaan program.

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

Biasanya, dalam C #, adalah perlu untuk menentukan jenis maklumat pada masa penciptaan program, tetapi jika jenisnya dynamic , jenis itu boleh ditentukan pada masa pelaksanaan. object Tidak seperti dynamic jenis, jika contoh yang ditetapkan kepada pembolehubah adalah kelas, anda juga boleh memanggil kaedah dan sifat kelas itu. Walau bagaimanapun, kerana apa yang masuk ke dalam pembolehubah ditentukan pada masa pelaksanaan, kesilapan akan berlaku pada masa pelaksanaan jika tidak ada kaedah yang ditentukan.

Kod yang dibuat menggunakan ini adalah seperti 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 pengisytiharan pembolehubah adalah dynamic , dan Excel.Application penciptaannya sedikit berbeza, tetapi kod selebihnya hampir digunakan semula.

Oleh kerana kod di atas tidak merujuk kepada pustaka Excel, adalah tidak mungkin untuk menggunakan sasaran maklumat kelas berkaitan Excel. Oleh itu, jenis pembolehubah adalah semua dynamic . Juga, kerana kita tidak boleh merujuk kelas secara langsung,Excel.Application kita sebaliknya Type.GetTypeFromProgID Excel.Application mendapatkan maklumat Jenis dan Activator.CreateInstance menyampaikannya untuk membuat Excel.Application contoh . Dengan cara ini, dalam persekitaran di mana Excel tidak dipasang, Type.GetTypeFromProgID anda tidak boleh mendapatkannya , Type jadi mungkin untuk mengasingkan sama ada ia dipasang atau tidak.

dynamic Kelemahan penggunaan adalah bahawa kelas tidak dapat ditentukan semasa membina program. Intellisense, yang merupakan fungsi penyelesaian apabila memasukkan kod, tidak memaparkan nama kaedah dan nama sifat. Mungkin kurang rumit untuk menulis kod dengan merujuk kepada perpustakaan pada mulanya dan menggantikannya dengan dynamic kemudian.

Apabila berjalan dalam persekitaran Microsoft 365, anda boleh mengesahkan bahawa ia ditunjukkan seperti berikut.

Berikut ialah hasil daripada menjalankannya pada versi Office 2007 yang lebih lama. Jika anda tidak menggunakan fungsi yang hanya berfungsi dalam versi tertentu, anda boleh menjadikannya berfungsi dengan agak meluas.

Dengan cara ini, jika anda menjalankannya dalam persekitaran di mana Excel tidak dipasang, anda boleh menentukan dengan betul bahawa ia tidak boleh diproses.