tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルレイトバインドするとExcelのFindNextオブジェクトが開放できなくなります
記事No10181
投稿日: 2010/07/14(Wed) 10:38
投稿者camputer
 こんにちは、いつも勉強させていただいております、camputerと申します。
どうぞよろしくお願いいたします。
 VB歴2年弱、プログラム歴10年程度
 環境:VB2008+XP+Excel2002でございます。

 Excelファイル操作用のコードをレイトバインドしてみたのですが、一箇所だけ
オブジェクト解放ができなくなってしまいました。(他の2000行程度につきましては
うまく解放できたのですが。。)もし怪しそうな箇所などございましたら、
是非ともアドバイスいただきたくお願いいたします。

  xlCells = xlSheet.Cells
  'Dim myRng As Excel.Range = xlCells.Find(OldStr)←レイトバインド前
  Dim myRng As Object = xlCells.Find(OldStr)
  If Not myRng Is Nothing Then
    Dim 最初セル As String = myRng.Address
    Do
      'Dim myFindNext As Excel.Range = xlCells.FindNext(myRng)←レイトバインド前
      Dim myFindNext As Object = xlCells.FindNext(myRng) '★
      MRComObject(myRng)'★
      myRng = myFindNext'★
      If myRng Is Nothing Then Exit Do
    Loop Until myRng.Address = 最初セル
    MRComObject(myRng)
  End If
  MRComObject(xlCells)
  '★マークをつけた3行を消すと綺麗に解放されます。

 以前もオブジェクト解放について沢山のアドバイスをいただけたので、上のコードも
レイトバインド前は綺麗に解放できておりました。
もしかすると、「Excel.Range」→「Object」としたことで型がはっきりせず
解放しにくくなる、という事でもあるのでございましょうか???
 私の考えといたしましては、
  1回目のmyRngに入ったオブジェクト
      →Loop1回目でMRComObject(myRng)
  1回目以降のmyFindNextに入ったオブジェクト
      →myRng = myFindNextでmyRngに入れておいて、次のLoopでMRComObject(myRng)
  最後のmyFindNextに入ったオブジェクト
      →   〃        〃        Loop直後でMRComObject(myRng)
ですべて解放できるのでは(?)などと思うのでございますが。。。

 是非ともアドバイスいただきたくどうぞよろしくお願いいたします。

[ツリー表示へ]
タイトルRe: レイトバインドするとExcelのFindNextオブジェクトが開放できなくなります
記事No10182
投稿日: 2010/07/14(Wed) 10:56
投稿者魔界の仮面弁士
# この場合は、開放ではなく解放かな…。

> Excelファイル操作用のコードをレイトバインドしてみたのですが
同様の話は、FindNext(Range) メソッド以外でも報告されています。

http://bbs.wankuma.com/index.cgi?mode=al2&namber=29001&KLOG=52  の No29016
http://hanatyan.sakura.ne.jp/dotnet/Excel08.htm  の4.


> 'Dim myFindNext As Excel.Range = xlCells.FindNext(myRng)←レイトバインド前
> Dim myFindNext As Object = xlCells.FindNext(myRng) '★

アーリーバインドの場合は、変数 myRng の型が Range でしたが、
レイトバインドの場合は Object 型ですよね。
このため、FindNext の処理では内部的に型チェックと Object → Range への
型変換が発生し、恐らくこの段階で参照カウントが増加してしまうのでしょう。

myRng を ReleaseComObject する際に、その戻り値を確認してみてください。
正しく解放されていれば 0 が返されるはずですが、0 以外になっているようなら、
この部分は ReleaseComObject ではなく FinalReleaseComObject で解放した方が安全です。

ちなみにこのようなケースでは、.NET 1.x 当時は、ReleaseComObject を 0 になるまで
繰り返すパターンで解放していたのですが、2.0 以降では、FinalReleaseComObject を
使う事が推奨されています。


もしくは作業用のアプリケーションドメインを作って、最後にアンロードするという手も。

