- - PR -
VB.NETでエクセルエグゼが残ってしまいます。
1|2|3|4|5
次のページへ»
投稿者 | 投稿内容 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2006-12-18 17:52
色々なサイトで上記の件名を検索して調べたのですが、
分からないので質問いたします。 まず、調べたサイトでの記述と自分の環境の相違について 自分の開発環境では、以前にシステムをリリースした際に不具合が生じたため、 参照設定でエクセルを参照しないで、エクセル出力処理をしています。 よって、 通常 Dim xlApp As New Excel.Application Dim xlBooks As Excel.Workbooks = xlApp.Workbooks Dim xlBook As Excel.Workbook = xlBooks.Add Dim xlSheets As Excel.Sheets = xlBook.Worksheets Dim xlSheet As Excel.Worksheet = xlSheets.Item(1) と宣言する部分を Dim xlApp As New Object xlApp = CType(CreateObject("Excel.Application"), Object) Dim xlBooks As Object = xlApp.Workbooks Dim xlBook As Object = xlBooks.Add Dim xlSheets As Object = xlBook.Worksheets Dim xlSheet As Object = xlSheets.Item(1) Dim xlCells As Object Dim xlRange As Object と宣言しています。 オブジェクト型の変数に無理やりしれたような感じです。 処理を終了してから 'オブジェクトの解放 MRComObject(xlCells) MRComObject(xlRange) MRComObject(xlSheet) MRComObject(xlSheets) xlBook.Close(False) MRComObject(xlBook) MRComObject(xlBooks) xlApp.Quit() MRComObject(xlApp) GC.Collect() ↓(http://www.bcap.co.jp/hanafusa/dotnet/Excelflm.htm参考) Private Sub MRComObject(ByRef objCom As Object) 'COM オブジェクトの使用後、明示的に COM オブジェクトへの参照を解放する Try '提供されたランタイム呼び出し可能ラッパーの参照カウントをデクリメントします If Not objCom Is Nothing AndAlso System.Runtime.InteropServices. _ Marshal.IsComObject(objCom) Then Dim I As Integer Do I = System.Runtime.InteropServices.Marshal.ReleaseComObject(objCom) Loop Until I <= 0 End If Catch Finally '参照を解除する objCom = Nothing End Try End Sub と行っています。 ある処理の場合はエクセルエグゼが残らずに消えるのですが、 もう一つの処理の場合だけ、エグゼが残ってしまいます。 しかし、システムを終了するとエグゼは消えます。 どなたかご教授願います。 | ||||||||||||
|
投稿日時: 2006-12-18 19:25
遅延バインディングならではの現象 だと思ったのですが、今回は無理矢理参照カウントをデクリメントしているようで... もうちょっと詳しい実装を見てみないと、アドバイスできそうにないです。 現象が確認できる、最低限のコード (ミニマム コード) を提示して頂けるのが 1 番です。
これに関しては、何ら不思議なことはないです。 ところで、"エグゼが消える" ではなく、"プロセスが解放される" と言いましょう。 それと、花ちゃんさんからのサイトに掲載されているコード、非常にまずいですね。
これだと、他で使われている場合も強制的に参照カウントをデクリメントされます。 こんな危険な実装をするくらいなら、GC に頼った方がまだマシだと思いますね。 _________________ C# と VB.NET の入門サイト じゃんぬねっと日誌 | ||||||||||||
|
投稿日時: 2006-12-19 11:01
ご返答ありがとうございます。
現象が確認出来るコードを掲載します。 'エクセル出力する為の変数定義 Dim xlApp As New Object xlApp = CType(CreateObject("Excel.Application"), Object) Dim xlBooks As Object = xlApp.Workbooks Dim xlBook As Object = xlBooks.Add Dim xlSheets As Object = xlBook.Worksheets Dim xlSheet As Object = xlSheets.Item(1) Dim xlCells As Object Dim xlRange As Object Dim vliRowsCount As Integer Dim vliColCount As Integer Dim omDataTable As New DataTable Dim olHairetu(0, omDataTable.Columns.Count - 1) Dim vlsCol As String Try 'データセットからデータテーブルの設定 omDataTable = omDataSet.Tables(0) 'Excelを非表示 xlApp.Visible = False '保存時の問合せのダイアログを非表示に設定 xlApp.DisplayAlerts = False 'シートの削除 xlSheets("Sheet2").delete() xlSheets("Sheet3").delete() 'セルの設定 xlCells = xlSheet.Cells '*************************************************************************** '↓以下の処理を行う場合(起動判定が"0"以外の場合)、プロセスが解放されません。↓ '*************************************************************************** '起動判別が"0"以外の場合 If vgsKidouHanbetu <> 0 Then '出力レイアウトの設定("1"の場合フォーマットの設定) Me.sgUriageSetLayout(xlSheets, xlSheet, vasJigyosyoName, 1) For vliColCount = 0 To (omDataTable.Columns.Count - 1) 'セル指定 xlRange = xlCells(1, vliColCount + 1) '出力内容 '項目名の設定 xlRange.value = omDataSet.Tables(0).Columns(vliColCount).ColumnName Next ReDim olHairetu(0, omDataTable.Columns.Count - 1) vlsCol = ":AQ" For vliRowsCount = 0 To (omDataTable.Rows.Count - 1) '最大列数分ループ For vliColCount = 0 To (omDataTable.Columns.Count - 1) olHairetu(0, vliColCount) = omDataTable.Rows.Item(vliRowsCount).ItemArray(vliColCount) '*************************************************************************** '↓これは文字化け対策です。必要最低限か迷ったので掲載します。↓ '*************************************************************************** 'DBNULLだった場合は処理を抜ける If IsDBNull(omDataTable.Rows.Item(vliRowsCount).ItemArray(vliColCount)) = False Then olHairetu(0, vliColCount) = Replace(olHairetu(0, vliColCount), "〜", "〜") End If Next xlSheets("Sheet1").range("A" & vliRowsCount + 2 & vlsCol & vliRowsCount + 2).Value = olHairetu Next '列幅の設定 xlSheets("Sheet1").columns("A:AQ").AutoFit() 'シート名の設定 xlSheet.name = vasJigyosyoName '********************* 'エクセルに保存 '********************* xlSheet.SaveAs(omSaveFilePass) '*************************************************************************** '↓以下の処理を行う場合(起動判定が"0"の場合)問題無くプロセスが解放されています。↓ '*************************************************************************** '起動判別が"0"の場合 Else If vasJigyosyoName = "全社" Then vlsCol = ":AQ" '出力レイアウトの設定 Me.sgUriageSetLayout(xlSheets, xlSheet, vasJigyosyoName, 1) Else vlsCol = ":AC" '出力レイアウトの設定 Me.sgUriageSetLayout(xlSheets, xlSheet, vasJigyosyoName, 2) End If '項目名の出力 For vliColCount = 0 To (omDataTable.Columns.Count - 1) 'セル指定 xlRange = xlCells(1, vliColCount + 1) '出力内容 '項目名の設定 xlRange.value = omDataSet.Tables(0).Columns(vliColCount).ColumnName Next ReDim olHairetu(0, omDataTable.Columns.Count - 1) '実データの出力 For vliRowsCount = 0 To (omDataTable.Rows.Count - 1) '最大列数分ループ For vliColCount = 0 To (omDataTable.Columns.Count - 1) olHairetu(0, vliColCount) = omDataTable.Rows.Item(vliRowsCount).ItemArray(vliColCount) '*************************************************************************** '↓同じく文字化け対策です。必要最低限か迷ったので掲載します。↓ '*************************************************************************** 'DBNULLだった場合は処理を抜ける If IsDBNull(omDataTable.Rows.Item(vliRowsCount).ItemArray(vliColCount)) = False Then olHairetu(0, vliColCount) = Replace(olHairetu(0, vliColCount), "〜", "〜") End If Next xlSheets("Sheet1").range("A" & vliRowsCount + 2 & vlsCol & vliRowsCount + 2).Value = olHairetu Next '列幅の設定 xlSheets("Sheet1").columns("A:AQ").AutoFit() 'シート名の設定 xlSheet.name = vasJigyosyoName '********************* '全社フォルダに保存(企画部保管用) '********************* If vasJigyosyoName = "全社" Then 'ファイルパス xlSheet.SaveAs(cgsCsvFileDir & "\" & vasJigyosyoName & "\売上受注\売上受注明細_" & Now.ToString("yyyyMMdd") & "(全社).xls") '********************* '各部支店用フォルダに保存 '********************* ElseIf vasJigyosyoName <> "全社" Then 'ファイルパス xlSheet.SaveAs(cgsCsvFileDir & "\" & vasJigyosyoName & "\売上受注\売上受注明細_" & Now.ToString("yyyyMMdd") & "(" & vasJigyosyoName & ").xls") End If End If Catch ex As Exception Return "1131" Finally 'オブジェクトの解放 MRComObject(xlCells) MRComObject(xlRange) MRComObject(xlSheet) MRComObject(xlSheets) xlBook.Close(False) MRComObject(xlBook) MRComObject(xlBooks) xlApp.Quit() MRComObject(xlApp) GC.Collect() End Try End Function というソースです。 概要はデータセットに格納されたデータを各処理条件(起動判定)によって 異なる形でエクセル出力すると言ったものです。 それと、 引用: -------------------------------------------------------------------------------- じゃんぬねっとさんの書き込み (2006-12-18 19:25) より: GC に頼った方がまだマシだと思いますね。 -------------------------------------------------------------------------------- とはどういう事でしょうか? GC.Collect() を使うという事でしょうか? 正直に申しますと、「GC.Collect()」 も他のサンプルソースで行っていたので それをみて記述した状況です。 | ||||||||||||
|
投稿日時: 2006-12-19 11:19
"現象が確認できる、最低限のコード (ミニマム コード) を提示して頂けるのが 1 番です" と書きました。 必要最低限かどうかは、試せばわかることだと思いますが... 言い方を変えますと '余分なものは必要ない' です。
参照カウントさえ、適切にデクリメントしていれば、GC.Collect メソッドなど必要ないです。 それどころか、GC.Collect メソッドはコストが高いので、無闇に使用すべきでないです。 ただ、先に指摘しました通り、参照カウントが 0 になるように、 無理矢理デクリメントするような実装よりは、遥かにマシだということです。 言い方を変えますと、
このようなことは、してはいけません。(先のレスで、太字にした部分です) _________________ C# と VB.NET の入門サイト じゃんぬねっと日誌 | ||||||||||||
|
投稿日時: 2006-12-19 11:45
調査した所次のコードを掲載します。
Dim xlApp As New Object xlApp = CType(CreateObject("Excel.Application"), Object) Dim xlBooks As Object = xlApp.Workbooks Dim xlBook As Object = xlBooks.Add Dim xlSheets As Object = xlBook.Worksheets Dim xlSheet As Object = xlSheets.Item(1) Dim xlCells As Object Dim xlRange As Object Dim vliRowsCount As Integer Dim vliColCount As Integer Dim omDataTable As New DataTable Dim olHairetu(0, omDataTable.Columns.Count - 1) Dim vlsCol As String Try 'データセットからデータテーブルの設定 omDataTable = omDataSet.Tables(0) 'Excelを非表示 xlApp.Visible = False '保存時の問合せのダイアログを非表示に設定 xlApp.DisplayAlerts = False 'シートの削除 xlSheets("Sheet2").delete() xlSheets("Sheet3").delete() 'セルの設定 xlCells = xlSheet.Cells '**************************************************************** '↓これを処理するとプロセスが開放されない状態です '**************************************************************** '列幅の設定 xlSheets("Sheet1").columns("A:AQ").AutoFit() '**************************************************************** 'シート名の設定 xlSheet.name = vasJigyosyoName '********************* 'エクセルに保存 '********************* xlSheet.SaveAs(omSaveFilePass) 'オブジェクトの解放 MRComObject(xlCells) MRComObject(xlRange) MRComObject(xlSheet) MRComObject(xlSheets) xlBook.Close(False) MRComObject(xlBook) MRComObject(xlBooks) xlApp.Quit() MRComObject(xlApp) GC.Collect() 「xlSheets("Sheet1").columns("A:AQ").AutoFit()」 の記述の処理を行わないで実行をした所、無事プロセスは開放されましたが、 上記の処理を行うとプロセスが開放されないと言うところまでわかりました! | ||||||||||||
|
投稿日時: 2006-12-19 12:10
解決?しました。
応急処置かも知れませんが、以下の方法でプロセスの開放が出来たので記載します。 下記の 「列幅の設定 xlSheets("Sheet1").columns("A:AQ").AutoFit() 'シート名の設定 xlSheet.name = vasJigyosyoName 」 と言う処理をこのファンクションで行わないで、 別ファンクションを呼び出して、(xlSheetsとxlSheetを引数で渡します) 処理を行った所プロセスが無事開放されました。 なぜ、この解決方法で大丈夫だったのか、また この解決方法は合っているのかと言うのが疑問に残るため、 引き続きご教授願いたいと思います。 | ||||||||||||
|
投稿日時: 2006-12-19 12:42
これはダメでしょう。 他の COM ラッパーオブジェクトは変数に参照を取り、 ReleaseComObject メソッドで参照カウントをデクリメントしています。 しかし、この部分だけは、なぜかそれが漏れています。 (他の部分はどこかからコピー & ペーストしたものだと思いますが)
まず、xlSheets("Sheet1") の時点で Excel.Worksheet オブジェクトの参照を取り忘れています。 さらに、Columns と Column で合計 3 つの参照の取り忘れがあります。 参照の取り忘れがある時点で、参照カウントのデクリメント漏れが確定していることになります。 このあたりは、過去ログで散々話題になっていることなので、
再度見直した方が良いでしょう。 どのサイトにも書いてある基本中の基本のことです。 _________________ C# と VB.NET の入門サイト じゃんぬねっと日誌 | ||||||||||||
|
投稿日時: 2006-12-21 10:44
返答遅くなりました。
自分なりに理解した部分に関して、つまり xlSheets("Sheet1").columns("A:AQ").AutoFit() を使うには、 columnsとcolumnを上の宣言部でDimで定義してから Dim xlcolumns as object Dim xlcolumn as object 各定義されたものを MRComObject(xlcolumns) MRComObject(xlcolumn) と放り込んで開放してあげればよいと言う事でしょうか。 引用: -------------------------------------------------------------------------------- じゃんぬねっとさんの書き込み (2006-12-19 12:42) より: 再度見直した方が良いでしょう。 -------------------------------------------------------------------------------- 確かに、定義なしでいきなりオブジェクトを使うのはNGであると 書いてありました。↓参照 http://homepage1.nifty.com/rucio/main/technique/teq_15.htm 参照の取り忘れについて少し分からないのですが、 引用: -------------------------------------------------------------------------------- じゃんぬねっとさんの書き込み (2006-12-19 12:42) より: xlSheets("Sheet1") の時点で Excel.Worksheet オブジェクトの参照を取り忘れています。 -------------------------------------------------------------------------------- とはどういう事でしょうか? Dim xlSheets As Object = xlBook.Worksheets と宣言して、 xlSheets の処理を行い、 MRComObject(xlSheets) だけでは足りないと言う事でしょうか? SheetもBookと同じ様にCloseしてあげなきゃいけないのでしょうか?? |
1|2|3|4|5
次のページへ»