vb.netでCOM参照オブジェクトを使用してExcelを扱う

##COM参照オブジェクトはあんまり良くない。
・COM参照オブジェクトはプロセスの解放漏れが非常に発生しやすい
 ※下記サンプルに詳しく書いてあります。
・低速(実装方法によって改善は可能)
・Excelのインストールが必須

なので、まだオープンソースライブラリ等を検討できるならそちらの導入をお勧めします。

##サンプル1 読み込み


    ''' <summary>
    ''' 入力元Excel読み込み処理
    ''' </summary>
    ''' <param name="filePath">Excelファイルパス</param>
    ''' <remarks>
    ''' ※Excelの読み込みでCOM参照を使用しているため、解放処理を厳密に記載すること。
    ''' ※解放処理を確実に行うため、以下のルールを厳守すること。
    ''' ・宣言したすべてのCOMオブジェクトに対し、Finally句で解放処理を行う。←解放漏れを確実に防ぐため。
    ''' 
    ''' ・COM解放は、子階層のものから解放すること。←子階層が解放されていないと親階層が解放されない。
    '''   Application
    '''   └Workbooks
    '''     └Workbook
    '''       └Worksheets
    '''         └Worksheet
    '''           └Range    ←この階層から解放する
    ''' 
    ''' ・子階層のCOMオブジェクトを使用する場合、１つ上の親オブジェクトを宣言しそこからアクセスする。
    '''   NG例：Dim excelApp As New Excel.Application
    '''         Dim wkbk As Excel.Workbook = Nothing
    '''         wkbk = Application.Workbooks.Open(FilePath) ←Workbooksが暗黙変換され、解放できないExcelプロセスができる。
    ''' 
    '''   OK例：Dim excelApp As New Excel.Application
    '''         Dim wkbks As Excel.Workbooks = Nothing
    '''         Dim wkbk As Excel.Workbook = Nothing
    '''         wkbks = excelApp.Workbooks	
    '''         wkbk = wkbks.Open(ustrFilePath)
    ''' 
    ''' ・DirectCastはCOMオブジェクトには使わない。←キャストされたCOMオブジェクトは解放できない。
    ''' </remarks>
    Private Function ReadExcelData(ByVal filePath As String) As List(Of clsDataExcelInput)

        Dim retList As New List(Of clsDataExcelInput)

        Dim excelApplication As New Excel.Application()
        Try '１．excelApplication解放処理用Try

            Dim workbooks As Excel.Workbooks = excelApplication.Workbooks
            Try '２．workbooks解放処理用Try

                Dim workbook As Excel.Workbook = workbooks.Open(filePath)
                Try '３．workbook解放処理用Try

                    Dim worksheets As Excel.Sheets = workbook.Sheets()
                    Try '４．worksheets解放処理用Try

                        '■全シートを参照
                        For sheet As Integer = 1 To worksheets.Count

                            Dim sheetData As New clsDataExcelInput
                            Dim worksheet As Excel.Worksheet = CType(worksheets(sheet), Excel.Worksheet)
                            Try '５．worksheet解放処理用Try

                                'シート名Eは終了を示すので、以降のシートは読み込まない
                                If worksheet.Name.Trim = "E" Then
                                    Exit For
                                End If

                                '■ヘッダ部分を参照
                                For Each cell As KeyValuePair(Of String, String) In pHashHeaderCellAddress

                                    Dim range As Excel.Range = Nothing
                                    Try '６．Range解放処理用Try
                                        If cell.Value = "" Then
                                            Continue For
                                        End If

                                        'セルデータをデータクラスにセット
                                        range = worksheet.Range(cell.Value)
                                        CallByName(sheetData, cell.Key, CallType.Set, range.Value)

                                    Finally
                                        '６．Range解放処理
                                        If range IsNot Nothing Then
                                            Marshal.ReleaseComObject(range)
                                        End If
                                    End Try

                                Next

                                '■明細列を参照
                                For row As Integer = LineFrom To LineSu + LineFrom - 1
                                    For Each col As KeyValuePair(Of String, String) In pHashMeisaiColAddress

                                        Dim range As Excel.Range = Nothing
                                        Try '６．Range解放処理用Try
                                            If col.Value = "" Then
                                                Continue For
                                            End If
                                            range = worksheet.Range(col.Value & row.ToString)
                                            '取り消し線設定時は処理しない
                                            If range.Font.Strikethrough IsNot Nothing AndAlso CBool(range.Font.Strikethrough) = True Then
                                                Exit For
                                            End If
                                            CallByName(sheetData, col.Key, CallType.Set, range.Text)

                                        Finally
                                            '６．Range解放処理
                                            If range IsNot Nothing Then
                                                Marshal.ReleaseComObject(range)
                                            End If
                                        End Try

                                    Next
                                Next

                                'シート名取得
                                sheetData.SheetName = worksheet.Name

                            Finally
                                '５．worksheet解放処理
                                Marshal.ReleaseComObject(worksheet)
                            End Try

                            '戻り値データを設定
                            retList.Add(sheetData)
                        Next

                    Finally
                        '４．worksheets解放処理
                        Marshal.ReleaseComObject(worksheets)
                    End Try

                Finally
                    '３．workbook解放処理
                    If workbook IsNot Nothing Then
                        excelApplication.DisplayAlerts = False
                        workbook.Close()
                    End If
                    Marshal.ReleaseComObject(workbook)
                End Try

            Finally
                '２．workbooks解放処理用Try
                Marshal.ReleaseComObject(workbooks)
            End Try

        Finally
            '１．excelApplication解放処理
            If excelApplication IsNot Nothing Then
                excelApplication.Quit()
            End If
            Marshal.ReleaseComObject(excelApplication)
        End Try

        Return retList

    End Function

