- PR -

.netからのExcel操作によるプロセスに関して

1
投稿者投稿内容
ゆう
会議室デビュー日: 2008/09/29
投稿数: 4
投稿日時: 2008-09-29 18:20
お世話になります。ゆうと申します。

VB.NET(visualstudio2005)からCOMオブジェクトを利用して、
Excelを操作しています。
掲示板を色々見せて頂き、なんとか進めていたんですが、
何とも解決できないようなことが起きており、
どなたか経験があれば教えて頂きたいと思い、投稿させてもらいました。
ソースを記載していますので長いですが、どなたか宜しくお願い致します。

[現象]
後述のソースにてExcelを操作している。
Excel操作のみであればプロセスも残らず問題無。
しかし空の変数(文字型)を追加(※1)したところで、
プロセスが残っていることに気づき、その変数を削除したところ、
正常動作した。

[ソース]
''====================================
'' 変数宣言
''====================================
'' エクセル関連
Dim lo_xlApp As Excel.Application = Nothing
Dim lo_xlBooks As Excel.Workbooks = Nothing
Dim lo_xlBook As Excel.Workbook = Nothing
Dim lo_xlSheets As Excel.Sheets = Nothing
Dim lo_xlSheet As Excel.Worksheet = Nothing
Dim lo_PageSetup As Object = Nothing
Dim lo_range As Excel.Range = Nothing
Dim lo_xlBorders As Excel.Border = Nothing
Dim lo_cells As Object = Nothing
Dim lo_font As Object = Nothing

'' 日付関連
Dim ls_start_date As String = ""
Dim ls_end_date As String = ""
Dim lio_culture As CultureInfo = New CultureInfo("ja-JP", True)
lio_culture.DateTimeFormat.Calendar = New JapaneseCalendar()

'' SQL関連
Dim ls_SQL As String = "" '' SQL
Dim lo_DataTable As DataTable = Nothing '' SQLの戻り値
Dim li_SqlCount As Integer = 0 '' SQLの実行件数

'' その他
Dim ls_tax As String = "" '' 消費税

'' ※1
Dim aaa As String = ""
Dim bbb As String = ""
'' ※1


中略


''====================================
'' 起動処理
''====================================
'' Excelインスタンス化
lo_xlApp = New Excel.Application

'' ワークブックを設定
lo_xlBooks = lo_xlApp.Workbooks

'' ブックを追加
lo_xlBook = lo_xlBooks.Add

'' ワークシートを追加
lo_xlSheets = lo_xlBook.Worksheets

'' ワークシートを設定
lo_xlSheet = lo_xlSheets.Item(1)

''--------------
'' ページ設定
''--------------
lo_PageSetup = lo_xlSheet.PageSetup

With lo_PageSetup
.Orientation = ex_xlLandscape
.ZOOM = 75
.LeftMargin = 0
.RightMargin = 0
End With


中略(内容を記載)


''====================================
'' 保存処理
''====================================
'' 保存時の問合せのダイアログを非表示に設定
lo_xlApp.DisplayAlerts = False

'' ファイルに保存
lo_xlBook.SaveAs(as_file_name, Excel.XlFileFormat.xlWorkbookNormal)

''元に戻す
lo_xlApp.DisplayAlerts = True


finallyにて以下処理


Call lsub_ComObject_relese(lo_PageSetup) '' PageSetup の解放
Call lsub_ComObject_relese(lo_xlSheet) '' Sheet の解放
Call lsub_ComObject_relese(lo_xlSheets) '' Sheets の解放
lo_xlBook.Close(False) '' Book を閉じる
Call lsub_ComObject_relese(lo_xlBook) '' Book の解放
Call lsub_ComObject_relese(lo_xlBooks) '' Books の解放
lo_xlApp.Quit() '' Excelを閉じる
Call lsub_ComObject_relese(lo_xlApp) '' App を解放
GC.Collect() '' ガーベジコレクト起動



'' ComObjectの解放は以下処理を実施
Public Sub lsub_ComObject_relese(ByRef ComObject As Object)

Try

'' 提供されたランタイム呼び出し可能ラッパーの参照カウントをデクリメントする
System.Runtime.InteropServices.Marshal.ReleaseComObject(ComObject)

