- - PR -
Vb.netでExcelが終了しない
投稿者 | 投稿内容 | ||||
---|---|---|---|---|---|
|
投稿日時: 2004-08-14 19:41
はじめまして。よろしくお願いします。
以前、http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=3895&forum=7 でExcelへの書き込みが増えるとExcelのプロセスが終了しないという書き込みがありましたが、同様の事象がVB.netでもありました。 コードを下記に明記します。 Private Sub a_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles a.Click Dim intRowPos As Integer Dim strFileName as Strimg = "C:\a.xls" Dim blnRet As Boolean Dim objExcel As New Excel.Application Dim objBooks As New Excel.Workbooks Dim objBook As New Excel.Workbook Dim objSheets As New Excel.Sheets Dim objSheet As New Excel.Worksheet Dim objCell As Excel.Range objExcel = CreateObject("Excel.Application") objBooks = Nothing objBook = Nothing objSheets = Nothing objSheet = Nothing Try If Dir$(strFileName) = "" Then objExcel.Workbooks.Add() Else objBooks = objExcel.Workbooks objBook = objBooks.Open(strFileName) End If objSheets = objBook.Sheets objSheet = objSheets(1) objCell = objSheet.Cells objCell.Clear() ''行の開始位置を設定 intRowPos = 1 Do if inrRowPos = 10001 Then Exit Do End if objCell(intRowPos, 1) = 1 objCell(intRowPos, 2) = 2 objCell(intRowPos, 3) = 3 objCell(intRowPos, 4) = 4 objCell(intRowPos, 5) = 5 objCell(intRowPos, 6) = 6 objCell(intRowPos, 7) = 7 objCell(intRowPos, = 8 objCell(intRowPos, 9) = 9 objCell(intRowPos, 10) = 10 objCell(intRowPos, 11) = 11 objCell(intRowPos, 12) = 12 objCell(intRowPos, 13) = 13 objCell(intRowPos, 14) = 14 objCell(intRowPos, 15) = 15 objCell(intRowPos, 16) = 16 objCell(intRowPos, 17) = 17 objCell(intRowPos, 18) = 18 objCell(intRowPos, 19) = 19 objCell(intRowPos, 20) = 20 intRowPos = intRowPos + 1 Loop objExcel.DisplayAlerts = False objBook.SaveAs(strFileName, Excel.XlFileFormat.xlExcel9795) objExcel.DisplayAlerts = True Catch ex As Exception ''Excel操作によるエラー処理 MsgBox(Err.Description) Return End Try If (IsNothing(objCell) = False) Then ''ランタイム呼び出し可能ラッパーの参照カウントをデクリメントする System.Runtime.InteropServices.Marshal.ReleaseComObject(objCell) objCell = Nothing End If If (IsNothing(objSheet) = False) Then ''ランタイム呼び出し可能ラッパーの参照カウントをデクリメントする System.Runtime.InteropServices.Marshal.ReleaseComObject(objSheet) objSheet = Nothing End If If (IsNothing(objSheets) = False) Then ''ランタイム呼び出し可能ラッパーの参照カウントをデクリメントする System.Runtime.InteropServices.Marshal.ReleaseComObject(objSheets) objSheets = Nothing End If If (IsNothing(objBook) = False) Then ''保存したExcelシートのクローズ objBook.Close(False) ''ランタイム呼び出し可能ラッパーの参照カウントをデクリメントする System.Runtime.InteropServices.Marshal.ReleaseComObject(objBook) objBook = Nothing End If If (IsNothing(objBooks) = False) Then ''ランタイム呼び出し可能ラッパーの参照カウントをデクリメントする System.Runtime.InteropServices.Marshal.ReleaseComObject(objBooks) objBooks = Nothing End If 'Excelを閉じる objExcel.Quit() 'Excelオブジェクトの解放 If Not objExcel Is Nothing Then ''ランタイム呼び出し可能ラッパーの参照カウントをデクリメントする System.Runtime.InteropServices.Marshal.ReleaseComObject(objExcel) objExcel = Nothing End If 'ガベージコレクションのメモリの解放 System.GC.Collect() 'DoEvents を発行 System.Windows.Forms.Application.DoEvents() Return End Sub どなたか解決方法をご伝授いただけませんでしょうか? よろしくお願いします。 ちなみに、開発環境は、Windows2000/Excel2000/Vb.net2003です。 | ||||
|
投稿日時: 2004-08-15 12:46
Dim objExcel As New Excel.Application
objExcel = CreateObject("Excel.Application") は, Dim objExcel As Excel.Application objExcel = New Excel.Application <----- (1) objExcel = CreateObject("Excel.Application") <----- (2) のようになるわけだから,(1) と (2) のふたつのインスタンスを作成しています。 この場合,(1)のインスタンス(参照カウント)を開放できなくなってしまいますよね? なので,New を取って, Dim objExcel As Excel.Application objExcel = CreateObject("Excel.Application") です。 また,これが実際の原因でないとしても, 他でも同様で, New を宣言時に付けると "既定のExcelのインスタンス" ができてしまうので, Dim objExcel As New Excel.Application Dim objBooks As New Excel.Workbooks Dim objBook As New Excel.Workbook Dim objSheets As New Excel.Sheets Dim objSheet As New Excel.Worksheet でなく Dim objExcel As Excel.Application Dim objBooks As Excel.Workbooks Dim objBook As Excel.Workbook Dim objSheets As Excel.Sheets Dim objSheet As Excel.Worksheet の方がいいですよね? というか,コードを見た限り,Newする必要はないです。 で,実際の開放は,Excelのアプリだけでいいハズです。 | ||||
|
投稿日時: 2004-08-15 12:54
いえ、ブックやシートも解放の必要があります。 AI-Lightというサイトで、最近「Excelの事でお聞きします」という質問がありました。こちらも参照してみてください。 | ||||
|
投稿日時: 2004-08-17 06:49
> いえ、ブックやシートも解放の必要があります。
確かにそのようなんですが... でも,例えば,横着して, Dim app As Excel.Application app = New Excel.ApplicationClass app.Workbooks.Add() For i As Integer = 1 To 100 app.Workbooks(1).Worksheets(1).Cells(i, 1).Formula _ = "Hoge" & CStr(i) Next app.Workbooks(1).Close(False) app.Quit() System.Runtime.InteropServices. _ Marshal.ReleaseComObject(app) app = Nothing GC.Collect() MessageBox.Show("終了") でも,残らないんですよね... 理屈的には,上位オブジェクトさえちゃんと開放すれば, 下位オブジェクトは開放されないとおかしいじゃないかな という気がするのですが... | ||||
|
投稿日時: 2004-08-17 07:06
今回の件ですが,宣言時に New してしまうと,
Dim objBooks As New Excel.Workbooks は Dim objBooks As Excel.Workbooks objBooks = New Excel.Workbooks の意味になり,オートメーションなのに, Applicationオブジェクトから参照をたどっていないので 既定のExcelのインスタンスができてしまっていて それが開放されないのが主な原因のような気がします... | ||||
|
投稿日時: 2004-08-17 08:21
そう単純な話でもないです。 COM ランタイムはオブジェクトの寿命を参照カウンタベースで管理しますが、現行の .NET の COM Interrop は参照カウンタを真面目に管理していません。 なので、比較的 COM 仕様に素直に従っている Office アプリケーションでは、「下位オブジェクトが居残っているが故に、Quit() してもサーバが終了しない」という事態が発生してしまいます。 IE なんかは、下位オブジェクトの参照が残っていても、Quit() で有無を言わさずサーバが終了するつくりになっています。 _________________ // 渋木宏明 (Hiroaki SHIBUKI) // http://hidori.jp/ // Microsoft MVP for Visual C# // // @IT会議室 RSS 配信中: http://hidori.jp/rss/atmarkIT/ | ||||
|
投稿日時: 2004-08-17 18:40
みなさん、いろいろとありがとうございました。
また、返事が遅くなりまして申し訳ございません。 実際のところVB6.0までは Dim objExcel As Excel.Application objExcel = CreateObject("Excel.Application") objExcel.WorkBooks(1).Sheets(1).Cells(1,1).Value = 100 objExcel.Quit() Set objExcel =Nothing と記述すれば、Excelのプロセスは解放されていたみたいですが、VB.NETだと、「WorkBooks」 「Sheets」「Cells」それぞれに対してのオブジェクトが「objExcel」にひもづくオブジェ クトとして、自動的に生成し確保されているみたいです。 (暗黙の了解で作成されているため、見た目では判断つかないそうです。) そのため、ガベージコレクションで、「Excel.Application」オブジェクトを解放しようとし た際、暗黙的で作られた、「WorkBooks」「Sheets」「Cells」の各オブジェクトは「暗黙 的なオブジェクト=必要なオブジェクト」と判断されるようです。すなわち、「明らかに不 要であるオブジェクト」と判断できないようです。その結果、親である「Excel.Application」 オブジェクトも解放されずに、Excelのプロセスが残ってしまってたみたいですね。 すなわち Dim objExcel As Excel.Application Dim objBooks As Excel.Workbooks Dim objBook As Excel.Workbook Dim objSheets As Excel.Sheets Dim objSheet As Excel.Worksheet Dim objCells As Excel.Range Dim objCell As Excel.Range objExcel = CreateObject("Excel.Application") objBooks = Nothing objBook = Nothing objSheets = Nothing objSheet = Nothing objCells = Nothing objCell = Nothing objBooks = objExcel.Workbooks objBook = objBooks.Open(strFileName) objSheets = objBook.Sheets objSheet = objSheets(1) objCells = objSheet.Cells objCell = objSheet.Cells(1, 1) objCell.Value = 100 System.Runtime.InteropServices.Marshal.ReleaseComObject(objCell) objCell = Nothing System.Runtime.InteropServices.Marshal.ReleaseComObject(objCells) objCells = Nothing System.Runtime.InteropServices.Marshal.ReleaseComObject(objSheet) objSheet = Nothing System.Runtime.InteropServices.Marshal.ReleaseComObject(objSheets) objSheet = Nothing objBook.Close(False) System.Runtime.InteropServices.Marshal.ReleaseComObject(objBook) objBook = Nothing System.Runtime.InteropServices.Marshal.ReleaseComObject(objBooks) objBooks = Nothing objExcel.Quit() System.Runtime.InteropServices.Marshal.ReleaseComObject(objExcel) objExcel = Nothing System.GC.Collect() System.Windows.Forms.Application.DoEvents() といった形で、ワークシート・ブック・セルそれぞれに対して明確的なオブジェクトを参照 して明確なオブジェクトを解放するという手順を踏まえて、行うとプロセスが消えたみた いです。 また、画面を使ってExcelの操作を行う場合は、実際の解放処理は画面に制御が戻った時な ので、「DoEvents()」を用いて処理を行ったほうが、確実にプロセスが解放できるみたいです。 稍丼さん・Jittaさん・渋木宏明(ひどり)さん、ありがとうございました。 | ||||
|
投稿日時: 2004-08-18 00:57
横着してもちゃんと消えた環境を書いておきます。
WinXP SP1 & Excel 2002 SP3 & VB.NET 2003 で確認しています。なんとなくですが, Excelのバージョンよっても微妙に違うということも なきにしもあらずという気もするんですが... 昨日からいろいろなパターンで検証しましたが,やはり, 上記の環境だと,Excelのインスタンスが残らないようにするには, VB.NET側で,変数でうけたものは,obj = Nothing は必要ですが, 途中利用?する個別のオブジェクトをわざわざ ReleaseComObject(...) する必要はなく, GC.Collect() の方が必須条件になっています。 |