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オブジェクトを使用する場合、1つ上の親オブジェクトを宣言しそこからアクセスする。
''' 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 '1.excelApplication解放処理用Try
Dim workbooks As Excel.Workbooks = excelApplication.Workbooks
Try '2.workbooks解放処理用Try
Dim workbook As Excel.Workbook = workbooks.Open(filePath)
Try '3.workbook解放処理用Try
Dim worksheets As Excel.Sheets = workbook.Sheets()
Try '4.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 '5.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 '6.Range解放処理用Try
If cell.Value = "" Then
Continue For
End If
'セルデータをデータクラスにセット
range = worksheet.Range(cell.Value)
CallByName(sheetData, cell.Key, CallType.Set, range.Value)
Finally
'6.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 '6.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
'6.Range解放処理
If range IsNot Nothing Then
Marshal.ReleaseComObject(range)
End If
End Try
Next
Next
'シート名取得
sheetData.SheetName = worksheet.Name
Finally
'5.worksheet解放処理
Marshal.ReleaseComObject(worksheet)
End Try
'戻り値データを設定
retList.Add(sheetData)
Next
Finally
'4.worksheets解放処理
Marshal.ReleaseComObject(worksheets)
End Try
Finally
'3.workbook解放処理
If workbook IsNot Nothing Then
excelApplication.DisplayAlerts = False
workbook.Close()
End If
Marshal.ReleaseComObject(workbook)
End Try
Finally
'2.workbooks解放処理用Try
Marshal.ReleaseComObject(workbooks)
End Try
Finally
'1.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オブジェクトを使用する場合、1つ上の親オブジェクトを宣言しそこからアクセスする。
''' 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 '1.excelApplication解放処理用Try
frmProgress = New XlsExportProgress("部品表単価を出力中です。", outputData.Count)
frmProgress.Show()
'メッセージ表示を無しに変更
excelApplication.DisplayAlerts = False
Dim workbooks As Excel.Workbooks = excelApplication.Workbooks
Try '2.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 '3.workbook解放処理用Try
Dim worksheets As Excel.Sheets = workbook.Sheets()
Try '4.worksheets解放処理用Try
'■全データ出力
For i_data As Integer = 0 To outputData.Count - 1
Dim worksheet As Excel.Worksheet = Nothing
Dim sheetData As New clsDataExcelInput
Try '5.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 '6.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
'6.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 '6.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
'6.Range解放処理
If range IsNot Nothing Then
Marshal.ReleaseComObject(range)
End If
End Try
Next
Next
End With
Finally
'5.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
'4.worksheets解放処理
Marshal.ReleaseComObject(worksheets)
End Try
'■書き込み内容を保存
workbook.SaveAs(outputFilePath, Excel.XlFileFormat.xlExcel8)
Finally
'3.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
'2.workbooks解放処理用Try
Marshal.ReleaseComObject(workbooks)
End Try
Finally
If frmProgress IsNot Nothing Then
frmProgress.Close()
End If
'1.excelApplication解放処理
If excelApplication IsNot Nothing Then
excelApplication.Quit()
End If
Marshal.ReleaseComObject(excelApplication)
End Try
End Sub