@IT会議室は、ITエンジニアに特化した質問・回答コミュニティ「QA@IT」に生まれ変わりました。ぜひご利用ください。
- PR -

何度も出ているExcelプロセスの件です。

投稿者投稿内容
K5
常連さん
会議室デビュー日: 2006/04/22
投稿数: 25
投稿日時: 2006-04-29 19:40
開発環境
VB.NET(VS2003)
EXCEL(OFFICE2003)

何度も出ている話題で申し訳ないのですが、過去のスレッドを参考に作成したのですが、Excelプロセスが残ってしまいます。
ビルドしてexeから実行しても同じでした。
アプリケーションを終了すると、Excelのプロセスも終了します。
アプリケーションを終了しないでも、Excelのプロセスが終了するようにしたいと考えています。

参考にさせて頂いたURL↓
http://jeanne.wankuma.com/tips/programing/releasecom.html
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=25113&forum=7&start=9
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=26918&forum=7&start=4

xlRange 解放時に以下のようなエラーが出ているので、xlRange の使い方が悪いのでしょうか。
<error: an exception of type: {System.Runtime.InteropServices.COMException} occurred> Excel.Range

初心者で申し訳ないですが、是非ご教授下さい。

コードも長くて申し訳ないです。
よろしくお願い致します。

コード:

' 必要な変数は Try の外で宣言する
Dim xlApplication As Excel.Application

' COM オブジェクトの解放を保証するために Try 〜 Finally を使用する
Try
xlApplication = New Excel.Application
' 警告メッセージなどを表示しないようにする
xlApplication.DisplayAlerts = False
Dim xlBooks As Excel.Workbooks = xlApplication.Workbooks
Try
Dim xlBook As Excel.Workbook = xlBooks.Add()
Try
Dim xlSheets As Excel.Sheets = xlBook.Worksheets
Try
Dim xlSheet As Excel.Worksheet = DirectCast(xlSheets(1), Excel.Worksheet)
Try
Dim xlCells As Excel.Range = xlSheet.Cells
Try
Dim xlRange As Excel.Range
Try
For iRowCnt = 0 To dtTbl.Rows.Count - 1
'列数分だけ処理
For iColCnt = 0 To dtTbl.Columns.Count - 1
'1行目の場合はカラム名を出力
If iRowCnt = 0 Then
xlRange = DirectCast(xlCells(iRowCnt + 2, iColCnt + 2), Excel.Range)
xlRange.NumberFormat = "@"
xlRange.Value = dtTbl.Columns(iColCnt).ColumnName
End If
xlRange = DirectCast(xlCells(iRowCnt + 3, iColCnt + 2), Excel.Range)
If iColCnt <> 10 And iColCnt <> 14 And iColCnt <> 16 Then
xlRange.NumberFormat = "@"
End If
xlRange.Value = CStr(dtTbl.Rows(iRowCnt)(iColCnt))
Next iColCnt
Next
'Excel を表示する(デバッグ用)
'xlApplication.Visible = True
'線を引く
xlSheet.UsedRange.Borders(Excel.XlBordersIndex.xlEdgeLeft).LineStyle = 1
xlSheet.UsedRange.Borders(Excel.XlBordersIndex.xlEdgeTop).LineStyle = 1
xlSheet.UsedRange.Borders(Excel.XlBordersIndex.xlEdgeBottom).LineStyle = 1
xlSheet.UsedRange.Borders(Excel.XlBordersIndex.xlEdgeRight).LineStyle = 1
xlSheet.UsedRange.Borders(Excel.XlBordersIndex.xlInsideVertical).LineStyle = 1
xlSheet.UsedRange.Borders(Excel.XlBordersIndex.xlInsideHorizontal).LineStyle = 1
'自動サイズ調整
xlSheet.Cells.Select()
xlSheet.Cells.EntireColumn.AutoFit()
'ファイルの保存
xlApplication.Workbooks(1).SaveCopyAs(strFilePath)
Finally
If Not xlRange Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange)
End If
End Try
Finally
If Not xlCells Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlCells)
End If
End Try
Finally
If Not xlSheet Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheet)
End If
End Try
Finally
If Not xlSheets Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheets)
End If
End Try
Finally
If Not xlBook Is Nothing Then
Try
xlBook.Close()
Finally
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBook)
End Try
End If
End Try
Finally
If Not xlBooks Is Nothing Then
Try
xlBooks.Close()
Catch ex As Exception
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBooks)
End Try
End If
End Try
Finally
If Not xlApplication Is Nothing Then
Try
xlApplication.Quit()
Finally
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApplication)
End Try
End If
End Try



