Trabalhar com o Excel no .NET independentemente da versão instalada do Excel

Página atualizada :
Data de criação de página :

Ambiente operacional

Estúdio Visual
  • Visual Studio 2022
.REDE
  • .NET 6.0
Windows
  • Janelas 7
  • Janelas 11
Distinguir-se
  • Microsoft 365
  • Office 2007

Pré-requisitos

Windows
  • Uma das versões
Distinguir-se
  • Uma das versões

Problemas com bibliotecas que funcionam com o Excel

Uma maneira de interagir programaticamente com o Excel é Microsoft.Office.Interop.Excel fazer referência ao . Isso é semelhante a trabalhar diretamente com o aplicativo Excel via COM, por isso é um pouco diferente de trabalhar com dados em um arquivo do Excel. Como premissa, o Excel deve ser instalado no ambiente a ser executado, mas muitas funções do Excel estão disponíveis do lado do programa.

Microsoft.Office.Interop.Excel Se você usar o programa, você vai pensar que o programa será escrito da seguinte maneira.

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

É problemático ter que liberar recursos todas as vezes, mas como você pode usar as classes preparadas, você pode executar o processamento do Excel de forma relativamente intuitiva.

A desvantagem, no entanto, é que a versão da biblioteca referenciada pelo programa deve corresponder ou ser compatível com a versão do Excel instalada no ambiente em que está sendo executada. Por exemplo, um programa criado fazendo referência à biblioteca interna da versão 15.0 (2013) do Excel só pode ser executado quando o Excel 2013 está instalado. Se você quiser que seu programa seja executado em muitos ambientes, talvez seja necessário manter a instalação do Excel em execução em uma versão consistente.

Se tal operação for possível, tudo bem, mas se a versão instalada do Excel for diferente, ela não poderá ser acomodada. Portanto, é necessário fazer algo do lado do programa.

Referenciando bibliotecas dinamicamente com dinâmicas em vez de referenciar diretamente

O problema nesse caso ocorre porque a versão da biblioteca é referenciada corrigida quando o programa é criado. Isso significa que, em vez de determinar a versão no momento da criação do programa, é necessário determinar a versão no momento da execução do programa.

Existem várias maneiras de fazê-lo, mas desta vez vamos resolvê-lo usando "" "" e "Type.GetTypeFromProgIDActivator.CreateInstance".dynamic

Normalmente, em C#, é necessário determinar as informações de tipo no momento da criação do programa, mas se o tipo for dynamic , o tipo pode ser determinado no momento da execução. object Ao contrário dynamic dos tipos, se a instância definida como uma variável for uma classe, você também poderá chamar os métodos e as propriedades dessa classe. No entanto, como o que entra na variável é determinado no momento da execução, ocorrerá um erro no tempo de execução se não houver nenhum método especificado.

O código criado usando estes é o seguinte.

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

O tipo da declaração da variável é , e Excel.Application a criação de é ligeiramente diferente, mas o resto do código é dynamic quase reutilizado.

Como o código acima não se refere à biblioteca do Excel, não é possível usar as informações de classe relacionadas ao Excel de destino. Portanto, o tipo da variável é todo dynamic . Além disso, como não podemos fazer referência à classe diretamente, em vez disso Type.GetTypeFromProgID Excel.Application,Excel.Application obtemos as informações Activator.CreateInstance Type e as passamos para criar Excel.Application uma instância de . A propósito, em um ambiente onde o Excel não está instalado, você não pode obtê-lo em Type , Type.GetTypeFromProgID então é possível isolar se ele está instalado ou não.

dynamic A desvantagem de usar é que a classe não pode ser determinada ao criar o programa. O Intellisense, que é uma função de conclusão ao inserir código, não exibe nomes de método e nomes de propriedade. Pode ser menos complicado escrever código com referência à biblioteca no início e substituí-lo por dynamic mais tarde.

Ao executar em um ambiente do Microsoft 365, você pode confirmar que ele é refletido da seguinte maneira.

A seguir está o resultado de executá-lo em uma versão muito mais antiga do Office 2007. Se você não usa uma função que só funciona em uma versão específica, você pode fazê-la funcionar amplamente.

A propósito, se você executá-lo em um ambiente onde o Excel não está instalado, você pode determinar corretamente que ele não pode ser processado.