Excelプロセスの後処理


さち  2009-06-06 13:48:12  No: 142017  IP: 192.*.*.*

VB2005Pro+WindowsXPです。

FAQによくあるExcelのプロセス事後処理なんですが、
いろいろな情報を元に最低限の処理を組んでみました。
#参照設定でExcelを追加しています。

Dim aplExcel As _
      New Microsoft.Office.Interop.Excel.Application
Dim xlBooks As Microsoft.Office.Interop.Excel.Workbooks
Dim xlSheet As Microsoft.Office.Interop.Excel.Worksheet
aplExcel.Visible = True
xlBooks = aplExcel.Workbooks
xlBooks.Add()
xlSheet = xlBooks(1).Sheets(1)
xlSheet.Range("B3").Value = "XXXXX"
xlBooks(1).Saved = True
aplExcel.Quit()
System.Runtime.InteropServices. _
    Marshal.ReleaseComObject(xlSheet)
System.Runtime.InteropServices. _
    Marshal.ReleaseComObject(xlBooks)
System.Runtime.InteropServices. _
    Marshal.ReleaseComObject(aplExcel)
'GC.Collect()

一応画面上にExcelシートが出てきて消えるという動作はしています。
最大の着目点は実行後にExcelプロセスが残るかどうかです。

上記コードを繰り返し実行しながらタスクマネージャでExcelプロセスの
有無を監視していると、残Excelプロセスの数が7-8個になったあとの
実行でいくつかのExeclプロセスがクリアされるようです。
「7-8個」というのはあまり確定的ではなく、1個目でクリアされるときも
あります。また、すべてクリアされる時もあれば1個残るときもあります。

上記コードの最終行のコメントをはずして「GC.Collect()」を必ず実行
すると確実にクリアされています。

そこで質問ですが、
1.上記のComオブジェクトの使用方法は正しいのでしょうか?
2.自動的にガベージコレクションを実行してくれる、という.NETの
    仕様を利用(7-8個残るのを容認)していていいのでしょうか?
3.それとも最終行の GC.Collect() を積極的に実行したほうがいい
    のでしょうか?
お願いいたします。

編集 削除
魔界の仮面弁士  2009-06-07 00:47:14  No: 142018  IP: 192.*.*.*

> 1.上記のComオブジェクトの使用方法は正しいのでしょうか?
正しくありません。たとえば、以下のような問題があります。

> xlBooks.Add()
ここでは、戻り値を変数に保持する必要があります。これは、戻り値となる
COM オブジェクトを、後で ReleaseComObject するために必要です。

> xlSheet = xlBooks(1).Sheets(1)
この場合には、
  book1 = xlBooks(1)
  sheets = book1.Sheets
  sheet1 = DirectCast(sheets(1), Excel.Worksheet)
のように、途中にある COM オブジェクトも取得しておかないと、
後から ReleaseComObject できません。

さらに言えば、先の Workbooks.Add の時点で、book1 のインスタンスを
得る事ができるはずなので、この場合、xlBooks(1) を取得しなおす必要は無いはずです。

> xlSheet.Range("B3").Value = "XXXXX"
これもNG。この場合には
  rng = xlSheet.Range("B3")
  rng.Value = "XXXXX"
として、後からこの rng As Excel.Range を ReleaseComObject しましょう。

> xlBooks(1).Saved = True
先に取得した変数を用いて、
  book1.Saved = True
のようにします。

> 2.自動的にガベージコレクションを実行してくれる、という.NETの
>     仕様を利用(7-8個残るのを容認)していていいのでしょうか?
それは保険的なものですから、容認せず、きちんと自分で後始末をつけた方が良いでしょう。


> 3.それとも最終行の GC.Collect() を積極的に実行したほうがいい
>     のでしょうか?
ガベージコレクトは、通常は .NET 任せで構わないと思います。
ジェネレーション管理など、その仕組みをきちんと理解した上で呼ぶ分には構いませんが、
呼び出すべきではないタイミングで、無計画に呼び出すというのは止めておいた方が無難かと。

編集 削除