[ メッセージ編集済み 編集者: K5 編集日時 2006-04-29 19:41 ]

[ メッセージ編集済み 編集者: K5 編集日時 2006-04-29 19:44 ]

[ メッセージ編集済み 編集者: K5 編集日時 2006-04-29 19:46 ]

[ メッセージ編集済み 編集者: K5 編集日時 2006-04-29 19:51 ]
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2006-04-29 20:26
こんにちは、何度もこの件について回答をしている、じゃんぬ です。(;^-^)

引用:

K5さんの書き込み (2006-04-29 19:40) より:

For iRowCnt = 0 To dtTbl.Rows.Count - 1
   :
Next


この間、xlRange が何度か未到達になっていますね。

引用:

xlRange = DirectCast(xlCells(iRowCnt + 2, iColCnt + 2), Excel.Range)


ここで設定していますが、

引用:

xlRange = DirectCast(xlCells(iRowCnt + 3, iColCnt + 2), Excel.Range)


参照を解放せず新たな COM オブジェクトを格納しています。
これではいけません。

引用:

xlSheet.UsedRange.Borders(Excel.XlBordersIndex.xlEdgeLeft).LineStyle = 1
xlSheet.UsedRange.Borders(Excel.XlBordersIndex.xlEdgeTop).LineStyle = 1
xlSheet.UsedRange.Borders(Excel.XlBordersIndex.xlEdgeBottom).LineStyle = 1
xlSheet.UsedRange.Borders(Excel.XlBordersIndex.xlEdgeRight).LineStyle = 1
xlSheet.UsedRange.Borders(Excel.XlBordersIndex.xlInsideVertical).LineStyle = 1
xlSheet.UsedRange.Borders(Excel.XlBordersIndex.xlInsideHorizontal).LineStyle = 1

xlSheet.Cells.Select()
xlSheet.Cells.EntireColumn.AutoFit()
xlApplication.Workbooks(1).SaveCopyAs(strFilePath)


このあたりのご自分で書かれたと思われるコードは全滅ですね。
中間で暗黙的に参照されているものがたくさんあります。

過去スレを見られたとのことですが、このあたりは過去に耳にタコができるくらい、
私自身が何度か書き込みしていることなので、もう 1 度読み返した方が良いかもしれません。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
K5
常連さん
会議室デビュー日: 2006/04/22
投稿数: 25
投稿日時: 2006-04-29 21:21
じゃんぬねっとさん

ご返信ありがとうございます。
読み返してもうまくいかないK5です。(ーー;)
過去スレを読んで自分なりに書き直してみたのですが、どうもうまく行きません。。。

まず、ご指摘のあったループでその都度COM オブジェクトを解放するようにしました。
コード:

For iRowCnt = 0 To dtTbl.Rows.Count - 1
'列数分だけ処理
For iColCnt = 0 To dtTbl.Columns.Count - 1
'1行目の場合はカラム名を出力
If iRowCnt = 0 Then
xlRange = DirectCast(xlCells(iRowCnt + 2, iColCnt + 2), Excel.Range)
xlRange.NumberFormat = "@"
xlRange.Value = dtTbl.Columns(iColCnt).ColumnName
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange) '追加
End If
xlRange = DirectCast(xlCells(iRowCnt + 3, iColCnt + 2), Excel.Range)
If iColCnt <> 10 And iColCnt <> 14 And iColCnt <> 16 Then
xlRange.NumberFormat = "@"
End If
xlRange.Value = CStr(dtTbl.Rows(iRowCnt)(iColCnt))
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange) '追加
Next iColCnt
Next



