Làm việc với Excel trong .NET độc lập với phiên bản Excel đã cài đặt

Trang Cập Nhật :
Ngày tạo trang :

Môi trường hoạt động

Visual Studio
  • Visual Studio 2022
.MẠNG
  • .NET 6,0
Windows
  • cửa sổ 7
  • cửa sổ 11
Excel
  • Microsoft 365
  • Văn phòng 2007

Điều kiện tiên quyết

Windows
  • Một trong những phiên bản
Excel
  • Một trong những phiên bản

Sự cố với thư viện hoạt động với Excel

Một cách để tương tác lập trình với Excel là Microsoft.Office.Interop.Excel tham khảo . Điều này tương tự như làm việc trực tiếp với ứng dụng Excel qua COM, vì vậy nó hơi khác so với làm việc với dữ liệu trong tệp Excel. Như một tiền đề, Excel phải được cài đặt trong môi trường được thực thi, nhưng nhiều chức năng của Excel có sẵn từ phía chương trình.

Microsoft.Office.Interop.Excel Nếu bạn sử dụng chương trình, bạn sẽ nghĩ rằng chương trình sẽ được viết theo cách sau.

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

Thật rắc rối khi phải phát hành tài nguyên mỗi lần, nhưng vì bạn có thể sử dụng các lớp đã chuẩn bị, bạn có thể thực hiện xử lý Excel tương đối trực quan.

Tuy nhiên, nhược điểm là phiên bản của thư viện được tham chiếu bởi chương trình phải khớp hoặc tương thích với phiên bản Excel được cài đặt trong môi trường mà nó đang chạy. Ví dụ: chương trình được tạo bằng cách tham chiếu thư viện Excel phiên bản 15.0 (2013) nội bộ của Excel chỉ có thể chạy khi Excel 2013 được cài đặt. Nếu bạn muốn chương trình của bạn chạy trong nhiều môi trường, bạn có thể cần phải giữ cho cài đặt Excel của bạn chạy trên một phiên bản nhất quán.

Nếu một hoạt động như vậy là có thể, nó là tốt, nhưng nếu phiên bản cài đặt của Excel là khác nhau, nó không thể được cung cấp. Do đó, cần phải làm một cái gì đó về phía chương trình.

Tham chiếu động các thư viện với động thay vì trực tiếp tham chiếu chúng

Sự cố trong trường hợp này xảy ra do phiên bản thư viện được tham chiếu cố định khi chương trình được tạo. Điều này có nghĩa là thay vì xác định phiên bản tại thời điểm tạo chương trình, cần xác định phiên bản tại thời điểm thực thi chương trình.

Có một số cách để làm điều đó, nhưng lần này chúng tôi sẽ giải quyết nó bằng cách sử dụng "" "" và "Type.GetTypeFromProgIDActivator.CreateInstance".dynamic

Thông thường, trong C#, cần xác định thông tin kiểu tại thời điểm tạo chương trình, nhưng nếu kiểu là dynamic , loại có thể được xác định tại thời điểm thực thi. object Không giống như dynamic các loại, nếu phiên bản được đặt thành một biến là một lớp, bạn cũng có thể gọi các phương thức và thuộc tính của lớp đó. Tuy nhiên, vì những gì đi vào biến được xác định tại thời điểm thực thi, một lỗi sẽ xảy ra tại thời điểm thực thi nếu không có phương thức được chỉ định.

Mã được tạo bằng cách sử dụng chúng như sau.

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

Loại khai báo của biến là dynamic , và Excel.Application việc tạo ra hơi khác nhau, nhưng phần còn lại của mã gần như được sử dụng lại.

Vì đoạn mã trên không tham chiếu đến thư viện Excel, nên không thể sử dụng thông tin lớp liên quan đến Excel đích. Do đó, loại biến là tất cả dynamic . Ngoài ra, vì chúng ta không thể tham chiếu trực tiếp đến lớp,Excel.Application thay vào đó Excel.Application Type.GetTypeFromProgID chúng ta lấy thông tin Activator.CreateInstance Type và truyền nó để tạo Excel.Application một thể hiện của . Nhân tiện, trong một môi trường mà Excel không được cài đặt, bạn không thể lấy nó trong Type , Type.GetTypeFromProgID vì vậy có thể cách ly cho dù nó có được cài đặt hay không.

dynamic Nhược điểm của việc sử dụng là không thể xác định lớp khi xây dựng chương trình. Intellisense, là một hàm hoàn thành khi nhập mã, không hiển thị tên phương thức và tên thuộc tính. Có thể ít cồng kềnh hơn khi viết mã với tham chiếu đến thư viện ngay từ đầu và thay thế nó bằng dynamic sau này.

Khi chạy trong môi trường Microsoft 365, bạn có thể xác nhận rằng môi trường đó được phản ánh như sau.

Sau đây là kết quả của việc chạy nó trên phiên bản Office 2007 cũ hơn nhiều. Nếu bạn không sử dụng một chức năng chỉ hoạt động trong một phiên bản cụ thể, bạn có thể làm cho nó hoạt động khá rộng rãi.

Nhân tiện, nếu bạn chạy nó trong môi trường chưa cài đặt Excel, bạn có thể xác định chính xác rằng nó không thể được xử lý.