Praca z programem Excel na platformie .NET niezależnie od zainstalowanej wersji programu Excel

Strona zaktualizowana :
Data utworzenia strony :

Środowisko pracy

Visual Studio
  • informacji o wersji Visual Studio 2022
.SIEĆ
  • .NET 6.0
Windows
  • Okna 7
  • Okna 11
Programu excel
  • Platforma Microsoft 365
  • Pakiet Office 2007

Warunki wstępne

Windows
  • Jedna z wersji
Programu excel
  • Jedna z wersji

Problemy z bibliotekami współpracującymi z programem Excel

Jednym ze sposobów programowej interakcji z programem Excel jest Microsoft.Office.Interop.Excel odwoływanie się do programu . Jest to podobne do pracy bezpośrednio z aplikacją Excel za pośrednictwem COM, więc różni się nieco od pracy z danymi w pliku Excel. Z założenia Excel musi być zainstalowany w środowisku, które ma być wykonywane, ale wiele funkcji Excela jest dostępnych od strony programu.

Microsoft.Office.Interop.Excel Jeśli korzystasz z programu, pomyślisz, że program zostanie napisany w następujący sposób.

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

Kłopotliwe jest każdorazowe zwalnianie zasobów, ale dzięki temu, że możesz korzystać z przygotowanych klas, możesz stosunkowo intuicyjnie wykonywać przetwarzanie Excela.

Wadą jest jednak to, że wersja biblioteki, do której odwołuje się program, musi być zgodna lub zgodna z wersją programu Excel zainstalowaną w środowisku, w którym jest uruchomiona. Na przykład program utworzony przez odwołanie się do wewnętrznej biblioteki programu Excel w wersji 15.0 (2013) można uruchomić tylko wtedy, gdy jest zainstalowany program Excel 2013. Jeśli chcesz, aby program działał w wielu środowiskach, może być konieczne utrzymanie instalacji programu Excel działającej w spójnej wersji.

Jeśli taka operacja jest możliwa, jest w porządku, ale jeśli zainstalowana wersja Excela jest inna, nie można jej dostosować. Dlatego konieczne jest zrobienie czegoś po stronie programu.

Dynamiczne odwoływanie się do bibliotek za pomocą funkcji dynamic zamiast bezpośredniego odwoływania się do nich

Problem w tym przypadku występuje, ponieważ wersja biblioteki jest przywoływana podczas tworzenia programu. Oznacza to, że zamiast określać wersję w momencie tworzenia programu, konieczne jest ustalenie wersji w momencie wykonywania programu.

Można to zrobić na kilka sposobów, ale tym razem rozwiążemy to za pomocą "" "" i "Type.GetTypeFromProgIDActivator.CreateInstance".dynamic

Zwykle w języku C# konieczne jest określenie informacji o typie w momencie tworzenia programu, ale jeśli typ to dynamic , typ można określić w momencie wykonywania. object W przeciwieństwie do dynamic typów, jeśli wystąpienie ustawione na zmienną jest klasą, można również wywołać metody i właściwości tej klasy. Ponieważ jednak to, co trafia do zmiennej, jest określane w czasie wykonywania, błąd wystąpi w czasie wykonywania, jeśli nie ma określonej metody.

Kod utworzony przy ich użyciu jest następujący.

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

Typ deklaracji zmiennej to dynamic , a Excel.Application tworzenie jest nieco inne, ale reszta kodu jest prawie ponownie używana.

Ponieważ powyższy kod nie odwołuje się do biblioteki programu Excel, nie można użyć informacji o klasie docelowej związanej z programem Excel. W związku z tym typ zmiennej to all dynamic . Ponadto, ponieważ nie możemy bezpośrednio odwoływać się do klasy,Excel.Application zamiast tego Excel.Application Type.GetTypeFromProgID pobieramy informacje Activator.CreateInstance o typie i przekazujemy je do w celu utworzenia Excel.Application instancji . Nawiasem mówiąc, w środowisku, w którym Excel nie jest zainstalowany, nie można go uzyskać w Type , więc można wyizolować, czy jest zainstalowany, Type.GetTypeFromProgID czy nie.

dynamic Wadą używania jest to, że klasy nie można określić podczas budowania programu. Funkcja IntelliSense, która jest funkcją uzupełniania podczas wprowadzania kodu, nie wyświetla nazw metod i nazw właściwości. Mniej kłopotliwe może być pisanie kodu z odniesieniem do biblioteki na początku i zastępowanie go dynamic późniejszym.

W przypadku uruchamiania w środowisku platformy Microsoft 365 można potwierdzić, że jest on odzwierciedlany w następujący sposób.

Poniżej przedstawiono wynik uruchomienia go w znacznie starszej wersji pakietu Office 2007. Jeśli nie używasz funkcji, która działa tylko w określonej wersji, możesz sprawić, by działała dość szeroko.

Nawiasem mówiąc, jeśli uruchomisz go w środowisku, w którym program Excel nie jest zainstalowany, możesz poprawnie określić, że nie można go przetworzyć.