また、自分で書いたところも下のように変数に入れて書き直すようにしてみました。
これって全く理解できてないでしょうか。。。
何度も申し訳ありませんが、よろしくお願い致します。

コード:

'線を引く
xlRange = DirectCast(xlSheet.UsedRange, Excel.Range)
xlRange.Borders(Excel.XlBordersIndex.xlEdgeLeft).LineStyle = 1
xlRange.Borders(Excel.XlBordersIndex.xlEdgeTop).LineStyle = 1
xlRange.Borders(Excel.XlBordersIndex.xlEdgeBottom).LineStyle = 1
xlRange.Borders(Excel.XlBordersIndex.xlEdgeRight).LineStyle = 1
xlRange.Borders(Excel.XlBordersIndex.xlInsideVertical).LineStyle = 1
xlRange.Borders(Excel.XlBordersIndex.xlInsideHorizontal).LineStyle = 1
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange)

'自動サイズ調整
xlRange = DirectCast(xlSheet.Cells, Excel.Range)
xlRange.Select()
xlRange.EntireColumn.AutoFit()
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange)

'ファイルの保存
xlBook.SaveAs(strFilePath)





[ メッセージ編集済み 編集者: K5 編集日時 2006-04-29 22:45 ]
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2006-04-29 22:42
引用:

K5さんの書き込み (2006-04-29 21:21) より:

読み返してもうまくいかないK5です。


読み返して頂けるだけでもありがたいです。(^-^)

引用:

また、自分で書いたところも下のように変数に入れて書き直すようにしてみました。
これって全く理解できてないでしょうか。。。


大丈夫です、理解できていますよ。

引用:

xlRange.EntireColumn.AutoFit()


このように、メンバへのパスが 2 連続以上ある場合は注意してください。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
K5
常連さん
会議室デビュー日: 2006/04/22
投稿数: 25
投稿日時: 2006-04-30 00:57
じゃんぬねっとさん
本当にありがとうございます。
何とか解決して、本当に助かりました。

さんざか出ている話なので、意味があるかわかりませんがコードを一応載せます。
Excelで線を引いたり、サイズの自動調整したい人のお役に立てればと思います。
コード見づらくてすいません。

コード:

' 必要な変数は Try の外で宣言する
Dim xlApplication As Excel.Application
' COM オブジェクトの解放を保証するために Try 〜 Finally を使用する
Try
xlApplication = New Excel.Application
' 警告メッセージなどを表示しないようにする
xlApplication.DisplayAlerts = False
Dim xlBooks As Excel.Workbooks = xlApplication.Workbooks
Try
Dim xlBook As Excel.Workbook = xlBooks.Add()
Try
Dim xlSheets As Excel.Sheets = xlBook.Worksheets
Try
Dim xlSheet As Excel.Worksheet = DirectCast(xlSheets(1), Excel.Worksheet)
Try
Dim xlCells As Excel.Range = xlSheet.Cells
Try
Dim xlRange As Excel.Range
Try
For iRowCnt = 0 To dtTbl.Rows.Count - 1
'列数分だけ処理
For iColCnt = 0 To dtTbl.Columns.Count - 1
'1行目の場合はカラム名を出力
If iRowCnt = 0 Then
xlRange = DirectCast(xlCells(iRowCnt + 2, iColCnt + 2), Excel.Range)
xlRange.NumberFormat = "@"
xlRange.Value = dtTbl.Columns(iColCnt).ColumnName
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange)
End If
xlRange = DirectCast(xlCells(iRowCnt + 3, iColCnt + 2), Excel.Range)
If iColCnt <> 10 And iColCnt <> 14 And iColCnt <> 16 Then
xlRange.NumberFormat = "@"
End If
xlRange.Value = CStr(dtTbl.Rows(iRowCnt)(iColCnt))
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange)
Next iColCnt
Next
'Excel を表示する(デバッグ用)
'xlApplication.Visible = True
'線を引く
xlRange = DirectCast(xlSheet.UsedRange, Excel.Range)
Dim xlBorders As Excel.Borders
Try
xlBorders = xlRange.Borders
xlBorders.LineStyle = 1
Finally
If Not xlBorders Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBorders)
End If
End Try
'自動サイズ調整
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange)
xlRange = DirectCast(xlCells.EntireColumn, Excel.Range)
xlRange.AutoFit()
'ファイルの保存
xlBook.SaveAs(strFilePath)
Finally
If Not xlRange Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange)
End If
End Try
Finally
If Not xlCells Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlCells)
End If
End Try
Finally
If Not xlSheet Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheet)
End If
End Try
Finally
If Not xlSheets Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheets)
End If
End Try
Finally
If Not xlBook Is Nothing Then
Try
xlBook.Close()
Finally
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBook)
End Try
End If
End Try
Finally
If Not xlBooks Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBooks)
End If
End Try
Finally
If Not xlApplication Is Nothing Then
Try
xlApplication.Quit()
Finally
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApplication)
End Try
End If
End Try