Catch ex As Exception
Throw ex
Finally
'参照を解除する
ComObject = Nothing
End Try
End Sub
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2008-09-29 18:39
引用:

ゆうさんの書き込み (2008-09-29 18:20) より:

後述のソースにてExcelを操作している。
Excel操作のみであればプロセスも残らず問題無。
しかし空の変数(文字型)を追加(※1)したところで、
プロセスが残っていることに気づき、その変数を削除したところ、正常動作した。


ソースを見る限りはたまたまだと思いますけどね。 いずれにせよこのままでは再現できないと思います (略されている箇所が気になりますが)。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
ゆう
会議室デビュー日: 2008/09/29
投稿数: 4
投稿日時: 2008-09-29 19:21
じゃんぬねっとさん!
お返事ありがとうございます。
再現ソースを取り急ぎ作成してみました。

何かお気づきの点ありましたらご教授ください。
※ソース汚くてごめんなさい。

[ソース]
[ソース]
''====================================
'' 変数宣言
''====================================
'' エクセル関連
Dim lo_xlApp As Excel.Application = Nothing
Dim lo_xlBooks As Excel.Workbooks = Nothing
Dim lo_xlBook As Excel.Workbook = Nothing
Dim lo_xlSheets As Excel.Sheets = Nothing
Dim lo_xlSheet As Excel.Worksheet = Nothing
Dim lo_PageSetup As Object = Nothing
Dim lo_range As Excel.Range = Nothing
Dim lo_xlBorders As Excel.Border = Nothing
Dim lo_cells As Object = Nothing
Dim lo_font As Object = Nothing

'' 日付関連
Dim ls_start_date As String = ""
Dim ls_end_date As String = ""
Dim lio_culture As CultureInfo = New CultureInfo("ja-JP", True)
lio_culture.DateTimeFormat.Calendar = New JapaneseCalendar()

'' SQL関連
Dim ls_SQL As String = "" '' SQL
Dim lo_DataTable As DataTable = Nothing '' SQLの戻り値
Dim li_SqlCount As Integer = 0 '' SQLの実行件数

'' その他
Dim ls_tax As String = ""

'' 以下で a1 〜 a40 を宣言
Dim a1 As String = ""



Dim a40 As String = ""

''====================================
'' 起動処理
''====================================
'' Excelインスタンス化
lo_xlApp = New Excel.Application

'' ワークブックを設定
lo_xlBooks = lo_xlApp.Workbooks

'' ブックを追加
lo_xlBook = lo_xlBooks.Add

'' ワークシートを追加
lo_xlSheets = lo_xlBook.Worksheets

'' ワークシートを設定
lo_xlSheet = lo_xlSheets.Item(1)

''--------------
'' ページ設定
''--------------
lo_PageSetup = lo_xlSheet.PageSetup

With lo_PageSetup
.Orientation = ex_xlLandscape
.ZOOM = 75
.LeftMargin = 0
.RightMargin = 0
End With

''--------------
'' 内容記載
''--------------
'' テスト用のメソッドを、ある程度の回数コール
'' ここでは例えば500回くらいコールしてみる
Call sub_set_test(lo_xlSheet.Range("A3:J3"), "test")



Call sub_set_test(lo_xlSheet.Range("A3:J3"), "test")


''====================================
'' 保存処理
''====================================
'' 保存時の問合せのダイアログを非表示に設定
lo_xlApp.DisplayAlerts = False

'' ファイルに保存
lo_xlBook.SaveAs(as_file_name, Excel.XlFileFormat.xlWorkbookNormal)

''元に戻す
lo_xlApp.DisplayAlerts = True


finallyにて以下処理


Call lsub_ComObject_relese(lo_PageSetup) '' PageSetup の解放
Call lsub_ComObject_relese(lo_xlSheet) '' Sheet の解放
Call lsub_ComObject_relese(lo_xlSheets) '' Sheets の解放
lo_xlBook.Close(False) '' Book を閉じる
Call lsub_ComObject_relese(lo_xlBook) '' Book の解放
Call lsub_ComObject_relese(lo_xlBooks) '' Books の解放
lo_xlApp.Quit() '' Excelを閉じる
Call lsub_ComObject_relese(lo_xlApp) '' App を解放
GC.Collect() '' ガーベジコレクト起動





