Sheet1・・・保護なし(保護に使用)
Sheet2・・・保護なし(保護に使用)
Sheet3・・・保護あり・パスなし(保護解除に使用)
Sheet4・・・保護あり・パスあり(保護解除に使用)

ExcelVBA

Sub Sample()
    Dim BookObj     As Workbook
    Dim FilePath    As String
    Dim SheetObj    As Worksheet
    
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath)
    
    'パスワード無し保護
    Set SheetObj = BookObj.Sheets(1)
    SheetObj.Protect ("")
    
    'パスワード有り保護
    Set SheetObj = BookObj.Sheets(2)
    SheetObj.Protect ("pass")
    
    'パスワード無し解除
    Set SheetObj = BookObj.Sheets(3)
    SheetObj.Unprotect ("")
    
    'パスワード有り解除
    Set SheetObj = BookObj.Sheets(4)
    SheetObj.Unprotect ("pass")
    
    '保存
    BookObj.Save
    
    '閉じる
    BookObj.Close
    
    Set BookObj = Nothing
End Sub

Excel(COM)

using Microsoft.Office.Interop.Excel;
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? worksheet = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // シートを取得
            worksheets = workbook.Sheets;

            // パスワード無し保護
            worksheet = worksheets[1];
            worksheet.Protect("");
            CleanUpComObject(ref worksheet);

            // パスワード有り保護
            worksheet = worksheets[2];
            worksheet.Protect("pass");
            CleanUpComObject(ref worksheet);

            // パスワード無し解除
            worksheet = worksheets[3];
            worksheet.Unprotect("");
            CleanUpComObject(ref worksheet);

            // パスワード有り保護
            worksheet = worksheets[4];
            worksheet.Unprotect("pass");
            CleanUpComObject(ref worksheet);

            // 保存
            workbook.Save();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref worksheet);
            CleanUpComObject(ref worksheets);
            CleanUpComObject(ref workbook);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

補足:Microsoft.Office.Interop.Excel でオブジェクトの解放について

ClosedXML

using ClosedXML.Excel;
using DocumentFormat.OpenXml.Spreadsheet;
using System;
using System.IO;
using System.Linq;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XLWorkbook(fileStream))
            {
                // パスワード無し保護
                var sheet1 = workbook.Worksheet(1);
                sheet1.Protect(); 

                // パスワード有り保護
                var sheet2 = workbook.Worksheet(2);
                sheet2.Protect("pass"); 

                // パスワード無し解除
                var sheet3 = workbook.Worksheet(3);
                sheet3.Unprotect();

                // パスワード有り解除
                var sheet4 = workbook.Worksheet(4);
                sheet4.Unprotect("pass");

                // 保存
                workbook.Save();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var package = new ExcelPackage(readStream))
            {
                // パスワード無し保護
                var sheet1 = package.Workbook.Worksheets[0];
                sheet1.Protection.IsProtected = true;

                // パスワード有り保護
                var sheet2 = package.Workbook.Worksheets[1];
                sheet2.Protection.SetPassword("pass"); // IsProtectedは指定しなくてもパスがかかる

                // パスワード無し解除
                var sheet3 = package.Workbook.Worksheets[2];
                sheet3.Protection.IsProtected = false;

                // パスワード有り解除
                var sheet4 = package.Workbook.Worksheets[3];
                sheet4.Protection.IsProtected = false; // パスが設定されていてもこれで解除
                
                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    package.SaveAs(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"エラー: {ex.Message}");
        }
    }
}

ExcelDataReader

読み取りのみのライブラリのためなし

NPOI

□xls

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xls");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = new HSSFWorkbook(readStream))
            {
                // パスワード無し保護
                var sheet1 = workbook.GetSheetAt(0);
                sheet1.ProtectSheet("");

                // パスワード有り保護
                var sheet2 = workbook.GetSheetAt(1);
                sheet2.ProtectSheet("pass");

                // パスワード無し解除
                var sheet3 = workbook.GetSheetAt(2);
                sheet3.ProtectSheet(null); // パスワード無しなら解除できた

                // パスワード有り解除
                var sheet4 = workbook.GetSheetAt(3);
                // ※解除方法はなさそう

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsx

using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = new XSSFWorkbook(readStream))
            {
                // パスワード無し保護
                var sheet1 = workbook.GetSheetAt(0);
                sheet1.ProtectSheet("");

                // パスワード有り保護
                var sheet2 = workbook.GetSheetAt(1);
                sheet2.ProtectSheet("pass");

                // パスワード無し解除
                var sheet3 = workbook.GetSheetAt(2);
                sheet3.ProtectSheet(null); // パスワード無しなら解除できた

                // パスワード有り解除
                var sheet4 = workbook.GetSheetAt(3);
                // ※解除方法はなさそう

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsもxlsxも扱う場合
Program.cs

using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            var extension = Path.GetExtension(filePath);

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = WorkbookFactory.Create(readStream, extension))
            {
                // パスワード無し保護
                var sheet1 = workbook.GetSheetAt(0);
                sheet1.ProtectSheet("");

                // パスワード有り保護
                var sheet2 = workbook.GetSheetAt(1);
                sheet2.ProtectSheet("pass");

                // パスワード無し解除
                var sheet3 = workbook.GetSheetAt(2);
                sheet3.ProtectSheet(null); // パスワード無しなら解除できた

                // パスワード有り解除
                var sheet4 = workbook.GetSheetAt(3);
                // ※解除方法はなさそう

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode
{
    public class WorkbookFactory
    {
        public static IWorkbook Create(Stream stream, string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(stream),
                ".xlsx" => new XSSFWorkbook(stream),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }

        public static IWorkbook Create(string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(),
                ".xlsx" => new XSSFWorkbook(),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }
    }
}
投稿日時: 2025-05-28 14:55:28

カレントの表示倍率を取得と設定

ExcelVBA

Sub Sample()
    Dim BookObj     As Workbook
    Dim FilePath    As String
    
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath)
    
    '表示倍率の取得
    Debug.Print ActiveWindow.Zoom

    '表示倍率の設定
    ActiveWindow.Zoom = 200
    
    '保存
    BookObj.Save
    
    '閉じる
    BookObj.Close
    
    Set BookObj = Nothing
End Sub

Excel(COM)

using Microsoft.Office.Interop.Excel;
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? worksheet1 = null;
        Excel.Worksheet? worksheet2 = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // 表示倍率の取得
            Console.WriteLine(application.ActiveWindow.Zoom);

            // 表示倍率の設定
            application.ActiveWindow.Zoom = 200;

            // 保存
            workbook.Save();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref worksheet1);
            CleanUpComObject(ref worksheet2);
            CleanUpComObject(ref worksheets);
            CleanUpComObject(ref workbook);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

補足:Microsoft.Office.Interop.Excel でオブジェクトの解放について

ClosedXML

using ClosedXML.Excel;
using System;
using System.IO;
using System.Linq;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XLWorkbook(fileStream))
            {
                // 表示倍率の取得
                var activeSheet = workbook.Worksheets.First(ws => ws.TabActive);
                Console.WriteLine(activeSheet.SheetView.ZoomScale);

                // 表示倍率の設定
                activeSheet.SheetView.ZoomScale = 200;

                // 保存
                workbook.Save();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var package = new ExcelPackage(readStream))
            {
                // Activeなシートを取得
                var sheetNo = package.Workbook.View.ActiveTab;
                var sheet = package.Workbook.Worksheets[sheetNo];

                // 表示倍率の取得
                Console.WriteLine(sheet.View.ZoomScale);

                // 表示倍率の設定
                sheet.View.ZoomScale = 200;

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    package.SaveAs(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"エラー: {ex.Message}");
        }
    }
}

ExcelDataReader

読み取りのみのライブラリのためなし

NPOI

□xls

using NPOI.HSSF.UserModel;
using System;
using System.IO;
using System.Reflection;


namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xls");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = new HSSFWorkbook(readStream))
            {
                // Activeなシートを取得
                var sheetNo = workbook.ActiveSheetIndex;
                var sheet = workbook.GetSheetAt(sheetNo);

                // 表示倍率の取得(標準APIで提供されていない)

                // 表示倍率の設定
                sheet.SetZoom(200);

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsx

using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = new XSSFWorkbook(readStream))
            {
                // Activeなシートを取得
                var sheetNo = workbook.ActiveSheetIndex;
                var sheet = workbook.GetSheetAt(sheetNo);

                // 表示倍率の取得(標準APIで提供されていない)

                // 表示倍率の設定
                sheet.SetZoom(200);

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsもxlsxも扱う場合
Program.cs

using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            var extension = Path.GetExtension(filePath);

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = WorkbookFactory.Create(readStream, extension))
            {
                // Activeなシートを取得
                var sheetNo = workbook.ActiveSheetIndex;
                var sheet = workbook.GetSheetAt(sheetNo);

                // 表示倍率の取得(標準APIで提供されていない)

                // 表示倍率の設定
                sheet.SetZoom(200);

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode
{
    public class WorkbookFactory
    {
        public static IWorkbook Create(Stream stream, string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(stream),
                ".xlsx" => new XSSFWorkbook(stream),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }

        public static IWorkbook Create(string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(),
                ".xlsx" => new XSSFWorkbook(),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }
    }
}
投稿日時: 2025-05-27 15:14:27

3つシートがあり、Sheet1とSheet2をIndexと名前を指定して削除してみます

ExcelVBA

Sub Sample()
    Dim BookObj     As Workbook
    Dim FilePath    As String
    
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath)
    
    
    Application.DisplayAlerts = False 'ダイアログ表示抑制
    
    'Index番号を指定して削除
    BookObj.Worksheets(1).Delete
    
    '名前を指定して削除
    BookObj.Sheets("Sheet2").Delete
    
    Application.DisplayAlerts = True 'ダイアログ表示抑制解除

    '閉じる
    BookObj.Save
    
    Set BookObj = Nothing
End Sub

Excel(COM)

using Microsoft.Office.Interop.Excel;
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? worksheet1 = null;
        Excel.Worksheet? worksheet2 = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // シートを取得
            worksheets = workbook.Sheets;

            application.DisplayAlerts = false; // ダイアログ表示抑制

            // Index番号で取得して削除
            worksheet1 = worksheets[1];
            worksheet1.Delete();

            // 名前で取得して削除
            worksheet2 = worksheets["Sheet2"];
            worksheet2.Delete();

            application.DisplayAlerts = true; // ダイアログ表示抑制解除

            // 保存
            workbook.Save();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref worksheet1);
            CleanUpComObject(ref worksheet2);
            CleanUpComObject(ref worksheets);
            CleanUpComObject(ref workbook);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

補足:Microsoft.Office.Interop.Excel でオブジェクトの解放について

ClosedXML

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XLWorkbook(fileStream))
            {
                // Index番号で取得して削除
                workbook.Worksheet(1).Delete();

                // 名前で取得して削除
                workbook.Worksheet("Sheet2").Delete();

                // 保存
                workbook.Save();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var package = new ExcelPackage(readStream))
            {
                // Index番号で取得して削除
                package.Workbook.Worksheets.Delete(0);

                // 名前で取得して削除
                package.Workbook.Worksheets.Delete("Sheet2");

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    package.SaveAs(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"エラー: {ex.Message}");
        }
    }
}

ExcelDataReader

読み取りのみのライブラリのためなし

NPOI

□xls

using NPOI.HSSF.UserModel;
using System;
using System.IO;
using System.Reflection;


namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xls");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = new HSSFWorkbook(readStream))
            {
                // Index番号を指定して削除
                workbook.RemoveSheetAt(0);

                // 名前を指定して直接削除はできないっぽい
                var index = workbook.GetSheetIndex("Sheet2");
                workbook.RemoveSheetAt(index); // 名前→Index番号→Index番号で削除

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsx

using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = new XSSFWorkbook(readStream))
            {
                // Index番号を指定して削除
                workbook.RemoveSheetAt(0);

                // 名前を指定して直接削除はできないっぽい
                var index = workbook.GetSheetIndex("Sheet2");
                workbook.RemoveSheetAt(index); // 名前→Index番号→Index番号で削除

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsもxlsxも扱う場合
Program.cs

using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            var extension = Path.GetExtension(filePath);

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = WorkbookFactory.Create(readStream, extension))
            {
                // Index番号を指定して削除
                workbook.RemoveSheetAt(0);

                // 名前を指定して直接削除はできないっぽい
                var index = workbook.GetSheetIndex("Sheet2");
                workbook.RemoveSheetAt(index); // 名前→Index番号→Index番号で削除

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode
{
    public class WorkbookFactory
    {
        public static IWorkbook Create(Stream stream, string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(stream),
                ".xlsx" => new XSSFWorkbook(stream),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }

        public static IWorkbook Create(string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(),
                ".xlsx" => new XSSFWorkbook(),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }
    }
}
投稿日時: 2025-05-27 14:33:27

シート名を変更する

ExcelVBA

Sub Sample()
    Dim BookObj     As Workbook
    Dim FilePath    As String
    Dim SheetObj    As Worksheet
    
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath)
    
    '最初のシートを取得
    Set SheetObj = BookObj.Sheets(1)
    
    'シート名を変更
    SheetObj.Name = "abc"
    
    '閉じる
    BookObj.Save
    
    Set BookObj = Nothing
    Set SheetObj = Nothing
End Sub

Excel(COM)

using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? worksheet = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // シートを取得
            worksheets = workbook.Sheets;

            // 最初のシートを取得
            worksheet = worksheets[1];

            // シート名変更
            worksheet.Name = "abc";

            // 保存
            workbook.Save();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref worksheet);
            CleanUpComObject(ref worksheets);
            CleanUpComObject(ref workbook);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

補足:Microsoft.Office.Interop.Excel でオブジェクトの解放について

ClosedXML

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XLWorkbook(stream))
            {
                // 最初のシートを取得
                var worksheet = workbook.Worksheet(1);

                // シート名変更
                worksheet.Name = "abc";

                // 上書き保存
                workbook.Save();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var package = new ExcelPackage(readStream))
            {
                // シートを取得
                var worksheet = package.Workbook.Worksheets[0];
                worksheet.Name = "abc";

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    package.SaveAs(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"エラー: {ex.Message}");
        }
    }
}

ExcelDataReader

読み取りのみのライブラリのためなし

NPOI

□xls

using NPOI.HSSF.UserModel;
using System;
using System.IO;
using System.Reflection;


namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xls");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = new HSSFWorkbook(readStream))
            {
                // 最初のシートの名前を変える
                workbook.SetSheetName(0, "abc");

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsx

using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = new XSSFWorkbook(readStream))
            {
                // 最初のシートの名前を変える
                workbook.SetSheetName(0, "abc");

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsもxlsxも扱う場合
Program.cs

using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            var extension = Path.GetExtension(filePath);

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = WorkbookFactory.Create(readStream, extension))
            {
                // 最初のシートの名前を変える
                workbook.SetSheetName(0, "abc");

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode
{
    public class WorkbookFactory
    {
        public static IWorkbook Create(Stream stream, string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(stream),
                ".xlsx" => new XSSFWorkbook(stream),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }

        public static IWorkbook Create(string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(),
                ".xlsx" => new XSSFWorkbook(),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }
    }
}
投稿日時: 2025-05-27 11:57:27
更新日時: 2025-05-27 12:10:27

ExcelVBA

Sub Sample()
    Dim BookObj     As Workbook
    Dim FilePath    As String
    
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath)
    
    '上書き保存
    BookObj.Save
    
    '閉じる
    BookObj.Close
    
    Set BookObj = Nothing
End Sub

Excel(COM)

using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
using System;
using System.Data;
using Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // 上書き保存
            workbook.Save();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref workbook);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);

            // RCW強制解放(残留プロセス対策)
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

補足:Microsoft.Office.Interop.Excel でオブジェクトの解放について

ClosedXML

ファイルパスでもFileStreamでもSaveで上書きできる
□ファイルパス