[ メッセージ編集済み 編集者: K5 編集日時 2006-04-30 00:58 ]
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2006-04-30 09:34
引用:

K5さんの書き込み (2006-04-30 00:57) より:

さんざか出ている話なので、意味があるかわかりませんがコードを一応載せます。
Excelで線を引いたり、サイズの自動調整したい人のお役に立てればと思います。
コード見づらくてすいません。


K5 さん、フィードバック、ありがとうございます。(*_ _)

xlRange についてですが、最小の For 内で宣言した方が安全です。
それ以外で使いまわしをしていますが、別の変数をあてた方がわかりやすいでしょう。

一部ですが、

コード:

    For iRowCnt = 0 To dtTbl.Rows.Count - 1
        For iColCnt = 0 To dtTbl.Columns.Count - 1
            If iRowCnt = 0 Then
                ' なるべく狭いスコープで宣言
                Dim xlRangeTop As Excel.Range

                Try
                    xlRangeTop = DirectCast(xlCells(iRowCnt + 2, iColCnt + 2), Excel.Range)
                    xlRangeTop.NumberFormat = "@"
                    xlRangeTop.Value = dtTbl.Columns(iColCnt).ColumnName
                Finally
                    If Not xlRangeTop Is Nothing Then
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRangeTop)
                    End If
                End Try
            End If

            Dim xlRange As Excel.Range

            Try
                xlRange = DirectCast(xlCells(iRowCnt + 3, iColCnt + 2), Excel.Range)

                If iColCnt <> 10 And iColCnt <> 14 And iColCnt <> 16 Then
                    xlRange.NumberFormat = "@"
                End If

                xlRange.Value = (dtTbl.Rows(iRowCnt)(iColCnt)).ToString()
            Finally
                If Not xlRange Is Nothing Then
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange)
                End If
            End Try
        Next
    Next


それ以外の部分は適切に割り当てられていて、ネストは深いですが見やすいですよ。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2006-04-30 10:00
おそらく最初に質問した Jitta です。

おすすめしませんが、こんな方法もあります。
COM リリース:お勧めではないが、こんな方法も...
# IE 以外の方が見やすい
K5
常連さん
会議室デビュー日: 2006/04/22
投稿数: 25
投稿日時: 2006-04-30 14:01
じゃんぬねっとさん

ご返信ありがとうございます。

引用:

xlRange についてですが、最小の For 内で宣言した方が安全です。
それ以外で使いまわしをしていますが、別の変数をあてた方がわかりやすいでしょう。



基本的に1変数につき、1度の使用を行った方が良いんですね。
わかりやすく解説頂き、ありがとうござます。
載せて頂いたように修正しようと思います。

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