''内容記載用のメソッド
Public Sub gsub_set_test(ByRef ao_range As Excel.Range, ByVal as_str As String)
'--------------
'' 内部変数宣言
'--------------
Dim lo_range As Excel.Range = Nothing

Try
lo_range = ao_range

With lo_range

lo_range.FormulaR1C1 = as_str

End With

Catch ex As Exception

Throw ex

Finally

Call lsub_ComObject_relese(ao_range)

Call lsub_ComObject_relese(lo_range)

End Try
End Sub




'' ComObjectの解放は以下処理を実施
Public Sub lsub_ComObject_relese(ByRef ComObject As Object)

Try

'' 提供されたランタイム呼び出し可能ラッパーの参照カウントをデクリメントする
System.Runtime.InteropServices.Marshal.ReleaseComObject(ComObject)

Catch ex As Exception
Throw ex
Finally
'参照を解除する
ComObject = Nothing
End Try
End Sub
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2008-09-30 10:28
引用:

ゆうさんの書き込み (2008-09-29 19:21) より:

'' テスト用のメソッドを、ある程度の回数コール
'' ここでは例えば500回くらいコールしてみる
Call sub_set_test(lo_xlSheet.Range("A3:J3"), "test")



Call sub_set_test(lo_xlSheet.Range("A3:J3"), "test")


おそらく参照渡しにしているから呼び出し先で ReleaseComObject メソッドでデクリメントしてやれば良いと考えているかと思いますが、ここは呼び出し元でキッチリ参照の面倒を見てください。

これで解決しないようであれば、この部分をコメントアウトしてみてください。 Worksheet.Range はオーバーロードによっては余計な参照がカウントされる恐れがあります (遅延バインディングだと 100%)。

引用:

GC.Collect() '' ガーベジコレクト起動


これは原因がわかりにくくなるだけなので外してください。 GC.Collect メソッドはコストも高いです。

これだけでは解決しないかもしれません。 ゆうさんは "空の変数 (文字型) を追加 (※1) した" もしくは "その変数を削除した" という切り分けをなさっていますが、それ以外の切り分けも積極的に試した方が解決が早いと思います。 たとえば PageSetup の記述をコメントアウトしても再現するか、再現しないかで別の原因が浮き彫りになることがあります。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
ゆう
会議室デビュー日: 2008/09/29
投稿数: 4
投稿日時: 2008-10-01 15:16
じゃんぬねっとさん。
お世話になります。
お返事遅れてしまい申し訳ありません。
色々と試しているのですが、どうしてもプロセスが残ってしまします。。。

PageSetup の記述を書き直し、一旦はOKだったのですが、
同じ構文しか書いていないのに、量が増えてくるとプロセスが残って
しまいます。
きっと私の記載が悪いと思うのですが。。

--ここは呼び出し元でキッチリ参照の面倒を見てください。
というと、
呼び出し元で変数に入れ、デクリメントする。
といった形でしょうか?

Dim lo_ange as Excel.Range

lo_range = lo_xlSheet.Range("A3:J3")

Call sub_set_test(lo_range, "test")

Call lsub_ComObject_relese(lo_range)
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2008-10-01 15:31
引用:

ゆうさんの書き込み (2008-10-01 15:16) より:

PageSetup の記述を書き直し、一旦はOKだったのですが、
同じ構文しか書いていないのに、量が増えてくるとプロセスが残ってしまいます。
きっと私の記載が悪いと思うのですが。。


どこかで参照カウントが残っているのが原因でしょうから、ReleaseComObject メソッドの戻り値の確認も行ってください。

引用:

呼び出し元で変数に入れ、デクリメントする。
といった形でしょうか?


そうです。 Excel.Range は意図しない参照のカウントアップをすることがありますので特に注意してください。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
ゆう
会議室デビュー日: 2008/09/29
投稿数: 4
投稿日時: 2008-10-01 19:47
じゃんぬねっとさん。
お世話になります。

色々細かくチェックしたところ、やはり暗黙宣言の部分がありました。
あるタイミングでは未発生だった為、見落としていたようです。。。
アドバイス等々ありがとうございました!
1

スキルアップ/キャリアアップ(JOB@IT)