##サンプル2 書き出し


    ''' <summary>
    ''' Excel出力処理
    ''' </summary>
    ''' <param name="outputFolder">出力フォルダパス</param>
    ''' <param name="xml">出力先情報XMLファイル</param>
    ''' <param name="outputData">Excel出力データ</param>
    ''' <remarks>
    ''' ※Excelの読み込みでCOM参照を使用しているため、解放処理を厳密に記載すること。
    ''' ※解放処理を確実に行うため、以下のルールを厳守すること。
    ''' ・宣言したすべてのCOMオブジェクトに対し、Finally句で解放処理を行う。←解放漏れを確実に防ぐため。
    ''' 
    ''' ・COM解放は、子階層のものから解放すること。←子階層が解放されていないと親階層が解放されない。
    '''   Application
    '''   └Workbooks
    '''     └Workbook
    '''       └Worksheets
    '''         └Worksheet
    '''           └Range    ←この階層から解放する
    ''' 
    ''' ・子階層のCOMオブジェクトを使用する場合、１つ上の親オブジェクトを宣言しそこからアクセスする。
    '''   NG例：Dim excelApp As New Excel.Application
    '''         Dim wkbk As Excel.Workbook = Nothing
    '''         wkbk = Application.Workbooks.Open(FilePath) ←Workbooksが暗黙変換され、解放できないExcelプロセスができる。
    ''' 
    '''   OK例：Dim excelApp As New Excel.Application
    '''         Dim wkbks As Excel.Workbooks = Nothing
    '''         Dim wkbk As Excel.Workbook = Nothing
    '''         wkbks = excelApp.Workbooks	
    '''         wkbk = wkbks.Open(ustrFilePath)
    ''' 
    ''' ・DirectCastはCOMオブジェクトには使わない。←キャストされたCOMオブジェクトは解放できない。
    ''' </remarks>
    Private Sub WriteExcelData(ByVal outputFolder As String, ByVal xml As clsDataXmlTemplate, ByVal outputData As List(Of DataSet))


        Dim excelApplication As New Excel.Application()
        Dim frmProgress As XlsExportProgress = Nothing
        Try '１．excelApplication解放処理用Try

            frmProgress = New XlsExportProgress("部品表単価を出力中です。", outputData.Count)
            frmProgress.Show()

            'メッセージ表示を無しに変更
            excelApplication.DisplayAlerts = False

            Dim workbooks As Excel.Workbooks = excelApplication.Workbooks
            Try '２．workbooks解放処理用Try

                '出力先ファイル
                Dim outputFilePath As String = Nothing
                Try
                    '■テンプレートから出力ファイルを作成
                    With True

                        'ファイルパス取得
                        Dim templatePath As String = cmnFileAccess.JoinPath(csAppSetting.DllFilesPath, "Excel\" & xml.baseFileName).ToString

                        Dim seiban As String = outputData(0).Tables(clsDataExcelInput.cTableNameHeader).Rows(0)(clsDataExcelInput.enumColHeader.Seiban).ToString
                        outputFilePath = cmnFileAccess.JoinPath(outputFolder, "加工部品表_" & seiban & "_" & Now.ToString("yyyyMMddhhmm") & ".xls").ToString

                        System.IO.File.Copy(templatePath, outputFilePath, True)

                    End With

                    Dim workbook As Excel.Workbook = workbooks.Open(outputFilePath)
                    Try '３．workbook解放処理用Try

                        Dim worksheets As Excel.Sheets = workbook.Sheets()
                        Try '４．worksheets解放処理用Try

                            '■全データ出力
                            For i_data As Integer = 0 To outputData.Count - 1

                                Dim worksheet As Excel.Worksheet = Nothing
                                Dim sheetData As New clsDataExcelInput
                                Try '５．worksheet解放処理用Try

                                    '■テンプレートシートから出力シートを生成
                                    With True
                                        Dim templateSheet As Excel.Worksheet = CType(worksheets(1), Excel.Worksheet)
                                        Dim lastSheet As Excel.Worksheet = CType(worksheets(worksheets.Count), Excel.Worksheet)
                                        Try
                                            templateSheet.Copy(After:=lastSheet)
                                            worksheet = CType(worksheets(worksheets.Count), Excel.Worksheet)
                                        Finally
                                            'テンプレートで使ったシート解放
                                            Marshal.ReleaseComObject(templateSheet)
                                            Marshal.ReleaseComObject(lastSheet)
                                        End Try
                                    End With

                                    '■ヘッダ部分を参照
                                    With outputData(i_data).Tables(clsDataExcelInput.cTableNameHeader)
                                        worksheet.Name = .Rows(0)("SheetName").ToString
                                        For Each col As DataColumn In .Columns
                                            Dim range As Excel.Range = Nothing
                                            Try '６．Range解放処理用Try

                                                Dim cell As DataRow() = xml.dsHeaderStyle.Tables("columnsSettings").Select("key = '" & col.ColumnName & "'")
                                                If cell(0)("value").ToString = "" Then
                                                    Continue For
                                                End If

                                                'セルデータをデータクラスにセット
                                                range = worksheet.Range(cell(0)("value").ToString)
                                                range.Value = .Rows(0)(col).ToString
                                            Finally
                                                '６．Range解放処理
                                                If range IsNot Nothing Then
                                                    Marshal.ReleaseComObject(range)
                                                End If
                                            End Try
                                        Next
                                    End With

                                    '■明細列を参照
                                    With outputData(i_data).Tables(clsDataExcelInput.cTableNameMeisai)
                                        For i_row As Integer = 0 To .Rows.Count - 1

                                            Dim rowWrite As String = (CInt(xml.dsDetailStyle.Tables("detailHeaderSettings").Select("key = 'detailHeaderUnitRow'")(0)("value")) + i_row + 1).ToString
                                            For Each col As DataColumn In .Columns
                                                Dim range As Excel.Range = Nothing
                                                Try '６．Range解放処理用Try
                                                    If .Rows(i_row)(col) Is DBNull.Value OrElse .Rows(i_row)(col) Is Nothing Then
                                                        Continue For
                                                    End If
                                                    '書き込みセルを取得
                                                    Dim colWrite As String = xml.dsDetailStyle.Tables("columnsSettings").Select("key = '" & col.ColumnName & "'")(0)("value").ToString.Split(","c)(0)

                                                    'セルデータをデータクラスにセット
                                                    range = worksheet.Range(colWrite & rowWrite)
                                                    range.Value = .Rows(i_row)(col).ToString
                                                Finally
                                                    '６．Range解放処理
                                                    If range IsNot Nothing Then
                                                        Marshal.ReleaseComObject(range)
                                                    End If
                                                End Try
                                            Next

                                        Next
                                    End With

                                Finally
                                    '５．worksheet解放処理
                                    Marshal.ReleaseComObject(worksheet)
                                End Try

                                'プログレスバーカウントUP
                                frmProgress.AddCountProgress()
                            Next

                            'テンプレートシートを削除
                            With True
                                Dim templateSheet As Excel.Worksheet = CType(worksheets(1), Excel.Worksheet)
                                Try
                                    templateSheet.Select()
                                    templateSheet.Delete()
                                Finally
                                    'テンプレートで使ったシート解放
                                    Marshal.ReleaseComObject(templateSheet)
                                End Try
                            End With

                        Finally
                            '４．worksheets解放処理
                            Marshal.ReleaseComObject(worksheets)
                        End Try

                        '■書き込み内容を保存
                        workbook.SaveAs(outputFilePath, Excel.XlFileFormat.xlExcel8)

                    Finally
                        '３．workbook解放処理
                        If workbook IsNot Nothing Then
                            workbook.Close()
                        End If
                        Marshal.ReleaseComObject(workbook)
                    End Try

                Catch ex As Exception
                    '処理失敗時は作成した出力ファイルを削除
                    If outputFilePath IsNot Nothing AndAlso System.IO.File.Exists(outputFilePath) Then
                        System.IO.File.Delete(outputFilePath)
                    End If
                    Throw ex
                End Try
            Finally
                '２．workbooks解放処理用Try
                Marshal.ReleaseComObject(workbooks)
            End Try

        Finally

            If frmProgress IsNot Nothing Then
                frmProgress.Close()
            End If

            '１．excelApplication解放処理
            If excelApplication IsNot Nothing Then
                excelApplication.Quit()
            End If
            Marshal.ReleaseComObject(excelApplication)
        End Try

    End Sub