עבודה עם Excel ב- .NET ללא תלות בגירסה המותקנת של Excel
סביבת הפעלה
- Visual Studio
-
- Visual Studio 2022
- .רשת
-
- .NET 6.0
- חלונות
-
- חלונות 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 המותקנת בסביבה שבה היא פועלת. לדוגמה, תוכנית שנוצרה על-ידי הפניה לספריה הפנימית של Excel בגירסה 15.0 (2013) יכולה לפעול רק כאשר Excel 2013 מותקן. אם ברצונך שהתוכנית תפעל בסביבות רבות, ייתכן שיהיה עליך להפעיל את התקנת Excel בגירסה עקבית.
אם פעולה כזו אפשרית, זה בסדר, אבל אם הגירסה המותקנת של Excel שונה, זה לא יכול להיות מאוכלס. לכן, יש צורך לעשות משהו בצד התוכנית.
הפניה דינמית לספריות עם דינמיות במקום הפניה ישירה אליהן
הבעיה במקרה זה מתרחשת מאחר שקיימת הפניה לגירסת הספריה שתוקנה בעת יצירת התוכנית. משמעות הדבר היא כי במקום לקבוע את הגירסה בזמן יצירת התוכנית, יש צורך לקבוע את הגירסה בזמן ביצוע התוכנית.
ישנן מספר דרכים לעשות זאת, אך הפעם נפתור זאת באמצעות "", "" ו- "Type.GetTypeFromProgID
Activator.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);
}
סוג ההצהרה של המשתנה הוא dynamic
, והיצירה Excel.Application
של שונה במקצת, אך שאר הקוד נמצא כמעט בשימוש חוזר.
מאחר שהקוד לעיל אינו מתייחס לספריית Excel, לא ניתן להשתמש במידע המחלקה הקשור ל- Excel המהווה יעד. לכן, סוג המשתנה הוא כל dynamic
.
כמו כן, מכיוון שאיננו יכולים להפנות ישירות למחלקה,Excel.Application
במקום זאת Type.GetTypeFromProgID
Excel.Application
אנו מכניסים את המידע Activator.CreateInstance
Type ומעבירים אותו אל כדי ליצור Excel.Application
מופע של .
אגב, בסביבה שבה Excel אינו מותקן, Type.GetTypeFromProgID
אתה לא יכול לקבל את זה , Type
ולכן ניתן לבודד אם הוא מותקן או לא.
dynamic
החיסרון בשימוש הוא שלא ניתן לקבוע את הכיתה בעת בניית התוכנית.
Intellisense, שהיא פונקציית השלמה בעת הזנת קוד, אינה מציגה שמות שיטות ושמות מאפיינים.
זה עשוי להיות פחות מסורבל לכתוב קוד עם התייחסות לספרייה בהתחלה ולהחליף אותו מאוחר dynamic
יותר.
בעת הפעלה בסביבת Microsoft 365, באפשרותך לאשר שהיא משתקפת באופן הבא.
להלן התוצאה של הפעלתו בגירסה ישנה בהרבה של Office 2007. אם אינך משתמש בפונקציה שפועלת רק בגירסה ספציפית, תוכל לגרום לה לפעול באופן נרחב למדי.
אגב, אם אתה מפעיל אותו בסביבה שבה Excel אינו מותקן, אתה יכול לקבוע כראוי כי זה לא ניתן לעבד.