ทํางานกับ Excel ใน .NET โดยไม่ขึ้นกับ Excel เวอร์ชันที่ติดตั้ง

ปรับปรุงหน้า :
วันที่สร้างเพจ :

สภาพแวดล้อมในการทํางาน

วิชวลสตูดิโอ
  • วิชวลสตูดิโอ 2022
ตาข่าย
  • .NET 6.0
หน้าต่าง
  • หน้าต่าง 7
  • หน้าต่าง 11
ยอดเยี่ยม
  • ไมโครซอฟท์ 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 ที่ติดตั้งในสภาพแวดล้อมที่กําลังทํางานอยู่ ตัวอย่างเช่น โปรแกรมที่สร้างขึ้นโดยอ้างอิงไลบรารี Excel เวอร์ชัน 15.0 (2013) ภายในสามารถเรียกใช้เมื่อติดตั้ง Excel 2013 เท่านั้น ถ้าคุณต้องการให้โปรแกรมของคุณทํางานในสภาพแวดล้อมต่างๆ คุณอาจต้องให้การติดตั้ง Excel ของคุณทํางานบนเวอร์ชันที่สอดคล้องกัน

หากการดําเนินการดังกล่าวเป็นไปได้ก็ไม่เป็นไร แต่ถ้า Excel เวอร์ชันที่ติดตั้งแตกต่างกันก็ไม่สามารถรองรับได้ ดังนั้นจึงจําเป็นต้องทําอะไรบางอย่างในด้านโปรแกรม

การอ้างอิงไลบรารีแบบไดนามิกด้วยไดนามิกแทนที่จะอ้างอิงโดยตรง

ปัญหาในกรณีนี้เกิดขึ้นเนื่องจากรุ่นของไลบรารีถูกอ้างอิงได้รับการแก้ไขเมื่อโปรแกรมถูกสร้างขึ้น ซึ่งหมายความว่าแทนที่จะกําหนดเวอร์ชันในขณะที่สร้างโปรแกรมจําเป็นต้องกําหนดเวอร์ชันในขณะที่เรียกใช้โปรแกรม

มีหลายวิธีในการทํา แต่คราวนี้เราจะแก้ปัญหาโดยใช้ "" "" และ ""Type.GetTypeFromProgIDActivator.CreateInstancedynamic

โดยปกติ ใน 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);
}

ประเภทของการประกาศตัวแปรคือ dynamic และ Excel.Application การสร้างแตกต่างกันเล็กน้อย แต่รหัสที่เหลือจะถูกนํากลับมาใช้ใหม่เกือบทั้งหมด

เนื่องจากโค้ดด้านบนไม่ได้อ้างถึงไลบรารี Excel จึงไม่สามารถใช้ข้อมูลคลาสที่เกี่ยวข้องกับ Excel เป้าหมายได้ ดังนั้นประเภทของตัวแปรคือ ทั้งหมด dynamic . นอกจากนี้ เนื่องจากเราไม่สามารถอ้างอิงคลาสได้โดยตรงExcel.Application เราจึงExcel.ApplicationType.GetTypeFromProgIDได้รับข้อมูลActivator.CreateInstanceประเภทและส่งต่อไปยังเพื่อสร้างExcel.Applicationอินสแตนซ์ของ . อย่างไรก็ตามในสภาพแวดล้อมที่ไม่ได้ติดตั้ง Type.GetTypeFromProgID Excel คุณไม่สามารถรับได้ Type ดังนั้นจึงเป็นไปได้ที่จะแยกได้ว่ามีการติดตั้งหรือไม่

dynamicข้อเสียของการใช้คือไม่สามารถกําหนดคลาสได้เมื่อสร้างโปรแกรม Intellisense ซึ่งเป็นฟังก์ชันเสร็จสิ้นเมื่อป้อนรหัส จะไม่แสดงชื่อเมธอดและชื่อคุณสมบัติ การเขียนโค้ดโดยอ้างอิงถึงไลบรารีในตอนเริ่มต้นอาจยุ่งยากน้อยกว่าและแทนที่ในภายหลังdynamic

เมื่อทํางานในสภาพแวดล้อม Microsoft 365 คุณสามารถยืนยันได้ว่าจะแสดงดังนี้

ต่อไปนี้เป็นผลมาจากการเรียกใช้บน Office 2007 รุ่นเก่ากว่ามาก หากคุณไม่ได้ใช้ฟังก์ชันที่ใช้งานได้เฉพาะในเวอร์ชันใดเวอร์ชันหนึ่งคุณสามารถทําให้ใช้งานได้อย่างกว้างขวาง

อย่างไรก็ตามหากคุณเรียกใช้ในสภาพแวดล้อมที่ไม่ได้ติดตั้ง Excel คุณสามารถระบุได้อย่างถูกต้องว่าไม่สามารถประมวลผลได้