using ClosedXML.Excel;
using System;
using System.IO;
using System.Linq;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            using (var workbook = new XLWorkbook(filePath))
            {
                // 上書き保存
                workbook.Save();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□FileStream

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XLWorkbook(stream))
            {
                // 上書き保存
                workbook.Save();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

□ファイルパス

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            using (var package = new ExcelPackage(filePath))
            {
                // 上書き保存
                package.Save();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□FileInfo

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            var fileInfo = new FileInfo(filePath);

             // 上書き保存
            using (var package = new ExcelPackage(fileInfo))
            {
                package.Save();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□FileStream

using Microsoft.Extensions.FileProviders;
using OfficeOpenXml;
using System;
using System.IO;
using System.IO.Pipes;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using( var readStream = new MemoryStream(fileBytes))
            using (var package = new ExcelPackage(readStream))
            {
                package.Workbook.Worksheets[0].Cells["A1"].Value = DateTime.Now.ToString("HH:mm:ss");

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    package.SaveAs(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

ExcelDataReader

読み取りのみのライブラリのためなし

NPOI

□xls

using NPOI.HSSF.UserModel;
using System;
using System.IO;
using System.Reflection;


namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xls");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = new HSSFWorkbook(readStream))
            {
                // シートを取得
                var worksheet = workbook.GetSheetAt(0);

                var row = worksheet.GetRow(0) ?? worksheet.CreateRow(0);
                var cell = row.GetCell(0) ?? row.CreateCell(0);
                cell.SetCellValue(DateTime.Now.ToString("HH:mm:ss"));

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsx

using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = new XSSFWorkbook(readStream))
            {
                // シートを取得
                var worksheet = workbook.GetSheetAt(0);

                var row = worksheet.GetRow(0) ?? worksheet.CreateRow(0);
                var cell = row.GetCell(0) ?? row.CreateCell(0);
                cell.SetCellValue(DateTime.Now.ToString("HH:mm:ss"));

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xls/xlsx対応版
Program.cs

using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            var extension = Path.GetExtension(filePath);

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = WorkbookFactory.Create(readStream, extension))
            {
                // シートを取得
                var worksheet = workbook.GetSheetAt(0);

                var row = worksheet.GetRow(0) ?? worksheet.CreateRow(0);
                var cell = row.GetCell(0) ?? row.CreateCell(0);
                cell.SetCellValue(DateTime.Now.ToString("HH:mm:ss"));

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode
{
    public class WorkbookFactory
    {
        public static IWorkbook Create(Stream stream, string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(stream),
                ".xlsx" => new XSSFWorkbook(stream),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }

        public static IWorkbook Create(string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(),
                ".xlsx" => new XSSFWorkbook(),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }
    }
}
投稿日時: 2025-05-25 03:38:25
更新日時: 2025-05-26 14:27:26

シートが3つあり、Sheet3がアクティブな状態のExcelを使用

ExcelVBA

Sub Sample()
    Dim BookObj     As Workbook
    Dim FilePath    As String
    Dim SheetObj    As Worksheet
    
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath)
    
    'Activeなシートを取得
    Set SheetObj = BookObj.ActiveSheet
    Debug.Print "Activeなシート名:" & SheetObj.Name ' Activeなシート名:Sheet3
    
    'Activeなシートを変更
    BookObj.Sheets(2).Activate
    
    'Activeなシートを取得
    Set SheetObj = BookObj.ActiveSheet
    Debug.Print "Activeなシート名:" & SheetObj.Name ' Activeなシート名:Sheet2
    
    BookObj.Close (False)
    
    Set BookObj = Nothing
    Set SheetObj = Nothing
End Sub

Excel(COM)

using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
using System;
using System.Data;
using Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? worksheet = null;
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // Activeなシートを取得
            worksheet = workbook.ActiveSheet;
            Console.WriteLine($"Activeなシート名:{worksheet.Name}"); // Activeなシート名:Sheet3
            CleanUpComObject(ref worksheet);

            // Activeなシートを変更
            worksheets = workbook.Sheets;
            worksheet = worksheets[2];
            worksheet.Activate();
            CleanUpComObject(ref worksheet);

            // Activeなシートを取得
            worksheet = workbook.ActiveSheet;
            Console.WriteLine($"Activeなシート名:{worksheet.Name}"); // Activeなシート名:Sheet2
            CleanUpComObject(ref worksheet);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref worksheet);
            CleanUpComObject(ref worksheets);
            CleanUpComObject(ref workbook, true, false);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);

            // RCW強制解放(残留プロセス対策)
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

補足:Microsoft.Office.Interop.Excel でオブジェクトの解放について

ClosedXML

using ClosedXML.Excel;
using System;
using System.IO;
using System.Linq;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XLWorkbook(fileStream))
            {
                // Activeなシートを取得
                var sheet = workbook.Worksheets.First(ws => ws.TabActive);
                Console.WriteLine($"Activeなシート名:{sheet.Name}"); // Activeなシート名:Sheet3

                // Activeなシートを変更
                workbook.Worksheet("Sheet2").SetTabActive();

                // Activeなシートを取得
                sheet = workbook.Worksheets.First(ws => ws.TabActive);
                Console.WriteLine($"Activeなシート名:{sheet.Name}"); // Activeなシート名:Sheet2
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var package = new ExcelPackage(stream))
            {
                // Activeなシート番号を取得(シート番号は0から)
                var sheetNo = package.Workbook.View.ActiveTab; // 2

                // Activeなシートを取得
                var sheet = package.Workbook.Worksheets[sheetNo];
                Console.WriteLine($"Activeなシート名:{sheet.Name}"); // Activeなシート名:Sheet3

                // Activeなシートを変更(シート番号は0から)
                sheetNo = 1;
                package.Workbook.View.ActiveTab = sheetNo;

                // Activeなシートだけtrueにし、それ以外はfalseにする
                for(var n = 0; n < package.Workbook.Worksheets.Count; n++)
                {
                    package.Workbook.Worksheets[n].View.TabSelected = (n == sheetNo);
                }

                // Activeなシートを取得
                sheet = package.Workbook.Worksheets[sheetNo];
                Console.WriteLine($"Activeなシート名:{sheet.Name}"); // Activeなシート名:Sheet2
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

ExcelDataReader

該当機能なし

NPOI

□xls

using NPOI.HSSF.UserModel;
using System;
using System.IO;
using System.Reflection;


namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        var filePath = "";

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            filePath = Path.Combine(folderPath, "sample.xls");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new HSSFWorkbook(fileStream))
            {
                // ■Activeなシートの取得
                // Activeなシート番号を取得(シート番号は0から)
                var sheetNo = workbook.ActiveSheetIndex; // 2

                // Activeなシートを取得
                var sheet = workbook.GetSheetAt(sheetNo);
                Console.WriteLine($"Activeなシート名:{sheet.SheetName}");

                // ■Activeなシートの変更
                // Activeなシートを変更
                sheetNo = 1;
                workbook.SetActiveSheet(sheetNo); // Sheet2をActiveに設定する(これを変えただけじゃかわらない)
                workbook.SetSelectedTab(sheetNo); //こちらも変えるといいとのこと

                // 上記2つの設定で変わったが・・・
                // IsSelectedまで返ると完璧とのこと
                for(var n = 0; n < workbook.NumberOfSheets; n++)
                {
                    workbook.GetSheetAt(n).IsSelected = (n == sheetNo);
                }

                // Activeなシートを取得
                sheet = workbook.GetSheetAt(sheetNo);
                Console.WriteLine($"Activeなシート名:{sheet.SheetName}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsx

using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        var filePath = "";

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XSSFWorkbook(fileStream))
            {
                // ■Activeなシートの取得
                // Activeなシート番号を取得(シート番号は0から)
                var sheetNo = workbook.ActiveSheetIndex; // 2

                // Activeなシートを取得
                var sheet = workbook.GetSheetAt(sheetNo);
                Console.WriteLine($"Activeなシート名:{sheet.SheetName}");

                // ■Activeなシートの変更
                // Activeなシートを変更
                sheetNo = 1;
                workbook.SetActiveSheet(sheetNo); // Sheet2をActiveに設定する(これを変えただけじゃかわらない)
                workbook.SetSelectedTab(sheetNo); //こちらも変えるといいとのこと

                // 上記2つの設定で変わったが・・・
                // IsSelectedまで返ると完璧とのこと
                for (var n = 0; n < workbook.NumberOfSheets; n++)
                {
                    workbook.GetSheetAt(n).IsSelected = (n == sheetNo);
                }

                // Activeなシートを取得
                sheet = workbook.GetSheetAt(sheetNo);
                Console.WriteLine($"Activeなシート名:{sheet.SheetName}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsもxlsxも扱う場合
Program.cs

using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        var filePath = "";

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            filePath = Path.Combine(folderPath, "sample.xlsx");

            var extension = Path.GetExtension(filePath);

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = WorkbookFactory.Create(fileStream, extension))
            {
                // ■Activeなシートの取得
                // Activeなシート番号を取得(シート番号は0から)
                var sheetNo = workbook.ActiveSheetIndex; // 2

                // Activeなシートを取得
                var sheet = workbook.GetSheetAt(sheetNo);
                Console.WriteLine($"Activeなシート名:{sheet.SheetName}");

                // ■Activeなシートの変更
                // Activeなシートを変更
                sheetNo = 1;
                workbook.SetActiveSheet(sheetNo); // Sheet2をActiveに設定する(これを変えただけじゃかわらない)
                workbook.SetSelectedTab(sheetNo); //こちらも変えるといいとのこと

                // 上記2つの設定で変わったが・・・
                // IsSelectedまで返ると完璧とのこと
                for (var n = 0; n < workbook.NumberOfSheets; n++)
                {
                    workbook.GetSheetAt(n).IsSelected = (n == sheetNo);
                }

                // Activeなシートを取得
                sheet = workbook.GetSheetAt(sheetNo);
                Console.WriteLine($"Activeなシート名:{sheet.SheetName}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode
{
    public class WorkbookFactory
    {
        public static IWorkbook Create(Stream stream, string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(stream),
                ".xlsx" => new XSSFWorkbook(stream),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }

        public static IWorkbook Create(string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(),
                ".xlsx" => new XSSFWorkbook(),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }
    }
}
投稿日時: 2025-05-24 18:23:24
更新日時: 2025-05-27 15:13:27

Sheet1シートがあるファイルを開き、
シート名で参照する方法と、シート番号で参照する方法
また、存在しないシート名を指定するとどうなるか確認

ExcelVBA

シート番号と名前でアクセスする方法
シート番号は1から始まることに注意
存在しない場合はエラーとなるので例外の対応を行っておく

Sub Sample()
    Dim BookObj     As Workbook
    Dim FilePath    As String
    Dim SheetObj    As Worksheet
    
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath)
    
    'シート番号で参照
    Set SheetObj = BookObj.Sheets(1)
    Debug.Print "シート番号で参照:" & SheetObj.Name
    
    '名前で参照
    Set SheetObj = BookObj.Sheets("Sheet1")
    Debug.Print "シート名で参照:" & SheetObj.Name
    
    On Error Resume Next
    '存在しない名前を指定して参照 => 例外が発生する
    Set SheetObj = BookObj.Sheets("AAA")
    
    If ERR.Number <> 0 Then
        Debug.Print ERR.Description 'インデックスが有効範囲にありません。
    End If
    
    On Error GoTo 0 'On Error Resume Next解除
    
    '保存せず閉じる
    BookObj.Close (False)
    
    Set BookObj = Nothing
    Set SheetObj = Nothing
End Sub

Excel(COM)

using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
using System;
using System.Data;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? worksheet = null;
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // シートを取得
            worksheets = workbook.Sheets;

            // シート番号で参照
            worksheet = worksheets[1];
            Console.WriteLine($"シート番号で参照:{worksheet.Name}");
            CleanUpComObject(ref worksheet);

            // シート名で参照
            worksheet = worksheets["Sheet1"];
            Console.WriteLine($"シート名で参照:{worksheet.Name}");
            CleanUpComObject(ref worksheet);

            try
            {
                // 存在しない名前を指定して参照 => 例外が発生する
                worksheet = worksheets["AAA"];
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message); // インデックスが無効です。 (0x8002000B (DISP_E_BADINDEX))
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref worksheet);
            CleanUpComObject(ref worksheets);
            CleanUpComObject(ref workbook, true, false);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);

            // RCW強制解放(残留プロセス対策)
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

補足:Microsoft.Office.Interop.Excel でオブジェクトの解放について

ClosedXML

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XLWorkbook(fileStream))
            {
                // シート番号で参照
                var sheet = workbook.Worksheet(1);
                Console.WriteLine($"シート番号で参照:{sheet.Name}");

                // シート名で参照
                sheet = workbook.Worksheet("Sheet1");
                Console.WriteLine($"シート名で参照:{sheet.Name}");

                // 存在しない名前を指定して参照 => 例外発生する
                try
                {
                    sheet = workbook.Worksheet("AAA"); // There isn't a worksheet named 'AAA'.
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }

                // TryGetWorksheetを使うのも一つの手
                IXLWorksheet? worksheet = null;
                if (!workbook.TryGetWorksheet("AAA", out worksheet))
                {
                    Console.WriteLine("AAAシートないよ");
                }

                // 存在しないシートについては、Containsでtrue/falseを返してくれる
                if (!workbook.Worksheets.Contains("AAA"))
                {
                    Console.WriteLine("AAAシートないよ");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var package = new ExcelPackage(stream))
            {
                // シート番号で参照(シート番号は0から)
                var sheet = package.Workbook.Worksheets[0];
                Console.WriteLine($"シート番号で参照:{sheet.Name}");

                // シート名で参照
                sheet = package.Workbook.Worksheets["Sheet1"];
                Console.WriteLine($"シート名で参照:{sheet.Name}");

                // 存在しない名前を指定して参照 => nullを返す
                sheet = package.Workbook.Worksheets["AAA"];
                if(sheet == null)
                {
                    Console.WriteLine("AAAシートないよ");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

ExcelDataReader

using ExcelDataReader;
using System;
using System.Data;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleExcelDataReader
{
    static void Main(string[] args)
    {
        // デフォルトでは、エンコード(CP1252)がサポートされておらずエラーになるのでこれが必要
        System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ファイル開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (var reader = ExcelReaderFactory.CreateReader(fileStream))
            {
                // DataSetに変換(シート情報を取得)
                var result = reader.AsDataSet();

                // シート番号で参照(シート番号は0から)
                var sheet = result.Tables[0];
                Console.WriteLine($"シート番号で参照:{sheet.TableName}");

                // シート名で参照
                sheet = result.Tables["Sheet1"];
                Console.WriteLine($"シート名で参照:{sheet.TableName}");

                // 存在しない名前を指定して参照 => nullを返す
                sheet = result.Tables["AAA"];
                if (sheet == null)
                {
                    Console.WriteLine("AAAシートないよ");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

NPOI

□xls

using NPOI.HSSF.UserModel;
using System;
using System.IO;
using System.Reflection;


namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        var filePath = "";

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            filePath = Path.Combine(folderPath, "sample.xls");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new HSSFWorkbook(fileStream))
            {
                // シート番号で参照(シート番号は0から)
                var sheet = workbook.GetSheetAt(0);
                Console.WriteLine($"シート番号で参照:{sheet.SheetName}");

                // シート名で参照
                sheet = workbook.GetSheet("Sheet1");
                Console.WriteLine($"シート名で参照:{sheet.SheetName}");

                // 存在しない名前を指定して参照 => nullを返す
                sheet = workbook.GetSheet("AAA");
                if (sheet == null)
                {
                    Console.WriteLine("AAAシートないよ");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsx

using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        var filePath = "";

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XSSFWorkbook(fileStream))
            {
                // シート番号で参照(シート番号は0から)
                var sheet = workbook.GetSheetAt(0);
                Console.WriteLine($"シート番号で参照:{sheet.SheetName}");

                // シート名で参照
                sheet = workbook.GetSheet("Sheet1");
                Console.WriteLine($"シート名で参照:{sheet.SheetName}");

                // 存在しない名前を指定して参照 => nullを返す
                sheet = workbook.GetSheet("AAA");
                if (sheet == null)
                {
                    Console.WriteLine("AAAシートないよ");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsもxlsxも扱う場合
Program.cs

using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        var filePath = "";

        try
        {   // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            filePath = Path.Combine(folderPath, "sample.xlsx");

            var extension = Path.GetExtension(filePath);

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = WorkbookFactory.Create(fileStream, extension))
            {
                // シート番号で参照(シート番号は0から)
                var sheet = workbook.GetSheetAt(0);
                Console.WriteLine($"シート番号で参照:{sheet.SheetName}");

                // シート名で参照
                sheet = workbook.GetSheet("Sheet1");
                Console.WriteLine($"シート名で参照:{sheet.SheetName}");

                // 存在しない名前を指定して参照 => nullを返す
                sheet = workbook.GetSheet("AAA");
                if (sheet == null)
                {
                    Console.WriteLine("AAAシートないよ");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode
{
    public class WorkbookFactory
    {
        public static IWorkbook Create(Stream stream, string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(stream),
                ".xlsx" => new XSSFWorkbook(stream),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }

        public static IWorkbook Create(string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(),
                ".xlsx" => new XSSFWorkbook(),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }
    }
}
投稿日時: 2025-05-24 16:47:24
更新日時: 2025-05-27 14:33:27

ExcelVBA

Sub Sample()
    Dim BookObj         As Workbook
    Dim c               As Long
    Dim FilePath        As String
    Dim r               As Long
    Dim SheetObj        As Worksheet
        
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath)
    
    'シート取得
    Set SheetObj = BookObj.Sheets("Sheet1")
    
    SheetObj.Range("A1").Value = 1
    SheetObj.Range("A2").Value = 1.1
    SheetObj.Range("A3").Value = "abc"
    SheetObj.Range("A4").Value = Now
        
    '閉じる
    BookObj.Close
    
    Set BookObj = Nothing
    Set RangeData = Nothing
    Set SheetObj = Nothing
End Sub

Excel(COM)

using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System;
using Excel = Microsoft.Office.Interop.Excel;
using Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? worksheet = null;
        Excel.Range? cellData = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            
            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // シートを取得
            worksheets = workbook.Sheets;
            worksheet = worksheets["Sheet1"];

            cellData = worksheet.Range["A1"];
            cellData.Value = 1;
            CleanUpComObject(ref cellData);

            cellData = worksheet.Range["A2"];
            cellData.Value = 1.1;
            CleanUpComObject(ref cellData);

            cellData = worksheet.Range["A3"];
            cellData.Value = "abc";
            CleanUpComObject(ref cellData);

            cellData = worksheet.Range["A4"];
            cellData.Value = DateTime.Now;
            CleanUpComObject(ref cellData);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref cellData);
            CleanUpComObject(ref worksheet);
            CleanUpComObject(ref worksheets);
            CleanUpComObject(ref workbook, true, true);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

補足:Microsoft.Office.Interop.Excel でオブジェクトの解放について

ClosedXML

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            
            // ブックを開く、読み書きモード、共有なし(排他)
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XLWorkbook(stream))
            {
                IXLWorksheet? worksheet = null;

                if(!workbook.TryGetWorksheet("Sheet1", out worksheet))
                {
                    throw new Exception("Sheet1シートがありませんでした");
                }

                worksheet.Cell("A1").Value = 1;
                worksheet.Cell("A2").Value = 1.1;
                worksheet.Cell("A3").Value = "abc";
                worksheet.Cell("A4").Value = DateTime.Now;

                workbook.Save();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var package = new ExcelPackage(readStream))
            {
                // シートを取得
                var worksheet = package.Workbook.Worksheets["Sheet1"];

                worksheet.Cells["A1"].Value = 1;
                worksheet.Cells["A2"].Value = 1.1;
                worksheet.Cells["A3"].Value = "abc";
                worksheet.Cells["A4"].Value = DateTime.Now.ToString("yyyy/MM/dd");

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    package.SaveAs(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"エラー: {ex.Message}");
        }
    }
}

ExcelDataReader

読み取りのみのライブラリのためなし

NPOI

□xls

using NPOI.HSSF.UserModel;
using System;
using System.IO;
using System.Reflection;


namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xls");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = new HSSFWorkbook(readStream))
            {
                // シートを取得
                var worksheet = workbook.GetSheet("Sheet1");

                // xls,xlsx共通で該当セルの情報がなければnullになるとのこと
                // 空=nullではないとのこと

                // A1セル
                var row = worksheet.GetRow(0) ?? worksheet.CreateRow(0);
                var cell = row.GetCell(0) ?? row.CreateCell(0);
                cell.SetCellValue(1);

                // A2セル
                row = worksheet.GetRow(1) ?? worksheet.CreateRow(1);
                cell = row.GetCell(0) ?? row.CreateCell(0);
                cell.SetCellValue(1.1);

                // A3セル
                row = worksheet.GetRow(2) ?? worksheet.CreateRow(2);
                cell = row.GetCell(0) ?? row.CreateCell(0);
                cell.SetCellValue("abc");

                // A4セル
                row = worksheet.GetRow(3) ?? worksheet.CreateRow(3);
                cell = row.GetCell(0) ?? row.CreateCell(0);
                cell.SetCellValue(DateTime.Now);

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsx

using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = new XSSFWorkbook(readStream))
            {
                // シートを取得
                var worksheet = workbook.GetSheet("Sheet1");

                // xls,xlsx共通で該当セルの情報がなければnullになるとのこと
                // 空=nullではないとのこと

                // A1セル
                var row = worksheet.GetRow(0) ?? worksheet.CreateRow(0);
                var cell = row.GetCell(0) ?? row.CreateCell(0);
                cell.SetCellValue(1);

                // A2セル
                row = worksheet.GetRow(0) ?? worksheet.CreateRow(0);
                cell = row.GetCell(0) ?? row.CreateCell(0);
                cell.SetCellValue(1.1);

                // A3セル
                row = worksheet.GetRow(0) ?? worksheet.CreateRow(0);
                cell = row.GetCell(0) ?? row.CreateCell(0);
                cell.SetCellValue("abc");

                // A4セル
                row = worksheet.GetRow(0) ?? worksheet.CreateRow(0);
                cell = row.GetCell(0) ?? row.CreateCell(0);
                cell.SetCellValue(DateTime.Now);

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xls/xlsx対応版

Program.cs

using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            var extension = Path.GetExtension(filePath);

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = WorkbookFactory.Create(readStream, extension))
            {
                // シートを取得
                var worksheet = workbook.GetSheet("Sheet1");

                // xls,xlsx共通で該当セルの情報がなければnullになるとのこと
                // 空=nullではないとのこと

                // A1セル
                var row = worksheet.GetRow(0) ?? worksheet.CreateRow(0);
                var cell = row.GetCell(0) ?? row.CreateCell(0);
                cell.SetCellValue(1);

                // A2セル
                row = worksheet.GetRow(0) ?? worksheet.CreateRow(0);
                cell = row.GetCell(0) ?? row.CreateCell(0);
                cell.SetCellValue(1.1);

                // A3セル
                row = worksheet.GetRow(0) ?? worksheet.CreateRow(0);
                cell = row.GetCell(0) ?? row.CreateCell(0);
                cell.SetCellValue("abc");

                // A4セル
                row = worksheet.GetRow(0) ?? worksheet.CreateRow(0);
                cell = row.GetCell(0) ?? row.CreateCell(0);
                cell.SetCellValue(DateTime.Now);

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode; // C#10

public class WorkbookFactory
{
    public static IWorkbook Create(Stream stream, string extension)
    {
        extension = extension.ToLower();

        return extension switch
        {
            ".xls" => new HSSFWorkbook(stream),
            ".xlsx" => new XSSFWorkbook(stream),
            _ => throw new NotSupportedException("対象外の拡張子です")
        };
    }

    public static IWorkbook Create(string extension)
    {
        extension = extension.ToLower();

        return extension switch
        {
            ".xls" => new HSSFWorkbook(),
            ".xlsx" => new XSSFWorkbook(),
            _ => throw new NotSupportedException("対象外の拡張子です")
        };
    }
}
投稿日時: 2025-05-23 23:56:23
更新日時: 2025-05-26 14:57:26

ログを出力する際に、 "3行14列"というよりは、"N3" といった方がわかりやすいだろうから、
アドレスの取得方法について

ExcelVBA

Sub Sample()
    Dim BookObj         As Workbook
    Dim c               As Long
    Dim FilePath        As String
    Dim r               As Long
    Dim SheetObj        As Worksheet
        
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath)
    
    'シート取得
    Set SheetObj = BookObj.Sheets("Sheet1")
    
    '2行、3列の各セルのアドレスを出力
    For r = 1 To 2
        For c = 1 To 3
            Debug.Print r & "行" & c & "列 => " & SheetObj.Cells(r, c).Address
        Next
    Next
        
    '閉じる
    BookObj.Close
    
    Set BookObj = Nothing
    Set RangeData = Nothing
    Set SheetObj = Nothing
End Sub

Excel(COM)

using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System;
using Excel = Microsoft.Office.Interop.Excel;
using Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? worksheet = null;
        Excel.Range? cellData = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            
            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // シートを取得
            worksheets = workbook.Sheets;
            worksheet = worksheets["Sheet1"];

            // 2行、3列の各セルのアドレスを出力
            for (var r = 1; r <= 2; r++)
            {
                for(var c = 1; c <= 3; c++)
                {
                    cellData = worksheet.Cells[r, c];
                    Console.WriteLine($"{r}行{c}列 => {cellData.Address}");

                    CleanUpComObject(ref cellData);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref cellData);
            CleanUpComObject(ref worksheet);
            CleanUpComObject(ref worksheets);
            CleanUpComObject(ref workbook);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

補足:Microsoft.Office.Interop.Excel でオブジェクトの解放について

ClosedXML

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            
            // ブックを開く、読み書きモード、共有なし(排他)
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XLWorkbook(stream))
            {
                IXLWorksheet? worksheet = null;

                if(!workbook.TryGetWorksheet("Sheet1", out worksheet))
                {
                    throw new Exception("Sheet1シートがありませんでした");
                }

                // 2行、3列の各セルのアドレスを出力
                for (var r = 1; r <= 2; r++)
                {
                    for (var c = 1; c <= 3; c++)
                    {
                        Console.WriteLine(worksheet.Cell(r, c).Address);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

using OfficeOpenXml;
using System;
using System.IO;
using System.IO.Pipes;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var package = new ExcelPackage(stream))
            {
                // シートを取得
                var worksheet = package.Workbook.Worksheets["Sheet1"];

                // 2行、3列の各セルのアドレスを出力
                for (var r = 1; r <= 2; r++)
                {
                    for (var c = 1; c <= 3; c++)
                    {
                        Console.WriteLine(worksheet.Cells[r, c].Address); // 行, 列
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"エラー: {ex.Message}");
        }
    }
}

ExcelDataReader

※ライブラリ側にはアドレスを出力する方法はないので、自作する

using ExcelDataReader;
using System;
using System.Data;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleExcelDataReader
{
    static void Main(string[] args)
    {
        // デフォルトでは、エンコード(CP1252)がサポートされておらずエラーになるのでこれが必要
        System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            
            // ファイル開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (var reader = ExcelReaderFactory.CreateReader(fileStream))
            {
                // DataSetに変換(シート情報を取得)
                var result = reader.AsDataSet();
                
                // シート取得
                var worksheet = result.Tables["Sheet1"];

                if(worksheet == null)
                {
                    throw new Exception("Sheet1シートは存在しません");
                }

                // アドレスを出すような仕組みはなし。
                // もしログにA1形式で出力したければ、自分で作ること

                // 2行、3列の各セルのアドレスを出力
                for (var r = 1; r <= 2; r++)
                {
                    for (var c = 1; c <= 3; c++)
                    {
                        var cellAddress = CellAddress(r, c);
                        Console.WriteLine($"{r}行{c}列 => {cellAddress}");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    static string CellAddress(int row, int col)
    {
        string colStr = "";

        while (0 < col)
        {
            col--; // Aのアスキーコードに足すので1減らしておく
            int remainder = col % 26;
            colStr = (char)('A' + remainder) + colStr;
            col /= 26;
        }

        return $"{colStr}{row}";
    }
}

NPOI

※ライブラリ側にはアドレスを出力する方法はないので、自作する

xls

using NPOI.HSSF.UserModel;
using NPOI.SS.Formula.Functions;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xls");
            
            // ブックを開く、読み取りモード、共有なし(排他)
            using (var stRead = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
            using (var workbook = new HSSFWorkbook(stRead))
            {
                // シートを取得
                var worksheet = workbook.GetSheet("Sheet1");

                // アドレスを出すような仕組みはなし。
                // もしログにA1形式で出力したければ、自分で作ること

                // 2行、3列の各セルのアドレスを出力
                for (var r = 1; r <= 2; r++)
                {
                    for (var c = 1; c <= 3; c++)
                    {
                        var cellAddress = CellAddress(r, c);
                        Console.WriteLine($"{r}行{c}列 => {cellAddress}");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    static string CellAddress(int row, int col)
    {
        string colStr = "";

        while (0 < col)
        {
            col--; // Aのアスキーコードに足すので1減らしておく
            int remainder = col % 26;
            colStr = (char)('A' + remainder) + colStr;
            col /= 26;
        }

        return $"{colStr}{row}";
    }
}

xlsx

using NPOI.SS.Util;
using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            
            // ブックを開く、読み取りモード、共有なし(排他)
            using (var stRead = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
            using (var workbook = new XSSFWorkbook(stRead))
            {
                // シートを取得
                var worksheet = workbook.GetSheet("Sheet1");

                // アドレスを出すような仕組みはなし。
                // もしログにA1形式で出力したければ、自分で作ること

                // 2行、3列の各セルのアドレスを出力
                for (var r = 1; r <= 2; r++)
                {
                    for (var c = 1; c <= 3; c++)
                    {
                        var cellAddress = CellAddress(r, c);
                        Console.WriteLine($"{r}行{c}列 => {cellAddress}");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    static string CellAddress(int row, int col)
    {
        string colStr = "";

        while (0 < col)
        {
            col--; // Aのアスキーコードに足すので1減らしておく
            int remainder = col % 26;
            colStr = (char)('A' + remainder) + colStr;
            col /= 26;
        }

        return $"{colStr}{row}";
    }
}

xls/xlsx対応版

Program.cs

using NPOI.SS.Util;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            var extension = Path.GetExtension(filePath);

            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
            using (var workbook = WorkbookFactory.Create(fileStream, extension))
            {
                // シートを取得
                var worksheet = workbook.GetSheet("Sheet1");

                // アドレスを出すような仕組みはなし。
                // もしログにA1形式で出力したければ、自分で作ること

                // 2行、3列の各セルのアドレスを出力
                for (var r = 1; r <= 2; r++)
                {
                    for (var c = 1; c <= 3; c++)
                    {
                        var cellAddress = CellAddress(r, c);
                        Console.WriteLine($"{r}行{c}列 => {cellAddress}");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    static string CellAddress(int row, int col)
    {
        string colStr = "";

        while (0 < col)
        {
            col--; // Aのアスキーコードに足すので1減らしておく
            int remainder = col % 26;
            colStr = (char)('A' + remainder) + colStr;
            col /= 26;
        }

        return $"{colStr}{row}";
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode; // C#10

public class WorkbookFactory
{
    public static IWorkbook Create(Stream stream, string extension)
    {
        extension = extension.ToLower();

        return extension switch
        {
            ".xls" => new HSSFWorkbook(stream),
            ".xlsx" => new XSSFWorkbook(stream),
            _ => throw new NotSupportedException("対象外の拡張子です")
        };
    }

    public static IWorkbook Create(string extension)
    {
        extension = extension.ToLower();

        return extension switch
        {
            ".xls" => new HSSFWorkbook(),
            ".xlsx" => new XSSFWorkbook(),
            _ => throw new NotSupportedException("対象外の拡張子です")
        };
    }
}
投稿日時: 2025-05-21 12:50:21
更新日時: 2025-05-21 12:57:21

ExcelVBA

Sub Sample()
    Dim BookObj         As Workbook
    Dim CellData        As Range
    Dim FilePath        As String
    Dim RangeData       As Range
    Dim SheetObj        As Worksheet
        
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath)
    
    'シート取得
    Set SheetObj = BookObj.Sheets("Sheet1")
    
    'A1形式
    Debug.Print SheetObj.Range("A1").Value
    
    'R1C1形式
    Debug.Print SheetObj.Cells(1, 1).Value
        
    '閉じる
    BookObj.Close
    
    Set BookObj = Nothing
    Set CellData = Nothing
    Set RangeData = Nothing
    Set SheetObj = Nothing
End Sub

Excel(COM)

using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System;
using Excel = Microsoft.Office.Interop.Excel;
using Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? worksheet = null;
        Excel.Range? cellData1 = null;
        Excel.Range? cellData2 = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            
            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // シートを取得
            worksheets = workbook.Sheets;
            worksheet = worksheets["Sheet1"];

            // A1形式
            cellData1 = worksheet.Range["A1"];
            Console.WriteLine(cellData1.Value);

            // R1C1形式
            cellData2 = worksheet.Cells[1, 1];
            Console.WriteLine(cellData2.Value);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref cellData1);
            CleanUpComObject(ref cellData2);
            CleanUpComObject(ref worksheet);
            CleanUpComObject(ref worksheets);
            CleanUpComObject(ref workbook);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

補足:Microsoft.Office.Interop.Excel でオブジェクトの解放について

ClosedXML

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            
            // ブックを開く、読み書きモード、共有なし(排他)
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XLWorkbook(stream))
            {
                IXLWorksheet? worksheet = null;

                if(!workbook.TryGetWorksheet("Sheet1", out worksheet))
                {
                    throw new Exception("Sheet1シートがありませんでした");
                }
                
                // A1形式
                Console.WriteLine(worksheet.Cell("A1").Value);

                // R1C1形式
                Console.WriteLine(worksheet.Cell(1, 1).Value);

            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

using OfficeOpenXml;
using System;
using System.IO;
using System.IO.Pipes;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var package = new ExcelPackage(stream))
            {
                // シートを取得
                var worksheet = package.Workbook.Worksheets["Sheet1"];

                // A1形式
                Console.WriteLine(worksheet.Cells["A1"].Value);

                // R1C1形式
                Console.WriteLine(worksheet.Cells[1, 1].Value); // 行, 列
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"エラー: {ex.Message}");
        }
    }
}

ExcelDataReader

using ExcelDataReader;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleExcelDataReader
{
    static void Main(string[] args)
    {
        // デフォルトでは、エンコード(CP1252)がサポートされておらずエラーになるのでこれが必要
        System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            
            // ファイル開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (var reader = ExcelReaderFactory.CreateReader(fileStream))
            {
                // DataSetに変換(シート情報を取得)
                var result = reader.AsDataSet();
                
                // シート取得
                var worksheet = result.Tables["Sheet1"];

                // A1形式は存在しない

                // R1C1形式(0から始まる)
                Console.WriteLine(worksheet.Rows[0][0]);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    // 列を表す数値から列を表す文字列に変換する
    static string ConvertColumnNumToStr(int colNum)
    {
        var colStr = string.Empty;

        while (colNum > 0)
        {
            int modulo = (colNum - 1) % 26;
            colStr = Convert.ToChar('A' + modulo) + colStr;
            colNum = (colNum - modulo) / 26;
        }

        return colStr;
    }
}

NPOI

xls

using NPOI.HSSF.UserModel;
using NPOI.SS.Formula.Functions;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xls");
            
            // ブックを開く、読み取りモード、共有なし(排他)
            using (var stRead = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
            using (var workbook = new HSSFWorkbook(stRead))
            {
                // シートを取得
                var worksheet = workbook.GetSheet("Sheet1");

                // A1形式は存在しない

                // R1C1形式(0から始まる)
                Console.WriteLine(worksheet.GetRow(0).GetCell(0).StringCellValue);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

xlsx

using NPOI.SS.Util;
using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            
            // ブックを開く、読み取りモード、共有なし(排他)
            using (var stRead = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
            using (var workbook = new XSSFWorkbook(stRead))
            {
                // シートを取得
                var worksheet = workbook.GetSheet("Sheet1");

                // A1形式は存在しない

                // R1C1形式(0から始まる)
                Console.WriteLine(worksheet.GetRow(0).GetCell(0).StringCellValue);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

xlsもxlsxも扱う場合

Program.cs

using NPOI.SS.Util;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            var extension = Path.GetExtension(filePath);

            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
            using (var workbook = WorkbookFactory.Create(fileStream, extension))
            {
                // シートを取得
                var worksheet = workbook.GetSheet("Sheet1");

                // A1形式は存在しない

                // R1C1形式(0から始まる)
                Console.WriteLine(worksheet.GetRow(0).GetCell(0).StringCellValue);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode; // C#10

public class WorkbookFactory
{
    public static IWorkbook Create(Stream stream, string extension)
    {
        extension = extension.ToLower();

        return extension switch
        {
            ".xls" => new HSSFWorkbook(stream),
            ".xlsx" => new XSSFWorkbook(stream),
            _ => throw new NotSupportedException("対象外の拡張子です")
        };
    }

    public static IWorkbook Create(string extension)
    {
        extension = extension.ToLower();

        return extension switch
        {
            ".xls" => new HSSFWorkbook(),
            ".xlsx" => new XSSFWorkbook(),
            _ => throw new NotSupportedException("対象外の拡張子です")
        };
    }
}
投稿日時: 2025-05-19 16:19:19
更新日時: 2025-05-21 03:57:21

ExcelVBA

Sub Sample()
    Dim BookObj         As Workbook
    Dim CellData        As Range
    Dim FilePath        As String
    Dim RangeData       As Range
    Dim SheetObj        As Worksheet
        
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath)
    
    'シート取得
    Set SheetObj = BookObj.Sheets("Sheet1")
    
    '範囲セル取得
    Set RangeData = SheetObj.Range("A1:C2")
    
    '範囲のセルを一つずつ取り出す
    For Each CellData In RangeData
        Debug.Print "アドレス:" & CellData.Address & "/値:" & CellData.Value
    Next
        
    '閉じる
    BookObj.Close
    
    Set BookObj = Nothing
    Set CellData = Nothing
    Set RangeData = Nothing
    Set SheetObj = Nothing
End Sub

Excel(COM)

using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System;
using Excel = Microsoft.Office.Interop.Excel;
using Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? worksheet = null;
        Excel.Range? rangeData = null;
        Excel.Range? cellData = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            
            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // シートを取得
            worksheets = workbook.Sheets;
            worksheet = worksheets["Sheet1"];

            // 範囲セル取得
            rangeData = worksheet.Range["A1", "C2"];

            for (var n = 1; n <= rangeData.Count; n++)
            {
                cellData = rangeData[n];

                Console.WriteLine($"アドレス:{cellData.Address}/値:{cellData.Value}");

                CleanUpComObject(ref cellData);
            }

        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref cellData);
            CleanUpComObject(ref rangeData);
            CleanUpComObject(ref worksheet);
            CleanUpComObject(ref worksheets);
            CleanUpComObject(ref workbook);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

補足:Microsoft.Office.Interop.Excel でオブジェクトの解放について

ClosedXML

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            
            // ブックを開く、読み書きモード、共有なし(排他)
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XLWorkbook(stream))
            {
                IXLWorksheet? worksheet = null;

                if(!workbook.TryGetWorksheet("Sheet1", out worksheet))
                {
                    throw new Exception("Sheet1シートがありませんでした");
                }

                var rangeData = worksheet.Range("A1:C2");

                foreach (var cellData in rangeData.Cells())
                {
                    Console.WriteLine($"アドレス:{cellData.Address}/値:{cellData.Value}");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

using OfficeOpenXml;
using System;
using System.IO;
using System.IO.Pipes;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var package = new ExcelPackage(stream))
            {
                // シートを取得
                var worksheet = package.Workbook.Worksheets["Sheet1"];

                // 範囲セルを取得
                var rangeData = worksheet.Cells["A1:C2"];

                foreach (var cellData in rangeData)
                {
                    Console.WriteLine($"アドレス:{cellData.Address}/値:{cellData.Value}");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"エラー: {ex.Message}");
        }
    }
}

ExcelDataReader

using ExcelDataReader;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleExcelDataReader
{
    static void Main(string[] args)
    {
        // デフォルトでは、エンコード(CP1252)がサポートされておらずエラーになるのでこれが必要
        System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            
            // ファイル開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (var reader = ExcelReaderFactory.CreateReader(fileStream))
            {
                // DataSetに変換(シート情報を取得)
                var result = reader.AsDataSet();
                
                // シート取得
                var worksheet = result.Tables["Sheet1"];
            
                for(var r = 0; r <= 1; r++)
                {
                    for(var c = 0; c <= 2; c++)
                    {
                        // セルの値
                        var value = worksheet.Rows[r][c];

                        // 列名
                        var colStr = ConvertColumnNumToStr(c + 1);
                        var address = $"{colStr}{r + 1}";

                        Console.WriteLine($"アドレス:{address}/値:{value}");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    // 列を表す数値から列を表す文字列に変換する
    static string ConvertColumnNumToStr(int colNum)
    {
        var colStr = string.Empty;

        while (colNum > 0)
        {
            int modulo = (colNum - 1) % 26;
            colStr = Convert.ToChar('A' + modulo) + colStr;
            colNum = (colNum - modulo) / 26;
        }

        return colStr;
    }
}

NPOI

xls

using NPOI.HSSF.UserModel;
using NPOI.SS.Formula.Functions;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xls");
            
            // ブックを開く、読み取りモード、共有なし(排他)
            using (var stRead = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
            using (var workbook = new HSSFWorkbook(stRead))
            {
                // シートを取得
                var worksheet = workbook.GetSheet("Sheet1");

                for(var r = 0; r <= 1; r++)
                {
                    for(var c = 0; c <= 2; c++)
                    {
                        // セルを取得
                        var cellData = worksheet.GetRow(r).GetCell(c);

                        // アドレスを取得
                        var address = new CellReference(r, c).FormatAsString();

                        // 値を取得
                        var value = cellData.StringCellValue;

                        Console.WriteLine($"アドレス:{address}/値:{value}");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

xlsx

using NPOI.SS.Util;
using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            
            // ブックを開く、読み取りモード、共有なし(排他)
            using (var stRead = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
            using (var workbook = new XSSFWorkbook(stRead))
            {
                // シートを取得
                var worksheet = workbook.GetSheet("Sheet1");

                for (var r = 0; r <= 1; r++)
                {
                    for (var c = 0; c <= 2; c++)
                    {
                        // セルを取得
                        var cellData = worksheet.GetRow(r).GetCell(c);

                        // アドレスを取得
                        var address = new CellReference(r, c).FormatAsString();

                        // 値を取得
                        var value = cellData.StringCellValue;

                        Console.WriteLine($"アドレス:{address}/値:{value}");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

xls/xlsx対応版

Program.cs

using NPOI.SS.Util;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            var extension = Path.GetExtension(filePath);

            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
            using (var workbook = WorkbookFactory.Create(fileStream, extension))
            {
                // シートを取得
                var worksheet = workbook.GetSheet("Sheet1");

                for (var r = 0; r <= 1; r++)
                {
                    for (var c = 0; c <= 2; c++)
                    {
                        // セルを取得
                        var cellData = worksheet.GetRow(r).GetCell(c);

                        // アドレスを取得
                        var address = new CellReference(r, c).FormatAsString();

                        // 値を取得
                        var value = cellData.StringCellValue;

                        Console.WriteLine($"アドレス:{address}/値:{value}");
                    }
                }
            }

        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode; // C#10

public class WorkbookFactory
{
    public static IWorkbook Create(Stream stream, string extension)
    {
        extension = extension.ToLower();

        return extension switch
        {
            ".xls" => new HSSFWorkbook(stream),
            ".xlsx" => new XSSFWorkbook(stream),
            _ => throw new NotSupportedException("対象外の拡張子です")
        };
    }

    public static IWorkbook Create(string extension)
    {
        extension = extension.ToLower();

        return extension switch
        {
            ".xls" => new HSSFWorkbook(),
            ".xlsx" => new XSSFWorkbook(),
            _ => throw new NotSupportedException("対象外の拡張子です")
        };
    }
}
投稿日時: 2025-05-19 14:33:19
更新日時: 2025-05-21 03:57:21

ExcelVBA

Sub Sample()
    Dim AddSheetObj     As Worksheet
    Dim BookObj         As Workbook
    Dim FilePath        As String
    Dim SheetCount      As Long
    Dim LastSheetObj    As Worksheet
    
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath)
    
    'シート数を取得
    SheetCount = BookObj.Sheets.Count
    
    '最後のシートを取得
    Set LastSheetObj = BookObj.Sheets(SheetCount)
    
    '渡し、その後ろに追加するよう指示する
    Set AddSheetObj = BookObj.Sheets.Add(After:=LastSheetObj)
    
    '追加されたシートの名前を変える
    AddSheetObj.Name = "sample"

    '保存
    BookObj.Save
        
    '閉じる
    BookObj.Close
    
    
    Set BookObj = Nothing
End Sub

Excel(COM)

using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? addWorksheet = null;
        Excel.Worksheet? lastWorksheet = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // シートを取得
            worksheets = workbook.Sheets;

            // 最後のシートを取得
            var sheetCount = worksheets.Count;
            lastWorksheet = worksheets[sheetCount];

            // シート追加
            addWorksheet = worksheets.Add(After: lastWorksheet);

            // シート名変更
            addWorksheet.Name = "sample";

            // 保存
            workbook.Save();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref addWorksheet);
            CleanUpComObject(ref lastWorksheet);
            CleanUpComObject(ref worksheets);
            CleanUpComObject(ref workbook, true, true);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

ClosedXML

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XLWorkbook(stream))
            {
                // 一番最後にsampleシート追加
                workbook.Worksheets.Add("sample");

                // 上書き保存
                workbook.Save();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var package = new ExcelPackage(readStream))
            {
                // シートを追加
                var worksheet = package.Workbook.Worksheets.Add("sample");

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    package.SaveAs(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"エラー: {ex.Message}");
        }
    }
}

ClosedXMLだと、直接Streamを使って上書きしているのに対し、
EPPlusだと、別のStreamに保存し元のStreamを空にしてからコピーをする手段をとっています
ライブラリ内部の設計の違いからくるもののそうです

ExcelDataReader

読み取りのみのためなし

NPOI

□xls

using NPOI.HSSF.UserModel;
using System;
using System.IO;
using System.Reflection;


namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xls");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = new HSSFWorkbook(readStream))
            {
                // シートを追加
                workbook.CreateSheet("sample");

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsx

using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = new XSSFWorkbook(readStream))
            {
                // シートを追加
                workbook.CreateSheet("sample");

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xls/xlsx対応版

Program.cs

using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            var extension = Path.GetExtension(filePath);

            // メモリにすべて取り込む。これなら遅延でStreamにアクセスするような問題が発生しない
            byte[] fileBytes = File.ReadAllBytes(filePath);
            using (var readStream = new MemoryStream(fileBytes))
            using (var workbook = WorkbookFactory.Create(readStream, extension))
            {
                // シートを追加
                workbook.CreateSheet("sample");

                // あらためて書き込み用としてStreamを開く
                using (var writeStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 書き込み
                    workbook.Write(writeStream);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode
{
    public class WorkbookFactory
    {
        public static IWorkbook Create(Stream stream, string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(stream),
                ".xlsx" => new XSSFWorkbook(stream),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }

        public static IWorkbook Create(string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(),
                ".xlsx" => new XSSFWorkbook(),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }
    }
}
投稿日時: 2025-05-18 04:38:18
更新日時: 2025-05-26 14:47:26

シートが3つ入っているExcelを開いて、シートの数を取得し出力します

ExcelVBA

Sub Sample()
    Dim BookObj     As Workbook
    Dim FilePath    As String
    Dim SheetObj    As Variant
    
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath)
    
    'シート数出力
    Debug.Print BookObj.Sheets.Count
        
    '保存せずに閉じる
    BookObj.Close (False)
    
    
    Set BookObj = Nothing
End Sub

Excel(COM)

using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // シートを取得
            worksheets = workbook.Sheets;

            // シート数出力
            Console.WriteLine(worksheets.Count);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref worksheets);
            CleanUpComObject(ref workbook);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

補足:Microsoft.Office.Interop.Excel でオブジェクトの解放について

ClosedXML

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XLWorkbook(stream))
            {
                // シート数出力
                Console.WriteLine(workbook.Worksheets.Count);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var package = new ExcelPackage(stream))
            {
                // シート数出力
                Console.WriteLine(package.Workbook.Worksheets.Count);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

ExcelDataReader

using ExcelDataReader;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleExcelDataReader
{
    static void Main(string[] args)
    {
        // デフォルトでは、エンコード(CP1252)がサポートされておらずエラーになるのでこれが必要
        System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ファイル開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (var reader = ExcelReaderFactory.CreateReader(fileStream))
            {
                // DataSetに変換(シート情報を取得)
                var result = reader.AsDataSet();

                // シート数出力
                Console.WriteLine(result.Tables.Count);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

NPOI

□xls

using NPOI.HSSF.UserModel;
using System;
using System.IO;
using System.Reflection;


namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xls");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            {
                using (var workbook = new HSSFWorkbook(fileStream))
                {
                    // シート数出力
                    Console.WriteLine(workbook.NumberOfSheets);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsx

using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            {
                using (var workbook = new XSSFWorkbook(fileStream))
                {
                    // シート数出力
                    Console.WriteLine(workbook.NumberOfSheets);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsもxlsxも扱う場合

Program.cs

using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            var extension = Path.GetExtension(filePath);

            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = WorkbookFactory.Create(fileStream, extension))
            {
                // シート数出力
                Console.WriteLine(workbook.NumberOfSheets);
            }

        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode
{
    public class WorkbookFactory
    {
        public static IWorkbook Create(Stream stream, string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(stream),
                ".xlsx" => new XSSFWorkbook(stream),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }

        public static IWorkbook Create(string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(),
                ".xlsx" => new XSSFWorkbook(),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }
    }
}
投稿日時: 2025-05-17 05:31:17
更新日時: 2025-05-26 13:43:26

シートが3つ入っているExcelを開いて、各ワークシートを取得し名前を出力します

ExcelVBA

Sub Sample()
    Dim BookObj     As Workbook
    Dim FilePath    As String
    Dim SheetObj    As Variant
    
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath)
    
    '全シート取得し、それぞれの名前を出力する
    For Each SheetObj In BookObj.Sheets
        Debug.Print SheetObj.Name
    Next
    
    '保存せずに閉じる
    BookObj.Close (False)
    
    Set BookObj = Nothing
End Sub

Excel(COM)

using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? worksheet = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // シートを取得
            worksheets = workbook.Sheets;

            // 各シートの名前を出力
            for (var i = 1; i <= worksheets.Count; i++)
            {
                // 各シートを取得して名前を表示
                worksheet = worksheets[i];
                Console.WriteLine(worksheet.Name);

                CleanUpComObject(ref worksheet);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref worksheet);
            CleanUpComObject(ref worksheets);
            CleanUpComObject(ref workbook);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

補足:Microsoft.Office.Interop.Excel でオブジェクトの解放について

ClosedXML

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XLWorkbook(stream))
            {
                // ワークシートをループで処理
                foreach (var worksheet in workbook.Worksheets)
                {
                    // シート名を出力
                    Console.WriteLine(worksheet.Name);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var package = new ExcelPackage(stream))
            {
                // ワークシートをループで処理
                foreach (var worksheet in package.Workbook.Worksheets)
                {
                    // シート名を出力
                    Console.WriteLine(worksheet.Name);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

ExcelDataReader

using ExcelDataReader;
using System;
using System.Data;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleExcelDataReader
{
    static void Main(string[] args)
    {
        // デフォルトでは、エンコード(CP1252)がサポートされておらずエラーになるのでこれが必要
        System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ファイル開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (var reader = ExcelReaderFactory.CreateReader(fileStream))
            {
                // DataSetに変換(シート情報を取得)
                var result = reader.AsDataSet();

                // DataTableCollectionからDataTable(シート情報)を一つずつ取り出す
                foreach (DataTable table in result.Tables)
                {
                    // シート名を出力
                    Console.WriteLine(table.TableName);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

NPOI

□xls

using NPOI.HSSF.UserModel;
using System;
using System.IO;
using System.Reflection;


namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xls");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            {
                using (var workbook = new HSSFWorkbook(fileStream))
                {
                    // foreachはない。シート数からシート番号としてシートを受け取りシート名を表示
                    for (var i = 0; i < workbook.NumberOfSheets; i++)
                    {
                        var worksheet = workbook.GetSheetAt(i);

                        Console.WriteLine(worksheet.SheetName);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsx

using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            {
                using (var workbook = new XSSFWorkbook(fileStream))
                {
                    // foreachはない。シート数からシート番号としてシートを受け取りシート名を表示
                    for (var i = 0; i < workbook.NumberOfSheets; i++)
                    {
                        var worksheet = workbook.GetSheetAt(i);

                        Console.WriteLine(worksheet.SheetName);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsもxlsxも扱う場合

Program.cs

using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // 拡張子取得
            var extension = Path.GetExtension(filePath);

            // ブックを開く
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = WorkbookFactory.Create(fileStream, extension))
            {
                // foreachはない。シート数からシート番号としてシートを受け取りシート名を表示
                for (var i = 0; i < workbook.NumberOfSheets; i++)
                {
                    var worksheet = workbook.GetSheetAt(i);

                    Console.WriteLine(worksheet.SheetName);
                }
            }

        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode
{
    public class WorkbookFactory
    {
        public static IWorkbook Create(Stream stream, string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(stream),
                ".xlsx" => new XSSFWorkbook(stream),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }

        public static IWorkbook Create(string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(),
                ".xlsx" => new XSSFWorkbook(),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }
    }
}
投稿日時: 2025-05-17 04:53:17
更新日時: 2025-05-26 14:21:26

シートが3つ入っているExcelを開いて、各ワークシートを取得し名前を出力します

ExcelVBA

Sub Sample()
    Dim BookObj     As Workbook
    Dim FilePath    As String
    Dim SheetObj    As Variant
    
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath)
    
    '全シート取得し、それぞれの名前を出力する
    For Each SheetObj In BookObj.Sheets
        Debug.Print SheetObj.Name
    Next
    
    '保存せずに閉じる
    BookObj.Close (False)
    
    Set BookObj = Nothing
End Sub

Excel(COM)

using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode
{
    internal class SampleExcel
    {
        static void Main(string[] args)
        {
            Excel.Application? application = null;
            Excel.Workbooks? workbooks = null;
            Excel.Workbook? workbook = null;
            Excel.Sheets? worksheets = null;
            Excel.Worksheet? worksheet = null;

            try
            {
                // 実行ファイルのあるフォルダパス取得
                var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
                if (folderPath == null) { return; }

                // Excelファイルパスを作成
                var filePath = Path.Combine(folderPath, "sample.xlsx");
                
                // Excelを開く
                application = new Excel.Application();
                application.Visible = true;

                // ブックを開く
                workbooks = application.Workbooks;
                workbook = workbooks.Open(filePath);

                // シートを取得
                worksheets = workbook.Sheets;

                // 各シートの名前を出力
                for (var i = 1; i <= worksheets.Count; i++)
                {
                    // 各シートを取得して名前を表示
                    worksheet = worksheets[i];
                    Console.WriteLine(worksheet.Name);

                    CleanUpComObject(ref worksheet);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                CleanUpComObject(ref worksheet);
                CleanUpComObject(ref worksheets);
                CleanUpComObject(ref workbook);
                CleanUpComObject(ref workbooks);
                CleanUpComObject(ref application);
            }
        }

        static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
        {
            // フラグによってWorkbookはClose / ApplicationはQuitする
            if (shouldClose && comObject != null)
            {
                // 型をチェックする
                if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
                {
                    // Workbookの場合
                    workbook.Close(saveChanges);
                }
                else if (comObject is Microsoft.Office.Interop.Excel.Application application)
                {
                    // Applicationの場合
                    application.Quit();
                }
            }

            // Objectを解放
            if (comObject != null && Marshal.IsComObject(comObject))
            {
                Marshal.ReleaseComObject(comObject);
                comObject = default!;
            }
        }
    }
}

補足:Microsoft.Office.Interop.Excel でオブジェクトの解放について

ClosedXML

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode
{
    internal class SampleClosedXML
    {
        static void Main(string[] args)
        {
            try
            {
                // 実行ファイルのあるフォルダパス取得
                var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
                if (folderPath == null) { return; }

                // Excelファイルパスを作成
                var filePath = Path.Combine(folderPath, "sample.xlsx");
                
                // ブックを開く、読み書きモード、共有なし(排他)
                using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
                using (var workbook = new XLWorkbook(stream))
                {
                    // ワークシートをループで処理
                    foreach (var worksheet in workbook.Worksheets)
                    {
                        // シート名を出力
                        Console.WriteLine(worksheet.Name);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

EPPlus

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode
{
    internal class SampleEPPlus
    {
        static void Main(string[] args)
        {
            // Ver8.0のソースです
            // 非商用個人利用の場合 名前を設定
            ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

            try
            {
                // 実行ファイルのあるフォルダパス取得
                var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
                if (folderPath == null) { return; }

                // Excelファイルパスを作成
                var filePath = Path.Combine(folderPath, "sample.xlsx");
                
                // ブックを開く、読み書きモード、共有なし(排他)
                using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
                using (var package = new ExcelPackage(stream))
                {
                    // ワークシートをループで処理
                    foreach(var worksheet in package.Workbook.Worksheets)
                    {
                        // シート名を出力
                        Console.WriteLine(worksheet.Name);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

ExcelDataReader

using ExcelDataReader;
using System;
using System.Data;
using System.IO;
using System.Reflection;

namespace SampleCode
{
    internal class SampleExcelDataReader
    {
        static void Main(string[] args)
        {
            // デフォルトでは、エンコード(CP1252)がサポートされておらずエラーになるのでこれが必要
            System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

            try
            {
                // 実行ファイルのあるフォルダパス取得
                var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
                if (folderPath == null) { return; }

                // Excelファイルパスを作成
                var filePath = Path.Combine(folderPath, "sample.xlsx");
                filePath = @"c:\temp\sample.xlsx"; //////////////////////////////////////////////ここ消す
                // ファイル開く、読み書きモード、共有なし(排他)
                using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
                using (var reader = ExcelReaderFactory.CreateReader(fileStream))
                {
                    // DataSetに変換(シート情報を取得)
                    var result = reader.AsDataSet();

                    // DataTableCollectionからDataTable(シート情報)を一つずつ取り出す
                    foreach (DataTable table in result.Tables)
                    {
                        // シート名を出力
                        Console.WriteLine(table.TableName);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

NPOI

xls

using NPOI.HSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode
{
    internal class SampleNPOI_xls
    {
        static void Main(string[] args)
        {
            try
            {
                // 実行ファイルのあるフォルダパス取得
                var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
                if (folderPath == null) { return; }

                // Excelファイルパスを作成
                var filePath = Path.Combine(folderPath, "sample.xls");
                
                // ブックを開く、読み書きモード、共有なし(排他)
                using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
                {
                    using (var workbook = new HSSFWorkbook(fileStream))
                    {
                        // foreachはない。シート数からシート番号としてシートを受け取りシート名を表示
                        for (var i = 0; i < workbook.NumberOfSheets; i++)
                        {
                            var worksheet = workbook.GetSheetAt(i);

                            Console.WriteLine(worksheet.SheetName);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

xlsx

using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode
{
    internal class SampleNPOI_xlsx
    {
        static void Main(string[] args)
        {
            try
            {
                // 実行ファイルのあるフォルダパス取得
                var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
                if (folderPath == null) { return; }

                // Excelファイルパスを作成
                var filePath = Path.Combine(folderPath, "sample.xlsx");
                
                // ブックを開く、読み書きモード、共有なし(排他)
                using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
                {
                    using (var workbook = new XSSFWorkbook(fileStream))
                    {
                        // foreachはない。シート数からシート番号としてシートを受け取りシート名を表示
                        for (var i = 0; i < workbook.NumberOfSheets; i++)
                        {
                            var worksheet = workbook.GetSheetAt(i);

                            Console.WriteLine(worksheet.SheetName);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

xlsもxlsxも扱う場合

Program.cs

using System;
using System.IO;
using System.Reflection;

namespace SampleCode
{
    internal class SampleNPOI
    {
        static void Main(string[] args)
        {
            try
            {
                // 実行ファイルのあるフォルダパス取得
                var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
                if (folderPath == null) { return; }

                // Excelファイルパスを作成
                var filePath = Path.Combine(folderPath, "sample.xlsx");
                filePath = @"c:\temp\sample.xlsx";
                
                var extension = Path.GetExtension(filePath);

                using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
                using (var workbook = WorkbookFactory.Create(fileStream, extension))
                {
                    for(var i = 0; i < workbook.NumberOfSheets; i++)
                    {
                        var worksheet = workbook.GetSheetAt(i);

                        Console.WriteLine(worksheet.SheetName);
                    }
                }

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode
{
    public class WorkbookFactory
    {
        public static IWorkbook Create(Stream stream, string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(stream),
                ".xlsx" => new XSSFWorkbook(stream),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }

        public static IWorkbook Create(string extension)
        {
            extension = extension.ToLower();

            return extension switch
            {
                ".xls" => new HSSFWorkbook(),
                ".xlsx" => new XSSFWorkbook(),
                _ => throw new NotSupportedException("対象外の拡張子です")
            };
        }
    }
}
投稿日時: 2025-05-17 04:52:17

もう一歩、シンプルな実装をするため解放ヘルパーなんてどう?と生成AIにすすめられたので触れてみる
Idisposableのクラスに、object を格納するリストを作って、disposeで逆順に開放し、
GC.Collect();GC.WaitForPendingFinalizers();も最後にしっかり2度呼ぶという話

ついでに、workbookは、登録時に閉じるか否か、閉じる場合は保存するか否かと
applicationは、登録時に閉じるか否かを指定できるようにすると・・・

こんな感じのクラスになります

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleExcel
{
    internal class ComReleaseManager : IDisposable
    {
        /// <summary>
        /// COMオブジェクトデータ
        /// </summary>
        /// <param name="ComObject">COMオブジェクト</param>
        /// <param name="ShouldClose">WorkbookでCloseするか否か, ApplicationでQuitするか否か</param>
        /// <param name="SaveChanges">WorkbookでCloseする場合、保存するかしないか</param>
        private record ComObjectEntry(object ComObject, bool ShouldClose, bool SaveChanges);

        // new () は左辺の型で new するのを省略する書き方(C# 9.0)
        private readonly List<ComObjectEntry> _comObjects = new ();

        // where T : class で値型ではなく、参照型のみに限定している
        private T RegisterInternal<T>(T comObject, bool shouldClose, bool saveChanges) where T : class
        {
            // null ならエラー
            if (comObject == null)
            {
                throw new ArgumentNullException();
            }

            // COMオブジェクトデータとして登録
            _comObjects.Add(new ComObjectEntry(comObject, shouldClose, saveChanges));

            // COMオブジェクトを返却
            return comObject;
        }

        // 一般的なCOMオブジェクト
        public T Register<T>(T comObject) where T : class
        {
            return RegisterInternal(comObject, false, false);
        }

        // Application登録(shouldClose指定あり)
        public Excel.Application Register(Excel.Application app, bool shouldClose)
        {
            return RegisterInternal(app, shouldClose, false);
        }

        // Workbook登録(shouldCloseとsaveChangesを指定)
        public Excel.Workbook Register(Excel.Workbook workbook, bool shouldClose, bool saveChanges)
        {
            return RegisterInternal(workbook, shouldClose, saveChanges);
        }

        // 後処理
        public void Dispose()
        {
            for (int i = _comObjects.Count - 1; i >= 0; i--)
            {
                var entry = _comObjects[i];
                try
                {
                    if (entry.ShouldClose)
                    {
                        switch (entry.ComObject)
                        {
                            case Excel.Workbook workbook:
                                workbook.Close(entry.SaveChanges);
                                break;

                            case Excel.Application app:
                                app.Quit();
                                break;
                        }
                    }

                    Marshal.ReleaseComObject(entry.ComObject);
                }
                catch
                {
                    // ログなどに残してもよい(今回は無視)
                }
            }

            _comObjects.Clear();

            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }
}

もう少し拡張するなら、debugビルドかreleaseビルドかによって
Marshal.ReleaseComObject関数が返す参照カウントの値をログにだせるようにすると解放漏れがあった場合にはすぐにわかってよいかも

補足:recordについて recordは、簡単に説明するとC#9.0に追加された機能 classでデータのみ保持する場合のシンタックスシュガーみたいなものになります

以下コードは

public record Person(string Name, int Age);

C# 9.0より前では以下と一緒

public class Person
{
    public string Name { get; }
    public int Age { get; }

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public override bool Equals(object obj) =>
        obj is Person other && Name == other.Name && Age == other.Age;

    public override int GetHashCode() => HashCode.Combine(Name, Age);

    public override string ToString() => $"Person {{ Name = {Name}, Age = {Age} }}";
}

以下ソースコードを解放ヘルパーを使って書き直してみます

Before:

using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode
{
    internal class SampleExcel
    {
        static void Main(string[] args)
        {
            Excel.Application? application = null;
            Excel.Workbooks? workbooks = null;
            Excel.Workbook? workbook = null;
            Excel.Sheets? worksheets = null;
            Excel.Worksheet? worksheet = null;

            try
            {
                // 実行ファイルのあるフォルダパス取得
                var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
                if (folderPath == null) { return; }

                // Excelファイルパスを作成
                var filePath = Path.Combine(folderPath, "sample.xlsx");

             
                
                // Excelを開く
                application = new Excel.Application();
                application.Visible = true;

                // ブックを開く
                workbooks = application.Workbooks;
                workbook = workbooks.Open(filePath);

                // シートを取得
                worksheets = workbook.Sheets;

                for (var i = 1; i <= worksheets.Count; i++)
                {
                    // 各シートを取得して名前を表示
                    worksheet = worksheets[i];
                    Console.WriteLine(worksheet.Name);

                    CleanUpComObject(ref worksheet);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                CleanUpComObject(ref worksheet);
                CleanUpComObject(ref worksheets);
                CleanUpComObject(ref workbook);
                CleanUpComObject(ref workbooks);
                CleanUpComObject(ref application);

                // RCW強制解放(残留プロセス対策)
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
        }

        static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
        {
            // フラグによってWorkbookはClose / ApplicationはQuitする
            if (shouldClose && comObject != null)
            {
                // 型をチェックする
                if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
                {
                    // Workbookの場合
                    workbook.Close(saveChanges);
                }
                else if (comObject is Microsoft.Office.Interop.Excel.Application application)
                {
                    // Applicationの場合
                    application.Quit();
                }
            }

            // Objectを解放
            if (comObject != null && Marshal.IsComObject(comObject))
            {
                Marshal.ReleaseComObject(comObject);
                comObject = default!;
            }
        }
    }
}

After:

using SampleExcel;
using System;
using System.IO;
using System.Reflection;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode
{
    internal class SampleExcel
    {
        static void Main(string[] args)
        {
            try
            {
                // 実行ファイルのあるフォルダパス取得
                var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
                if (folderPath == null) { return; }

                // Excelファイルパスを作成
                var filePath = Path.Combine(folderPath, "sample.xlsx");

                using (var comManager = new ComReleaseManager())
                {
                    // Excelを開く
                    var application = comManager.Register(new Excel.Application(), true);

                    // ブックを開く
                    var workbooks = comManager.Register(application.Workbooks);
                    var workbook = comManager.Register(workbooks.Open(filePath), true, false);
                    
                    // シートを取得
                    var worksheets = comManager.Register(workbook.Sheets);
   
                    for(var i = 1; i < worksheets.Count; i++)
                    {
                        var worksheet = comManager.Register(worksheets[i]);

                        Console.WriteLine(worksheet.Name);
                    }
                
                }
                // using をぬけると生成したComオブジェクトを逆順に解放し、必要に応じて、Close Quitし、2回GCを実行する
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

現実的で読みやすいレベルのソースコードになりました!

ただ、解放クラスは使いまわしをしない方針のため、セルを数万行チェックするといったようなケースの場合
大量にリストにオブジェクトを抱えた状態になるので、あまりおすすめできないとおもいます

それらは、一時的変数で管理してすぐに廃棄していくのがベストだと思うので、うまく組み合わせて使っていくことが重要だと思います

投稿日時: 2025-05-14 13:18:14
更新日時: 2025-05-14 13:51:14

解放をしない場合の挙動

適切な解放がされないと、プロセスが消えずに残ることになります。

実際に確認をしてみます
以下のように、applicationクラスのインスタンスを作成し、すぐ閉じます。

using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        application = new Excel.Application();
        application.Quit();
    }
}

これでおきることは、new Excel.Application()を実行すると

バックグラウンドにExcelのプロセス現れます

 

プログラムではQuitして終了したようにみえても、プロセスには残り続けています

もし、定時実行をし続けようならば、実行するたびにプロセスがのこっていきます・・・

残ったプロセスは手動で消してください・・・

アプリケーションを表示した際のプロセスの挙動

application.VisibleをtrueにするとExcelが表示されます

using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        application = new Excel.Application();
        application.Visible = true;
    }
}

この時の動きは、 new Excel.Application()で バックグラウンドとしてプロセスが作成され

その後、application.Visible = true でExcelが表示されアプリのプロセスに移動

↑バックグラウンドのプロセス一覧 ↑アプリのプロセス一覧

プログラムはここで終了となり、開かれているExcelを閉じると、再びバックグラウンドの方に移動します

パット見た感じアプリの一覧から消えるので、解放していなくても消えたようにみえるかもしれませんが、バックグラウンド側に残っています

一度でも参照したら解放する

たとえば、exeのあるフォルダに、3シートある sample.xlsxがあったとします
開いて、各シート名を出力してみます

using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? worksheet = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // シートを取得
            worksheets = workbook.Sheets;

            for (var i = 1; i <= worksheets.Count; i++)
            {
                // 各シートを取得して名前を表示
                worksheet = worksheets[i];
                Console.WriteLine(worksheet.Name);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            if (worksheet != null) { Marshal.ReleaseComObject(worksheet); }
            if (worksheets != null) { Marshal.ReleaseComObject(worksheets); }
            if (workbook != null)
            {
                workbook.Close();
                Marshal.ReleaseComObject(workbook);
            }
            if (workbooks != null) { Marshal.ReleaseComObject(workbooks); }
            if (application != null)
            {
                application.Quit();
                Marshal.ReleaseComObject(application);
            }
        }
    }
}

これは、バックグラウンドにプロセスが残ります
一番最後のSheet3については、finallyで解放していますが、
Sheet1とSheet2は、参照しているのに解放していないので、プロセスが残ります

参照したら都度解放する必要があります

using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? worksheet = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // シートを取得
            worksheets = workbook.Sheets;

            for (var i = 1; i <= worksheets.Count; i++)
            {
                // 各シートを取得して名前を表示
                worksheet = worksheets[i];
                Console.WriteLine(worksheet.Name);

                // 参照したら都度解放する
                Marshal.ReleaseComObject(worksheet);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            if (worksheet != null) { Marshal.ReleaseComObject(worksheet); }
            if (worksheets != null) { Marshal.ReleaseComObject(worksheets); }
            if (workbook != null)
            {
                workbook.Close();
                Marshal.ReleaseComObject(workbook);
            }
            if (workbooks != null) { Marshal.ReleaseComObject(workbooks); }
            if (application != null)
            {
                application.Quit();
                Marshal.ReleaseComObject(application);
            }
        }
    }
}

不要にReleaseComObjectを実行した場合

ループしてWorkSheetのオブジェクトを参照した場合、都度解放をしているのでループを抜けた場合、
参照カウントは0になっています。ただ、ReleaseComObjectを実行してもnullにはならないので、
finallyにきたら、さらにReleaseComObjectを実行することになります。

一般的には、不要にReleaseComObjectを実行するものではないといわれていますが、
それによって意図しない状態になったのを見たことがないのでなんともいえないです。

そのため、途中で開放した場合は、nullにしてしまうのがより良いと考えられているそうです

using System.IO;
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? worksheet = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // シートを取得
            worksheets = workbook.Sheets;

            for (var i = 1; i <= worksheets.Count; i++)
            {
                // 各シートを取得して名前を表示
                worksheet = worksheets[i];
                Console.WriteLine(worksheet.Name);

                // 参照したら都度解放する
                Marshal.ReleaseComObject(worksheet);
                worksheet = null;
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            if (worksheet != null) { Marshal.ReleaseComObject(worksheet); }
            if (worksheets != null) { Marshal.ReleaseComObject(worksheets); }
            if (workbook != null)
            {
                workbook.Close();
                Marshal.ReleaseComObject(workbook);
            }
            if (workbooks != null) { Marshal.ReleaseComObject(workbooks); }
            if (application != null)
            {
                application.Quit();
                Marshal.ReleaseComObject(application);
            }
        }
    }
}

とはいえこれはよいことではあるけども、少しめんどくさい
そこで、解放処理や、Close、Quitをする処理をまとめると・・・

using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? worksheet = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // シートを取得
            worksheets = workbook.Sheets;

            for (var i = 1; i <= worksheets.Count; i++)
            {
                // 各シートを取得して名前を表示
                worksheet = worksheets[i];
                Console.WriteLine(worksheet.Name);

                CleanUpComObject(ref worksheet);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref worksheet);
            CleanUpComObject(ref worksheets);
            CleanUpComObject(ref workbook);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

もう少し、掘り下げておくとWorksheetのオブジェクトについては
for文のループ中でしかつかっていないので、その中で変数を作り格納しすぐ開放すればよいように思える

for (var i = 1; i <= worksheets.Count; i++)
{
    // 各シートを取得して名前を表示
    var worksheet = worksheets[i];
    Console.WriteLine(worksheet.Name);

    // 何かしらの処理

    CleanUpComObject(ref worksheet);
}

ただ、参照したあとに何かしらの処理で例外が発生したら、参照カウントが下げることができないので
オブジェクトを参照した直後から、解放するまではtry catchで囲わないといけないので、
あちことで変数を作って参照していたらtry catchだらけになることに注意

for (var i = 1; i <= worksheets.Count; i++)
{
    // 各シートを取得して名前を表示
    var worksheet = worksheets[i];
    try
    {
        Console.WriteLine(worksheet.Name);

        // 何かしらの処理

        CleanUpComObject(ref worksheet);
    }
    catch
    {
        throw;
    }
    finally
    {
        CleanUpComObject(ref worksheets);
    }
}

いろいろ調べてみるかぎり、より完全に解放するためには、GCを2度読んであげるとよいとされているそうです

using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref workbook);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);

            // RCW強制解放(残留プロセス対策)
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

2度呼ぶ理由についての生成AIの説明

✅ 結論から:なぜ2回 GC.Collect(); GC.WaitForPendingFinalizers(); が必要なのか?

これは、COMオブジェクトの参照がすべて解放されたことを確実にするために行われます。

📌 背景知識:COMオブジェクトとRCW(Runtime Callable Wrapper)

・.NET から COM オブジェクト(例:Excel)を使うとき、RCW という「仲介役」が自動的に生成されます。

・RCW は、.NET 側のガベージコレクションによって管理されますが、RCW自体がCOMオブジェクトの参照カウントを持っているため、RCWが解放されるまでCOMオブジェクトは解放されません。

🔄 なぜ2回GCするのか?

1回目の GC.Collect(); GC.WaitForPendingFinalizers(); で何が起きるか

・.NETでGC対象になったRCWオブジェクト(≒Excel COMオブジェクトのラッパー)を収集対象にする

・それにより、RCWのファイナライザ(最終処理)が実行される

・そのファイナライザの中で、COMオブジェクトへの参照が ReleaseComObject() などで解放される

🔁 しかし、ファイナライザが実行された直後にGCが実行されるとは限らないため、もう一度 GC をかけることで:

・ファイナライザによって解放されたRCW自体も収集され

・結果として RCWが保持していたCOMオブジェクトの参照カウントが0になり、COMオブジェクトも確実に解放される

⚠ よくある誤解ポイント

・RCWの解放自体は、明示的に Marshal.ReleaseComObject() を使うことでもできます。これを使えば、2回の GC は不要なこともあります。

・ただし、RCWを複数の変数で共有していたり、Excelオブジェクトの階層(Workbooks→Workbook→Worksheetなど)で参照が残っていたりすると、ReleaseComObjectだけでは不十分で、最終的にGCで回収させる必要が出るため、2回GCが有効になる。

とのこと

おまけ

生成AIさんこのコードで問題ない?

worksheetsをループ処理でシート名を表示するプログラム
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;
        Excel.Sheets? worksheets = null;
        Excel.Worksheet? worksheet = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            // ブックを開く
            workbooks = application.Workbooks;
            workbook = workbooks.Open(filePath);

            // シートを取得
            worksheets = workbook.Sheets;

            for (var i = 1; i <= worksheets.Count; i++)
            {
                // 各シートを取得して名前を表示
                worksheet = worksheets[i];
                Console.WriteLine(worksheet.Name);

                CleanUpComObject(ref worksheet);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref worksheet);
            CleanUpComObject(ref worksheets);
            CleanUpComObject(ref workbook);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);

            // RCW強制解放(残留プロセス対策)
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

worksheetは、for文でしか使っていないので、一時変数に格納して不要になればすぐ解放することも考えられるけど、
オブジェクト参照から不要になり解放するまでに、処理があって例外が発生する可能性があると都度try~catchを
書く必要があるから、コードが読みにくくなるのであえて書いていないけどどう?

ご提示の設計・実装方針は現実的かつ推奨されるパターンです。

COMオブジェクト(特にExcel)は、取得したすべてのオブジェクトを明示的に解放することが重要であり、

finallyブロックでまとめて後始末をするやり方は、例外発生時も含めてリソースリークを防ぐ観点からも適切です

ポイント整理

・for文内でworksheetを都度解放するのは理想的ですが、例外処理や可読性の観点から冗長になりやすいです。

・あなたのように、finallyでworksheetも含めてまとめて解放する方法は、例外が発生しても確実にリソースを解放できるため、実運用でよく使われています

・ただし、for文内でCleanUpComObject(ref worksheet)を呼んでいるため、ループのたびに前の参照を解放し、finallyでも念のため解放する形になっています。この重複は問題ありませんが、二重解放を防ぐため、CleanUpComObject内でnullチェックをしているのは良い実装です。

投稿日時: 2025-05-13 03:43:13
更新日時: 2025-05-24 00:20:24

ExcelVBA

SaveAsでファイルパスを指定する

Sub Sample()
    Dim BookObj     As Workbook
    Dim FilePath    As String
    
    '新規ブック作成
    Set BookObj = Workbooks.Add
    
    '保存するファイルパス
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    '名前つけて保存
    BookObj.SaveAs Filename:=FilePath
    
    Set BookObj = Nothing
End Sub

ただし、ファイルが既にある場合は「この場所に 'XXXX'という名前のファイルが既にあります。置き換えますか?」
と表示されるため、ファイルがある場合も上書きするのであれば、以下のようにします

Sub Sample()
    Dim BookObj     As Workbook
    Dim FilePath    As String
    
    '新規ブック作成
    Set BookObj = Workbooks.Add
    
    '保存するファイルパス
    
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xls"
    
    '同一ファイルがあった場合の警告表示を無効とする
    Application.DisplayAlerts = False
    
    '名前つけて保存
    BookObj.SaveAs Filename:=FilePath
    
    '警告表示を有効に戻す
    Application.DisplayAlerts = True
    
    Set BookObj = Nothing
End Sub

もし、ファイルが既にある場合に上書きしたくない場合は事前にファイル存在チェックをして処理を止めればよいでしょう

Excel(COM)

using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
using System;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;

            workbooks = application.Workbooks;
            workbook = workbooks.Add();

            // 警告表示を無効にする
            application.DisplayAlerts = false;

            // 名前つけて保存
            workbook.SaveAs2(filePath);

            // 警告表示を有効に戻す
            application.DisplayAlerts = false;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref workbook, true, false);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);

            // RCW強制解放(残留プロセス対策)
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

ClosedXML

ファイルパスと、Streamを使った方法

□ファイルパスで名前を付けて保存

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            using (var workbook = new XLWorkbook())
            {
                // シートが一つもないブックは保存でエラーになるので追加
                var sheet = workbook.AddWorksheet("Sheet1");

                // 名前をつけて保存
                workbook.SaveAs(filePath);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□FileStreamで名前を付けて保存

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        var filePath = string.Empty;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            filePath = Path.Combine(folderPath, "sample.xlsx");

            // ファイル出力用のストリームを作成(まだ中身は空のものが作られる)
            using (var stream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
            // メモリ上にワークブックを作成(この時点ではストリームとは未接続)
            using (var workbook = new XLWorkbook())
            {
                // シートが一つもないブックは保存でエラーになるので追加
                var sheet = workbook.AddWorksheet("Sheet1");

                // 作成したワークブックをストリームに保存(初めてファイルと紐づく)
                workbook.SaveAs(stream);
            }
        }
        catch (Exception ex)
        {
            // エラーが発生し、保存ができていなければ、空ファイルを作っただけなので、紛らわしいので消しておく
            if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
            {
                File.Delete(filePath);
            }

            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

□ファイルパスで名前を付けて保存

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            using (var package = new ExcelPackage())
            {
                // シートが一つもないブックは保存でエラーになるので追加
                var worksheet = package.Workbook.Worksheets.Add("sample");

                // 保存
                package.SaveAs(filePath);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

FileStreamを使った場合は、ClosedXMLと同じなのでそちらを参照

ExcelDataReader

読み取りのみのライブラリのため保存はありません

NPOI

NPOIは、ファイルパスをうけとれないので、FileStreamで処理する

□xls

using NPOI.HSSF.UserModel;
using System.Reflection;

var filePath = "";
try
{
    // 実行ファイルのあるフォルダパス取得
    var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
    if (folderPath == null) { return; }

    // Excelファイルパスを作成
    filePath = Path.Combine(folderPath, "sample.xls");
    
    using (var stream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
    using (var workbook = new HSSFWorkbook())
    {
        // シートが一つもないブックは保存でエラーになるので追加
        workbook.CreateSheet("Sheet1");

        // 作成したワークブックをストリームに保存
        workbook.Write(stream);
    }
}
catch (Exception ex)
{
    // エラーが発生し、保存ができていなければ、空ファイルを作っただけなので、紛らわしいので消しておく
    if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
    {
        File.Delete(filePath);
    }

    Console.WriteLine(ex.Message);
}

□xlsx

using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        var filePath = "";

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            filePath = Path.Combine(folderPath, "sample.xlsx");

            using (var stream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XSSFWorkbook())
            {
                // シートなしでも保存はできてしまう
                // sample.xlsxの一部の内容に問題が見つかりました。と出るのでシートは追加すること
                workbook.CreateSheet("Sheet1");

                // 作成したワークブックをストリームに保存
                workbook.Write(stream);
            }
        }
        catch (Exception ex)
        {
            // エラーが発生し、保存ができていなければ、空ファイルを作っただけなので、紛らわしいので消しておく
            if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
            {
                File.Delete(filePath);
            }

            Console.WriteLine(ex.Message);
        }
    }
}

□xls/xlsx対応版

Program.cs

using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        var filePath = "";

        try
        {   // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            filePath = Path.Combine(folderPath, "sample.xlsx");

            var extension = Path.GetExtension(filePath);

            // ブックを開く、読み取りモード、共有は読み取りのみOK
            using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = WorkbookFactory.Create(fileStream, extension))
            {
                // シートなしでも保存はできてしまう
                // sample.xlsxの一部の内容に問題が見つかりました。と出るのでシートは追加すること
                workbook.CreateSheet("Sheet1");

                // 作成したワークブックをストリームに保存
                workbook.Write(fileStream);
            }
        }
        catch (Exception ex)
        {
            // エラーが発生し、保存ができていなければ、空ファイルを作っただけなので、紛らわしいので消しておく
            if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
            {
                File.Delete(filePath);
            }

            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode; // C#10

public class WorkbookFactory
{
    public static IWorkbook Create(Stream stream, string extension)
    {
        extension = extension.ToLower();

        return extension switch
        {
            ".xls" => new HSSFWorkbook(stream),
            ".xlsx" => new XSSFWorkbook(stream),
            _ => throw new NotSupportedException("対象外の拡張子です")
        };
    }

    public static IWorkbook Create(string extension)
    {
        extension = extension.ToLower();

        return extension switch
        {
            ".xls" => new HSSFWorkbook(),
            ".xlsx" => new XSSFWorkbook(),
            _ => throw new NotSupportedException("対象外の拡張子です")
        };
    }
}
投稿日時: 2025-05-11 10:19:11
更新日時: 2025-05-24 11:45:24

ExcelVBA

Open関数のPasswordに設定

Sub Sample()
    Dim BookObj     As Workbook
    Dim FilePath    As String
    Dim ReadPass    As String: ReadPass = "test"
    
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'パスワード付きExcelを開く
    Set BookObj = Workbooks.Open(FilePath, Password:=ReadPass)
    
    Set BookObj = Nothing
End Sub

Excel(COM)

using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
using System;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;
            workbooks = application.Workbooks;

            // 書き込みパスワード
            var pass = "test";

            // 書き込みパスワードでブックを開く
            workbook = workbooks.Open(Filename: filePath, Password: pass);  // 名前付き引数がおすすめ
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref workbook, true, false);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);

            // RCW強制解放(残留プロセス対策)
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

ClosedXML

未対応

EPPlus

未対応

ExcelDataReader

未対応

NPOI

未対応

参考:
ClosedXML, ExcelDataReader, EPPlus, NPOIがパスワード解除について

投稿日時: 2025-05-11 07:00:11
更新日時: 2025-05-24 09:47:24

以下リンク先に、マイクロソフトの説明があります。
Office のサーバーサイド オートメーションについて

開発者は Microsoft Office のオートメーションを使用して、Office 製品に組み込まれている能力や機能を使用するカスタムソリューションを構築できます。 このようなプログラムによる開発は、比較的簡単にクライアント システムに実装できますが、Microsoft Active Server Pages (ASP)、ASP.NET、DCOM、Windows NT サービスなどのサーバー側のコードから自動化が行われると、多くの問題が発生する可能性があります。

まずは問題が起きるか否かの話
クライアントだと問題はないが、サーバーでの実行は問題が発生する可能性ありと言っています

Microsoft Office のすべての現行バージョンは、クライアント ワークステーション上のエンドユーザー製品として実行されるように設計、テスト、および構成されました。 また、対話型デスクトップとユーザー プロファイルが想定されています。

対話型デスクトップとユーザープロファイルといっているので、ユーザーがログインしGUI上でユーザーが操作することを想定し、ダイアログ等が出た場合には対処できることが前提とされている。ユーザープロファイルを想定しているということは、サーバー上のようにログイン無しのバックグラウンド実行は想定していないことを暗に意味しています。そのことは次の行にはっきりと記されています

Microsoft では、無人の非対話型クライアント アプリケーションまたはコンポーネント (ASP、ASP.NET、DCOM、NT Services を含む) からの Microsoft Office アプリケーションの自動化は現在推奨されておらず、サポートされていません。この環境で Office を実行すると、Office が不安定な動作やデッドロックを発生する可能性があるためです。

推奨されていないとまで言っていますね

続いて、5つの問題について話が続きます。

ユーザー ID: オートメーションによって Office アプリケーションが起動する場合でも、アプリケーション実行時にユーザー ID が想定されています。 アプリケーションを起動したユーザーのユーザー レジストリ ハイブの設定に基づいて、ツールバー、メニュー、オプション、プリンター、一部のアドインの初期化が試行されます。 多くのサービスは、ユーザー プロファイルがないアカウント (SYSTEM アカウント、IWAM_[サービス名] アカウントなど) で実行されています。 そのため、Office が起動時に正しく初期化されないことがあります。 この場合、CreateObject 関数または CoCreateInstance 関数のエラーが Office から返されます。 Office アプリケーションを起動できても、ユーザー プロファイルが存在しない場合、他の関数が正常に動作しない可能性があります。

この件についてGPTに確認してみました

自分:この問題については、一度でも実行するアカウントでログインしてOfficeを扱ったのであれば、初期化はされているの認識であっていますか?またユーザープロファイルに依存するような関数を使っていなければ、この点については問題ないといえるか?

GPT:はい、あなたの理解はほぼ正しいです。以下、Microsoftの説明を技術的に整理しつつ、あなたの質問に回答します。
✅ 回答1:一度でもログインしてOfficeを扱ったアカウントなら、初期化は済んでいるか?
→ 原則として「はい」です。
・ Officeは、初回起動時にそのユーザーの**ユーザープロファイル(レジストリ、テンプレート、アドインの情報など)**を構成します。

・ そのため、一度でも対象アカウントでWindowsにインタラクティブにログインしてOfficeを起動していれば、そのユーザーのプロファイルは存在し、初期化も完了しています。

・ こうしたプロファイルがあることで、CreateObject や CoCreateInstance を使ってOfficeを起動する際にエラーが出にくくなります。

✅ 回答2:ユーザープロファイルに依存しない関数だけを使うなら問題ない?
→ 「ある程度までは」その通りですが、油断は禁物です。

Officeアプリケーション(特にExcelやWord)は、内部的に次のような処理でユーザープロファイルに依存する可能性があります:

・ 初期プリンタの取得

・ テンプレートファイル(Normal.dotmなど)の読み込み

・ アドインやレジストリ設定(セキュリティ設定など)の確認

・ UI要素(ツールバー、メニュー)の構成

もしあなたのバッチ処理が以下のような限定的な処理であれば、問題が起きる可能性はかなり低くなります:

・ Excelファイルの読み書き

・ セルの値の取得・設定

・ ファイルの保存や印刷指示(プリンタ依存に注意)

・ 複雑なマクロやアドイン、テンプレートを使用しない

プログラムを実行するアカウントでログインし、Office操作して初期化をすませたうえで、
ファイルの中身の読み書きぐらいだと可能性はかなり低いという言い方になっています。

次の問題が一番大きい気がします

デスクトップとの対話機能: Office アプリケーションは、対話型デスクトップで実行されることを想定しています。 状況によっては、特定のオートメーション機能が正常に動作するために、アプリケーションを可視化する必要があります。 予期しないエラーが発生した場合、または関数を完了するために不明なパラメーターが必要な場合、Office はモーダル ダイアログ ボックスを表示し、実行する動作をユーザーに確認するように設計されています。 対話型ではないデスクトップのモーダル ダイアログ ボックスを閉じることはできません。 そのため、そのスレッドは永久に応答しなくなります (ハングします)。 コーディング方法によってこの問題が発生する可能性を軽減できる場合もありますが、このような方法で問題全体を回避することはできません。 そのため、サーバーサイド環境から Office アプリケーションを実行することは危険であり、サポートされません。

ログインして人が操作することを想定しているので、場合によってはダイアログが表示されたり、ハングしてとまっているということを人が判断しとめたりそういったことを前提しているから、よくないという話ですね
これはさすがに自動化している以上、一定の時間がたつとプログラム側でエラーをはいて終了とかそういった対応を想定しておかないといけないとかそういう話になると思いますが、そもそもサポートしていないと念を押しています・・・。

次の問題

再入機能とスケーラビリティ: サーバーサイド コンポーネントは、複数のクライアントに対応するために最小限のオーバーヘッドで高いスループットを持つ、再入性が高いマルチスレッドの COM コンポーネントである必要があります。 Office アプリケーションは、ほぼすべての点でその反対のアプリケーションです。 Office アプリケーションは、再入機能がない STA ベースのオートメーション サーバーであり、1 つのクライアントに対して多様でリソースを大量に消費する機能を提供するように設計されています。 また、サーバーサイド ソリューションとしてのスケーラビリティをほとんど提供していません。 さらに、メモリなどの重要な要素に対して固定の制限があります。 これらの制限は構成で変更できません。 さらに重要な点は、Office アプリケーションが、メモリ マップ ファイル、グローバル アドインまたはテンプレート、共有オートメーション サーバーなどのグローバル リソースを使用していることです。 そのため、複数クライアント環境で Office アプリケーションが構成されると、同時に実行できるインスタンス数が制限され、競合条件が発生する可能性があります。 同時に複数インスタンスの Office アプリケーションを実行する予定の場合、開発者は、"プーリング" または Office アプリケーションへのアクセスをシリアル化して、デッドロックやデータ破損の可能性を回避することを検討する必要があります。

定時実行のバッチ処理等でマルチスレッドによる実行がなければこの話は問題ないことになります

次の問題

回復性と安定性: Office 2000、Office XP、Office 2003、Office 2007 では、Microsoft Windows インストーラー (MSI) テクノロジを使用して、エンド ユーザーのインストールと自己修復を容易にします。 MSI では、"初回使用時にインストールする" という概念が導入されています。 これにより、システムの実行時に機能を動的にインストールまたは構成したり、特定のユーザーに対してより頻繁に機能をインストールしたり構成したりできます。 サーバー側環境では、パフォーマンスが低下し、ユーザーにインストールの承認またはインストール ディスクの提供を求めるダイアログ ボックスが表示される可能性が高くなります。 これは、エンド ユーザー製品としての Office の回復性を高めるために設計されていますが、Office の MSI 機能の実装は、サーバー側環境では逆効果です。 さらに、この種類の使用のために設計またはテストされていないため、Office がサーバー側で実行されている場合、一般的な Office の安定性は保証されません。 ネットワーク サーバーで Office をサービス コンポーネントとして使用すると、そのコンピューターの安定性が低下し、ネットワーク全体の安定性が低下する可能性があります。

GPTとの会話をのせておきます

自分:動的にインストールする話がかかれてますが、そういったことをしないのであれば、この話は関係ないということでしょうか?

GPT:はい、その認識は基本的に正しいです。

この部分に書かれている「回復性と安定性」に関する注意は、特に以下のような**“動的な機能インストール”や“自己修復”が発動するような場面”**に対しての警告です:

該当する「危険なシナリオ」 Officeには以下のようなMSIベースの仕組みがあります:

初回使用時にインストールされる機能(Install on first use)

特定の機能(たとえば数式エディター、図表描画ツールなど)は、Officeの最初のインストール時にはインストールされず、使おうとしたときに動的にインストールされる設定になっていることがあります。

自己修復機能(self-repair)

Officeの構成に何か欠損があると、起動時に自動的に再構成や修復が走ることがあります。

ユーザー単位で異なる構成が適用されることがある

特にマルチユーザー環境で、ユーザーごとにOfficeの状態が異なる場合があります。

🚨 サーバー側でこれが発動すると何が起きるか? インストールメディアの要求(=ダイアログボックスが表示されてハング)

インタラクティブな操作ができず失敗

サーバーの安定性が下がる(特にIISやバッチジョブ実行中にOfficeが待ち状態になる)

✅ バッチ処理のような用途で問題が出るか? → 原則として「いいえ」、問題にならないケースが多いです。 なぜなら:

バッチ処理で使用するOffice機能を事前にすべてフルインストールしておけば、「初回使用時インストール」や「自己修復」は原則発動しません。

事前に実行アカウントでOfficeを起動し、必要な機能が問題なく動作することを確認しておけば、突発的なインストールや修復の可能性はかなり低くなります。

さらに、Office 2016以降(特にC2R版)では、このMSIの挙動はかなり簡素化・抑制されています。

最後

サーバーサイドのセキュリティ: Office アプリケーションは、サーバーサイドで使用するように設計されていません。 そのため、Office アプリケーションは、配布されたコンポーネントで発生するセキュリティの問題を考慮していません。 Office は受信要求を認証しません。 また、サーバーサイド コードから、予期しないマクロの実行や、マクロを実行する可能性がある他のサーバーの起動を阻止する機能もありません。 匿名 Web サイトからサーバーにアップロードされたファイルを開かないでください。 サーバーは、最後に設定されたセキュリティ設定に基づいて、完全な特権を持つ管理者またはシステム コンテキストでマクロを実行できるため、ネットワークが侵害される可能性があります。 また、Office は処理を高速にするために、クライアントの認証情報をキャッシュできる多くのクライアント側コンポーネント (Simple MAPI、WinInet、MSDAIPP など) を使用しています。 Office がサーバーサイドで自動化されている場合、1 つのインスタンスで複数のクライアントにサービスを提供する可能性があります。 そのセッションで認証情報がキャッシュされると、あるクライアントが別のクライアントのキャッシュ済み資格情報を使用する可能性があります。 結果として、そのクライアントは他のユーザーになりすまして許可されていないアクセス許可を得る可能性があります。

この話では、素性のわからないファイルを実行してはいけないという話なので、プログラム自身が生成するような用途だとこの問題は該当しないと考えています

いろんなリスクはあるものの状況によっては回避し問題が起きる可能性は下げれるものの、ただサポート外の動作であるため、やはり使わないことがベストという話になります

続いてライセンスの話

技術的な問題とは別に、ライセンスの問題も考慮する必要があります。 現在のライセンス ガイドラインでは、クライアントにライセンス認証済みの Office がインストールされている場合を除き、クライアント要求を処理するサーバー上で Office アプリケーションを使用することは禁じられています。 サーバーサイド オートメーションを使用して、ライセンス認証されていないワークステーションに Office 機能を提供することは、ソフトウェア ライセンス条項 (EULA) では認められていません。

サーバー側にライセンス無しでの実行がNGなのは言うまでもなくだと思います・・

最終的に・・・

Microsoft では、サーバー側ソリューションを開発する必要がある場合は、開発者が Office の Automation の代替手段を見つけることを強くお勧めします。

別手段で実行することを推奨しています。そのため、何かしらのライブラリを使用しての対応が必要になってくるという話ですね・・・

なので、COM経由の操作は許可しているものの、ユーザーがログインしている状況化で、アプリを表示した状態で実行し意図しない動きの場合に対応ができることが前提という話になるので、サーバーでは技術的につかわないようにという話でした・・・

投稿日時: 2025-05-11 03:50:11

ExcelVBA

VBAだとWritePasswordにパスを設定しておけば、開いた際にパスを求められるようになります

Sub Sample()
    Dim BookObj     As Workbook
    Dim FilePath    As String
    Dim WritePass   As String: Pass = "test"
    
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    '書き込みパスワード付きExcelを開く
    Set BookObj = Workbooks.Open(FilePath, WriteResPassword:=WritePass)
    
    Set BookObj = Nothing
End Sub

Excel(COM)

Excel(COM)も同様です。

using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
using System;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");
            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;
            workbooks = application.Workbooks;

            // 書き込みパスワード
            var writePass = "test";

            // 書き込みパスワードでブックを開く
            workbook = workbooks.Open(Filename: filePath, WriteResPassword: writePass);  // 名前付き引数がおすすめ
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref workbook, true, false);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);

            // RCW強制解放(残留プロセス対策)
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

ClosedXML

未対応

EPPlus

未対応

ExcelDataReader

未対応

NPOI

未対応

参考:
ClosedXML, ExcelDataReader, EPPlus, NPOIがパスワード解除について

投稿日時: 2025-05-10 18:18:10
更新日時: 2025-05-24 09:25:24

ExcelVBA

Open時、ReadOnlyをTrueにすることで読み取り専用で開く

Sub Sample()
    Dim BookObj As Workbook
    Dim FilePath    As String
    
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath, ReadOnly:=True)
    
    Set BookObj = Nothing
End Sub

Excel(COM)

VBA同様 ReadOnlyにTrueを指定して、読み取り専用で開く

using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
using System;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;
            workbooks = application.Workbooks;
            // ブックを開く(ReadOnlyの項目をtrue)
            workbook = workbooks.Open(Filename: filePath, ReadOnly: true); // 名前付き引数がおすすめ
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref workbook, true, false);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);

            // RCW強制解放(残留プロセス対策)
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

ClosedXML

FileStreamで読み取り専用で開く

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み取りモード、共有なし(排他)
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (var workbook = new XLWorkbook(stream))
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

FileStreamで読み取り専用で開く

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み取りモード、共有は読み取りのみOK
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (var package = new ExcelPackage(stream))
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

ExcelDataReader

FileAccessがReadであろうがなかろうが、読み取りのみのライブラリのため書き込む機能なし

using ExcelDataReader;
using System;
using System.Data;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleExcelDataReader
{
    static void Main(string[] args)
    {
        // デフォルトでは、エンコード(CP1252)がサポートされておらずエラーになるのでこれが必要
        System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み取りモード、共有は読み取りのみOK
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (var reader = ExcelReaderFactory.CreateReader(fileStream))
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

NPOI

FileStreamで読み取り専用で開く
□xls

using NPOI.HSSF.UserModel;
using System;
using System.IO;
using System.Reflection;


namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xls");

            // ブックを開く、読み取りモード、共有は読み取りのみOK
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                var workbook = new HSSFWorkbook(fileStream);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsx

using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み取りモード、共有は読み取りのみOK
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                var workbook = new XSSFWorkbook(fileStream);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xls/xlsx対応版

Program.cs

using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        try
        {   // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            var extension = Path.GetExtension(filePath);

            // ブックを開く、読み取りモード、共有は読み取りのみOK
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (var workbook = WorkbookFactory.Create(fileStream, extension))
            { 
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode; // C#10

public class WorkbookFactory
{
    public static IWorkbook Create(Stream stream, string extension)
    {
        extension = extension.ToLower();

        return extension switch
        {
            ".xls" => new HSSFWorkbook(stream),
            ".xlsx" => new XSSFWorkbook(stream),
            _ => throw new NotSupportedException("対象外の拡張子です")
        };
    }

    public static IWorkbook Create(string extension)
    {
        extension = extension.ToLower();

        return extension switch
        {
            ".xls" => new HSSFWorkbook(),
            ".xlsx" => new XSSFWorkbook(),
            _ => throw new NotSupportedException("対象外の拡張子です")
        };
    }
}
投稿日時: 2025-05-10 16:10:10
更新日時: 2025-05-24 09:06:24

既存ファイルを開く

ExcelVBA

Sub Sample()
    Dim BookObj As Workbook
    Dim FilePath    As String
    
    'ファイルパス作成
    FilePath = ThisWorkbook.Path & "\sample.xlsx"
    
    'ブックを開く
    Set BookObj = Workbooks.Open(FilePath)
    
    Set BookObj = Nothing
End Sub

Excel(COM)

using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // Excelを開く
            application = new Excel.Application();
            application.Visible = true;
            workbooks = application.Workbooks;
            // ブックを開く
            workbook = workbooks.Open(filePath);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref workbook, true, false);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);

            // RCW強制解放(残留プロセス対策)
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

補足:Microsoft.Office.Interop.Excel でオブジェクトの解放について

ClosedXML

ファイルパス、FileStreamを使った開き方

□ファイルパスを使って開く

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            using (var workbook = new XLWorkbook(filePath))
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□FileStreamを使って開く

using ClosedXML.Excel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XLWorkbook(fileStream))
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

fileStreamを使えば、ファイルへのアクセス方法(読み書き)や他プロセスへの共有方法も制御できるので細かい制御が必要であればこちらを使う

EPPlus

ファイルパス、FileInfo、FileStreamを使った開き方

□ファイルパスを使って開く

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ファイルパスで開く
            using (var package = new ExcelPackage(filePath))
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□FileInfoを使って開く

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ファイルの情報を使う予定があるならfleinfoで開く
            var fileInfo = new FileInfo(filePath);

            // ファイルパスで開く
            using (var package = new ExcelPackage(fileInfo))
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

更新日時とかファイルの情報を使う予定ならFileInfoを使って開く


□FileStreamを使って開く

using OfficeOpenXml;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var package = new ExcelPackage(fileStream))
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

fileStreamを使えば、ファイルへのアクセス方法(読み書き)や他プロセスへの共有方法も制御できるので細かい制御が必要であればこちらを使う

ExcelDataReader

Streamのみ

using ExcelDataReader;
using System;
using System.Data;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleExcelDataReader
{
    static void Main(string[] args)
    {
        // デフォルトでは、エンコード(CP1252)がサポートされておらずエラーになるのでこれが必要
        System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み取りモード、共有は読み取りのみOK
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (var reader = ExcelReaderFactory.CreateReader(fileStream))
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

NPOI

Streamのみ

□xls

using NPOI.HSSF.UserModel;
using System;
using System.IO;
using System.Reflection;


namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xls");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new HSSFWorkbook(fileStream))
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsx

using NPOI.XSSF.UserModel;
using System;
using System.IO;
using System.Reflection;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        try
        {
            // 実行ファイルのあるフォルダパス取得
            var folderPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
            if (folderPath == null) { return; }

            // Excelファイルパスを作成
            var filePath = Path.Combine(folderPath, "sample.xlsx");

            // ブックを開く、読み書きモード、共有なし(排他)
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            using (var workbook = new XSSFWorkbook(fileStream))
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xls/xlsx対応版

Program.cs

using System;
namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        try
        {   // xls
            using (var workbook_xls = WorkbookFactory.Create(".xls"))
            {
            }

            // xlsx
            using (var workbook_xlsx = WorkbookFactory.Create(".xlsx"))
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode; // C#10

public class WorkbookFactory
{
    public static IWorkbook Create(Stream stream, string extension)
    {
        extension = extension.ToLower();

        return extension switch
        {
            ".xls" => new HSSFWorkbook(stream),
            ".xlsx" => new XSSFWorkbook(stream),
            _ => throw new NotSupportedException("対象外の拡張子です")
        };
    }

    public static IWorkbook Create(string extension)
    {
        extension = extension.ToLower();

        return extension switch
        {
            ".xls" => new HSSFWorkbook(),
            ".xlsx" => new XSSFWorkbook(),
            _ => throw new NotSupportedException("対象外の拡張子です")
        };
    }
}
投稿日時: 2025-05-08 16:15:08
更新日時: 2025-05-24 11:51:24

新規にブックを作成する

ExcelVBA

Sub Sample()
    Dim bookObj As workbook
    
    Set bookObj = Workbooks.Add
    
    Set bookObj = Nothing
End Sub

Excel(COM)

using System;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

namespace SampleCode; // C#10~

internal class SampleExcel
{
    static void Main(string[] args)
    {
        Excel.Application? application = null;
        Excel.Workbooks? workbooks = null;
        Excel.Workbook? workbook = null;

        try
        {
            // Excelが起動
            application = new Excel.Application();

            // 表示
            application.Visible = true; // 処理を確認したいときだけtrueにする

            // ブックを追加
            workbooks = application.Workbooks;
            workbook = workbooks.Add();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            CleanUpComObject(ref workbook, true, false);
            CleanUpComObject(ref workbooks);
            CleanUpComObject(ref application);

            // RCW強制解放(残留プロセス対策)
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }

    static void CleanUpComObject<T>(ref T comObject, bool shouldClose = true, bool saveChanges = false)
    {
        // フラグによってWorkbookはClose / ApplicationはQuitする
        if (shouldClose && comObject != null)
        {
            // 型をチェックする
            if (comObject is Microsoft.Office.Interop.Excel.Workbook workbook)
            {
                // Workbookの場合
                workbook.Close(saveChanges);
            }
            else if (comObject is Microsoft.Office.Interop.Excel.Application application)
            {
                // Applicationの場合
                application.Quit();
            }
        }

        // Objectを解放
        if (comObject != null && Marshal.IsComObject(comObject))
        {
            Marshal.ReleaseComObject(comObject);
            comObject = default!;
        }
    }
}

補足:Microsoft.Office.Interop.Excel でオブジェクトの解放について

ClosedXML

using ClosedXML.Excel;
using System;

namespace SampleCode; // C#10~

internal class SampleClosedXML
{
    static void Main(string[] args)
    {
        try
        {
            using (var workbook = new XLWorkbook())
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

EPPlus

using OfficeOpenXml;
using System;
namespace SampleCode; // C#10~

internal class SampleEPPlus
{
    static void Main(string[] args)
    {
        // Ver8.0のソースです
        // 非商用個人利用の場合 名前を設定
        ExcelPackage.License.SetNonCommercialPersonal("SampleTarou");

        try
        {
            using (var package = new ExcelPackage())
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

ExcelDataReader

読み取りのみのライブラリのためなし

NPOI

□xls

using NPOI.HSSF.UserModel;
using System;


namespace SampleCode; // C#10~

internal class SampleNPOI_xls
{
    static void Main(string[] args)
    {
        try
        {
            using (var workbook = new HSSFWorkbook())
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xlsx

using NPOI.XSSF.UserModel;
using System;

namespace SampleCode; // C#10~

internal class SampleNPOI_xlsx
{
    static void Main(string[] args)
    {
        try
        {
            // xlsx形式
            using (var workbook = new XSSFWorkbook())
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

□xls/xlsx対応版

Program.cs

using System;
namespace SampleCode; // C#10~

internal class SampleNPOI
{
    static void Main(string[] args)
    {
        try
        {   // xls
            using (var workbook_xls = WorkbookFactory.Create(".xls"))
            {
            }

            // xlsx
            using (var workbook_xlsx = WorkbookFactory.Create(".xlsx"))
            {
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

WorkbookFactory.cs

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.IO;

namespace SampleCode; // C#10

public class WorkbookFactory
{
    public static IWorkbook Create(Stream stream, string extension)
    {
        extension = extension.ToLower();

        return extension switch
        {
            ".xls" => new HSSFWorkbook(stream),
            ".xlsx" => new XSSFWorkbook(stream),
            _ => throw new NotSupportedException("対象外の拡張子です")
        };
    }

    public static IWorkbook Create(string extension)
    {
        extension = extension.ToLower();

        return extension switch
        {
            ".xls" => new HSSFWorkbook(),
            ".xlsx" => new XSSFWorkbook(),
            _ => throw new NotSupportedException("対象外の拡張子です")
        };
    }
}
投稿日時: 2025-05-08 15:05:08
更新日時: 2025-05-24 01:57:24

COM経由でExcelを操作するための準備

依存関係から右クリックでCOM参照の追加を選択

Microsoft.Excel XX.X ObjectLibrary を選択(要Excelインストール)

OKで閉じる

ClosedXMLでExcelを操作するための準備

Nugetから ClosedXML をインストール

ExcelDataReaderでExcelを操作するための準備

Nugetから ExcelDataReader をインストール

ExcelDataReader.DataSetもインストールしてください
AsDataSet() を使うのに必要になってきます

EPPlusでExcelを操作するための準備

Nugetから EPPlus をインストール

NPOIでExcelを操作するための準備

Nugetから NPOIをインストール

投稿日時: 2025-05-07 13:02:07
更新日時: 2025-05-17 04:16:17

最近の投稿

最近のコメント

タグ

アーカイブ

その他