- PR -

Vb.netでExcelが終了しない

投稿者投稿内容
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2004-08-18 08:54
引用:

稍丼さんの書き込み (2004-08-18 00:57) より:

Excelのバージョンよっても微妙に違うということも
なきにしもあらずという気もするんですが...


 ひどりさんの、
引用:

渋木宏明(ひどり)さんの書き込み (2004-08-17 08:21) より:

COM ランタイムはオブジェクトの寿命を参照カウンタベースで管理しますが、現行の .NET の COM Interrop は参照カウンタを真面目に管理していません
なので、比較的 COM 仕様に素直に従っている Office アプリケーションでは、「下位オブジェクトが居残っているが故に、Quit() してもサーバが終了しない」という事態が発生してしまいます。


太字にしたところが、とっても重要なように思います

 また、ある環境ではOkでも、別の環境ではNGなら、NGな環境をOkにする作りにしておく必要がある、と思います。
yayadon
常連さん
会議室デビュー日: 2003/07/23
投稿数: 41
投稿日時: 2004-08-19 02:54
GC.Collect() の方がキーになっていると先に書いたんですが,
理由は,GC.Collect() で RCW 自体が開放されて,
握っていたCOMオブジェクトを放すためのようです。

 .NET Client --> RCW --> COMサーバー
                  ↑
          ここの参照は,1止まりなので
          RCWがGCされると,
          .NET Client からの参照数にかかわらず
          自動的にIUnknown::Releaseするため


ということで,GC.Collectは,最終手段としてかなり有効なため
GC.Collectしてもインスタンスが残っている場合は,
まず,暗黙の既定のインスタンスができている可能性が高いです。

>-----------------------------------------------------------
また,よく調べてみると,
ReleaseCOMObjectは,
GC.Collect() を呼ぶ必要がないように用意されたということなので,
.ReleaseCOMObject(...) をしつこく呼でいても,
GC.Collect() を最後に呼んでしまうのならば,
苦労した意味がなくなってしまうような気がします。
GC.Collect() を最後に呼ぶのならば,
あまり細かいことを気にしなくて
ただ,GC.Collect() すればいいことになるわけで。

>-------------------------------------------------------

で,ReleaseCOMObject は,
GC.Collectに頼らなくてもいいように用意されたメソッドなのに,
なので,ReleaseCOMObject(...) を小刻みに呼んでいるのに
どうして開放されないのか,再度,調べてみると...

まず,Cells を Range でうけて,開放します。

>------------------------------------------------------------
  略
 Dim wsh As Excel.Worksheet
 Dim rng As Excel.Range

 略

  rng = wsh.Cells

  For i As Integer = 1 To 100
    rng(i, 1).Formula = "Hoge" & CStr(i)
  Next

  System.Runtime.InteropServices.Marshal.ReleaseComObject(rng)
  rng = Nothing

  略

>------------------------------------------------------------------

上のパターンだとまだ残ります。

で,
Excelのオブジェクトをオブジェクトブラウザで眺めていたんですが,
rng(i, 1) ですが,よく考えてみると,.Cells( row , col ) は,
Rangeオブジェクトから,
再度,新規のRangeオブジェクトが返ってくる形のようです。

なので,

>------------------------------------------------------------------

  略
  Dim wsh As Excel.Worksheet
  Dim rng As Excel.Range

  略

  rng = wsh.Cells

  For i As Integer = 1 To 100
    Dim tmpRange As Excel.Range
    tmpRange = rng(i, 1)
    tmpRange.Formula = "Hoge" & CStr(i)
    System.Runtime.InteropServices.Marshal.ReleaseComObject(tmpRange)
    tmpRange = Nothing
  Next

  System.Runtime.InteropServices.Marshal.ReleaseComObject(rng)
  rng = Nothing

  略

>---------------------------------------------------------------------

のようにしたらみごと,
GC.Collect() を呼ばないでも
ReleaseCOMOjbectだけで,インスタンスはきれいに消えました。

なので,

  worksheet.Cells( row , col ).Format = "Hoge"

は,

  rng1 = worksheet.Cells
  rng2 = rng1( row , col )
  rng2.Format = "Hoge"

のように分割しないといけないということのようです。

つまり,一度rangeでうけていても,
あるセルを参照するたびに,rangeでうけて,
そして,それを毎回 ReleaseCOMObject( range ) する必要があるということのようです。

ReleaseCOMObjectだけで,消えてくれたので,うれしいのですが,
死ぬほど面倒なので,GC.Collect() が許される状況ならば,
VB/VBA上のコードのように,今までどおり,obj = Nothing するだけにしておいて,
最後に GC.Collect() でまるっとRCWを開放した方が
やっぱり楽かなという感じです...

[ メッセージ編集済み 編集者: 稍丼 編集日時 2004-08-19 03:26 ]

[ メッセージ編集済み 編集者: 稍丼 編集日時 2004-08-19 03:27 ]

[ メッセージ編集済み 編集者: 稍丼 編集日時 2004-08-19 03:28 ]

[ メッセージ編集済み 編集者: 稍丼 編集日時 2004-08-19 03:29 ]
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2004-08-19 09:01
稍丼さん

 詳細にありがとうございます。

 先にリンクをしています、AI-Lightの投稿では、『GC.Collect()をしてもダメだった』とあります。この方の最初の投稿で、.NET側のプログラムで保持している変数は、すべてNothing代入をしているので、参照が残っているという状況ではない、と思います。

 自分がやってみた感想としては、GC.Collect()をすることは絶対で、ReleaseCOMObjectを併用しておく方が安全、ってところでしょうか。ExcelApplicationなどは、CloseしてQuitしてReleaseしておかなければ落ちてくれないように、記憶しています。
yayadon
常連さん
会議室デビュー日: 2003/07/23
投稿数: 41
投稿日時: 2004-08-19 18:05
> 自分がやってみた感想としては、GC.Collect()をすることは絶対で、
> ReleaseCOMObjectを併用しておく方が安全、ってところでしょうか。

COM Interop( 特に,RCW(Runtime Callable Wrapper)) の実装?が
当てにならない(flawed)という記述を,ひどりさんのレスだけでなく,
調べてみると,海外のMVPの方のブログでも見かけました。
なので,今のところは,
安全な方へ転ぶように書くのが無難なんでしょうね...

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