Εργασία με το Excel στο .NET ανεξάρτητα από την εγκατεστημένη έκδοση του Excel

Σελίδα ενημέρωση :
Ημερομηνία δημιουργίας σελίδας :

Περιβάλλον λειτουργίας

Οπτικό στούντιο
  • Visual Studio 2022
.ΔΊΧΤΥ
  • .NET 6.0
παράθυρα
  • Τα Windows 7
  • Παράθυρα 11
Αριστεύω
  • Το Microsoft 365
  • Γραφείο 2007

Προϋποθέσεις

παράθυρα
  • Μία από τις εκδόσεις
Αριστεύω
  • Μία από τις εκδόσεις

Προβλήματα με βιβλιοθήκες που λειτουργούν με το Excel

Ένας τρόπος αλληλεπίδρασης μέσω προγραμματισμού με το Excel είναι Microsoft.Office.Interop.Excel η αναφορά στο . Αυτό είναι παρόμοιο με την εργασία απευθείας με την εφαρμογή Excel μέσω COM, επομένως είναι ελαφρώς διαφορετικό από την εργασία με δεδομένα σε ένα αρχείο Excel. Ως προϋπόθεση, το Excel πρέπει να εγκατασταθεί στο περιβάλλον που πρόκειται να εκτελεστεί, αλλά πολλές λειτουργίες του Excel είναι διαθέσιμες από την πλευρά του προγράμματος.

Microsoft.Office.Interop.Excel Εάν χρησιμοποιείτε το πρόγραμμα, θα νομίζετε ότι το πρόγραμμα θα γραφτεί με τον ακόλουθο τρόπο.

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

Είναι ενοχλητικό να πρέπει να απελευθερώνετε πόρους κάθε φορά, αλλά επειδή μπορείτε να χρησιμοποιήσετε τις προετοιμασμένες τάξεις, μπορείτε να εκτελέσετε την επεξεργασία του Excel σχετικά διαισθητικά.

Το μειονέκτημα, ωστόσο, είναι ότι η έκδοση της βιβλιοθήκης που αναφέρεται από το πρόγραμμα πρέπει να ταιριάζει ή να είναι συμβατή με την έκδοση του Excel που είναι εγκατεστημένη στο περιβάλλον στο οποίο εκτελείται. Για παράδειγμα, ένα πρόγραμμα που δημιουργήθηκε με αναφορά στην εσωτερική βιβλιοθήκη έκδοση 15.0 (2013) του Excel μπορεί να εκτελεστεί μόνο όταν είναι εγκατεστημένο το Excel 2013. Εάν θέλετε το πρόγραμμά σας να εκτελείται σε πολλά περιβάλλοντα, ίσως χρειαστεί να διατηρήσετε την εγκατάσταση του Excel σε συνεπή έκδοση.

Εάν μια τέτοια λειτουργία είναι δυνατή, είναι εντάξει, αλλά εάν η εγκατεστημένη έκδοση του Excel είναι διαφορετική, δεν μπορεί να φιλοξενηθεί. Επομένως, είναι απαραίτητο να κάνουμε κάτι από την πλευρά του προγράμματος.

Αναφορά σε βιβλιοθήκες δυναμικά με δυναμική αντί για άμεση αναφορά σε αυτές

Το ζήτημα σε αυτήν την περίπτωση παρουσιάζεται επειδή η έκδοση βιβλιοθήκης αναφέρεται διορθώθηκε κατά τη δημιουργία του προγράμματος. Αυτό σημαίνει ότι αντί να προσδιοριστεί η έκδοση κατά τη στιγμή της δημιουργίας του προγράμματος, είναι απαραίτητο να προσδιοριστεί η έκδοση κατά τη στιγμή της εκτέλεσης του προγράμματος.

Υπάρχουν διάφοροι τρόποι για να το κάνετε, αλλά αυτή τη φορά θα το λύσουμε χρησιμοποιώντας "" "" και "Type.GetTypeFromProgIDActivator.CreateInstance".dynamic

Κανονικά, στη C#, είναι απαραίτητο να προσδιορίσετε τις πληροφορίες τύπου κατά τη στιγμή της δημιουργίας του προγράμματος, αλλά εάν ο τύπος είναι dynamic , ο τύπος μπορεί να προσδιοριστεί κατά τη στιγμή της εκτέλεσης. object Σε αντίθεση με dynamic τους τύπους, εάν η παρουσία που έχει οριστεί σε μια μεταβλητή είναι κλάση, μπορείτε επίσης να καλέσετε τις μεθόδους και τις ιδιότητες αυτής της κλάσης. Ωστόσο, δεδομένου ότι αυτό που εισέρχεται στη μεταβλητή καθορίζεται κατά το χρόνο εκτέλεσης, θα προκύψει σφάλμα κατά το χρόνο εκτέλεσης εάν δεν υπάρχει καθορισμένη μέθοδος.

Ο κώδικας που δημιουργήθηκε χρησιμοποιώντας αυτά έχει ως εξής.

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

Ο τύπος της δήλωσης της μεταβλητής είναι , και Excel.Application η δημιουργία της είναι dynamic ελαφρώς διαφορετική, αλλά ο υπόλοιπος κώδικας σχεδόν επαναχρησιμοποιείται.

Δεδομένου ότι ο παραπάνω κώδικας δεν αναφέρεται στη βιβλιοθήκη του Excel, δεν είναι δυνατή η χρήση των πληροφοριών κλάσης προορισμού που σχετίζονται με το Excel. Επομένως, ο τύπος της μεταβλητής είναι όλα dynamic . Επίσης, δεδομένου ότι δεν μπορούμε να αναφερθούμε απευθείας στην κλάση,Excel.Application παίρνουμε τις Excel.Application Type.GetTypeFromProgID πληροφορίες Activator.CreateInstance Type και τις μεταβιβάζουμε για να δημιουργήσουμε Excel.Application μια παρουσία του . Παρεμπιπτόντως, σε ένα περιβάλλον όπου το Excel δεν είναι εγκατεστημένο, Type.GetTypeFromProgID δεν μπορείτε να το πάρετε , Type οπότε είναι δυνατό να απομονώσετε εάν είναι εγκατεστημένο ή όχι.

dynamic Το μειονέκτημα της χρήσης είναι ότι η κλάση δεν μπορεί να προσδιοριστεί κατά τη δημιουργία του προγράμματος. Το Intellisense, το οποίο είναι μια λειτουργία ολοκλήρωσης κατά την εισαγωγή κώδικα, δεν εμφανίζει ονόματα μεθόδων και ονόματα ιδιοτήτων. Μπορεί να είναι λιγότερο δυσκίνητο να γράψετε κώδικα με αναφορά στη βιβλιοθήκη στην αρχή και να τον αντικαταστήσετε με dynamic αργότερα.

Όταν εκτελείται σε περιβάλλον Microsoft 365, μπορείτε να επιβεβαιώσετε ότι αντικατοπτρίζεται ως εξής.

Το παρακάτω είναι το αποτέλεσμα της εκτέλεσής του σε μια πολύ παλαιότερη έκδοση του Office 2007. Εάν δεν χρησιμοποιείτε μια λειτουργία που λειτουργεί μόνο σε μια συγκεκριμένη έκδοση, μπορείτε να την κάνετε να λειτουργεί αρκετά ευρέως.

Παρεμπιπτόντως, εάν το εκτελέσετε σε περιβάλλον όπου το Excel δεν είναι εγκατεστημένο, μπορείτε να προσδιορίσετε σωστά ότι δεν είναι δυνατή η επεξεργασία του.