[ツリー表示へ]
タイトルRe^2: レイトバインドするとExcelのFindNextオブジェクトが開放できなくなります
記事No10183
投稿日: 2010/07/14(Wed) 11:21
投稿者camputer
 魔界の仮面弁士 博士(`ー´)ゞ

 どうもありがとうございます!!m(_ _)m

> アーリーバインドの場合は、変数 myRng の型が Range でしたが、
> レイトバインドの場合は Object 型ですよね。
> このため、FindNext の処理では内部的に型チェックと Object → Range への
> 型変換が発生し、恐らくこの段階で参照カウントが増加してしまうのでしょう。
 なんとその様な現象が起こるのでございますか。。。全然存じませんでした。
参考リンクをどうもありがとうございます。リンク先で
  > むしろ、型変換にともなって参照カウントが増加するなどの理由から、
  > レイトバインドの方が、解放が難しくなる事さえありえます。
と書いてくださっていますね^^(早くこういう記事を自分で探してこれるようにならな略orz)

> myRng を ReleaseComObject する際に、その戻り値を確認してみてください。
> 正しく解放されていれば 0 が返されるはずですが、0 以外になっているようなら、
> この部分は ReleaseComObject ではなく FinalReleaseComObject で解放した方が安全です。
 はい!今からやり方から調べて戻り値を調査してまいります。

> ちなみにこのようなケースでは、.NET 1.x 当時は、ReleaseComObject を 0 になるまで
> 繰り返すパターンで解放していたのですが、2.0 以降では、FinalReleaseComObject を
> 使う事が推奨されています。
 どうもありがとうございます。こういう、長い間VBを触っていないと
得にくいお話は理解が深まりますしタメになります。

> この場合は、開放ではなく解放
orz いつもご指摘どうもありがとうございます。タイトル以外修正いたしました。
(言い訳がましいでございますが、今タイプしておりますPCがIMEの学習機能設定を
ほぼ0にされておりまして、何度打っても「解放」を第一候補にしてくれません。。。
注意いたします^^;)

 再度お礼&ご報告させていただきます!どうもありがとうございました!!

[ツリー表示へ]
タイトル【解決】Re^3: レイトバインドするとExcelのFindNextオブジェクトが開放できなくなります
記事No10184
投稿日: 2010/07/14(Wed) 19:29
投稿者camputer

 どうもありがとうございました!!m(_ _)m

一瞬で解決いたしました!3日も悩んだのに orz ・・・

オブジェクト解放につきましてはほとんど手中に収めたつもりで
おりましたが、全然まだまだですねー。。VB奥深し ̄_ ̄;

  If Not myRng Is Nothing Then
    Dim 最初セル As String = myRng.Address
    Do
      Dim myFindNext As Object = xlCells.FindNext(myRng)
      System.Runtime.InteropServices.Marshal.FinalReleaseComObject(myRng)
      myRng = myFindNext
      If myRng Is Nothing Then Exit Do
    Loop Until myRng.Address = 最初セル
    MRComObject(myRng)
  End If
といたしました。Loop後の解放につきましてはそのままでございます。
こちらもFinalReleaseComObjectしてしまうとエラーが出るようでしたので、
魔界の仮面弁士様がおっしゃられた通り、
>このため、FindNext の処理では内部的に型チェックと Object → Range への
>型変換が発生し、恐らくこの段階で参照カウントが増加してしまうのでしょう。
が原因のようでございます。

 この度はどうもありがとうございました!!ようやく次のコードを
考えて楽しむことができます^^(早速また別の新たな問題が。。。φ(-_-;  )
 ご親切にどうもありがとうございました!!m(_ _)m

[ツリー表示へ]
タイトルRe: 【解決】Re^3: レイトバインドするとExcelのFindNextオブジェクトが開放できなくなります
記事No10185
投稿日: 2010/07/15(Thu) 01:44
投稿者魔界の仮面弁士
> Loop後の解放につきましてはそのままでございます。
> こちらもFinalReleaseComObjectしてしまうとエラーが出るようでしたので、
何のエラーか書かれていませんが、どのような物ですか?

まだ使用される可能性があるオブジェクトを解放した場合、以後、その変数を使う時に
エラーが発生することになりますが、そうではなく解放処理時にエラーになるのなら、
「Nothing を渡した」か、「COM オブジェクト以外の物を渡した」のいずれかでしょう。

たとえば、Loop 処理が
>       If myRng Is Nothing Then Exit Do
で抜けた場合、myRng が Nothing になっている可能性がありますから、
その場合は (Final)ReleaseComObject してはいけませんね。


>     MRComObject(myRng)
どの実装パターンの MRComObject か分かりませんが、No.10182 の URL にある物なら、
MRComObject(myRng, True) で、FinalReleaseComObject が呼ばれます。

[ツリー表示へ]
タイトルRe^2: 【解決】Re^3: レイトバインドするとExcelのFindNextオブジェクトが開放できなくなります
記事No10186
投稿日: 2010/07/15(Thu) 08:10
投稿者camputer
> 何のエラーか書かれていませんが、どのような物ですか?
 魔界の仮面弁士様
おはようございます!!夜遅い時間にも関わらずコメントいただき
どうもありがとうございます。お返事が遅くなり申し訳ございませんm(_ _)m
今確認できる状況でございますゆえ、調査してまいります。
しばしお待ちくださいませ。。。


#==【追記】====================================================

 調べてまいりました。ご推察なさられたとおり、    
myRng = Nothingの時のエラーで間違いなさそうでございます。    
【エラー表示内容】    
    ArgumentNullExceptionはハンドルされませんでした。
    値を Null にすることはできません。 パラメータ名: o
になります。
    
MRComObjectにつきましては、以下になります。最後にobjCom = Nothingしております。    
(この行につきましては殆ど意味が無いのかな?などと考えながらも
       必ずしも無意味では無さそうなので入れてみました。)    

    Public Shared Sub MRComObject(Of T As Class)(ByRef objCom As T, Optional ByVal force As Boolean = False)    
        If objCom Is Nothing Then Return    
        Try    
            If System.Runtime.InteropServices.Marshal.IsComObject(objCom) Then    
                If force Then    
                   System.Runtime.InteropServices.Marshal.FinalReleaseComObject(objCom)    
                Else    
                    Dim count As Integer = System.Runtime.InteropServices.Marshal.ReleaseComObject(objCom)
                    'MsgBox(count)
                    'Debug.WriteLine(count)
                End If
            End If
        Finally
            objCom = Nothing
        End Try
    End Sub

 If force Then 時にFinal〜しておりますが、今回の現象においては上記コードではCom〜が一回も解放できませんでした。
前コメント記載コードのように、毎回Final〜すると現時点で100%解放できております。



 昨日アドバイスいただけてから、すべてのComオブジェクト解放時に「毎回」
FinalReleaseComObjectを使用してしまう、という事はNGなのかな???などと考えております。
毎回、「完璧に」解放してから次に進むべきではないのかな??などと思う次第なのでございますが。。。
例えばMRComObjectを次のようにしてしまうとか(?)。。。

    Public Shared Sub MRComObject(Of T As Class)(ByRef objCom As T, Optional ByVal force As Boolean = False)
        If objCom Is Nothing Then Return
        Try
            If System.Runtime.InteropServices.Marshal.IsComObject(objCom) Then
                System.Runtime.InteropServices.Marshal.FinalReleaseComObject(objCom)
            End If
        Finally
            objCom = Nothing
        End Try
    End Sub

もしくは

    Public Shared Sub MRComObject(Of T As Class)(ByRef objCom As T, Optional ByVal force As Boolean = False)
        If objCom Is Nothing Then Return
        Try
            If System.Runtime.InteropServices.Marshal.IsComObject(objCom) Then
                Do
                    i = System.Runtime.InteropServices.Marshal.ReleaseComObject(objCom)
                Loop Until i <= 0
            End If
        Finally
            objCom = Nothing
        End Try
    End Sub

 自分でもう少し勉強して参ります^^ この度は誠にありがとうございました!!!m(_ _)m

[ツリー表示へ]