DOBON.NETプログラミング道掲示板

■35255 / ResNo.10)  Re[6]: Excel Com オブジェクトの増殖
  
□投稿者/ 魔界の仮面弁士 大御所(1494回)-(2022/11/28(Mon) 06:16:23)
  • アイコンNo35251に返信(たこさんの記事)
    > 問題は無い様に思えるのですが…
    Friend Sub WorkSheet_Select には、もう一つ問題点がありそうです。

    > If Not check Then
    >   _xlsWorkSheet = CType(xlsSheets.Add(), Excel.Worksheet)
    >   xlsWorkSheet.Name = strSheetName
    > Else
    >   _xlsWorkSheet = CType(xlsSheets(strSheetName), Excel.Worksheet)
    > End If
    これだと、「以前に _xlsWorkSheet が参照していた COM オブジェクト」が
    解放されなくなってしまいます。


    No35247 において、解説の最後に
    >> Dim y As Excel.Workbooks = x.Workbooks
    >> Dim z As Excel.Workbooks = x.Workbooks
    という実験コードを書いていますが、この場合、VBA や VBS とは異なり、
    .NET においては y と z が別インスタンスとなることに注意が必要です。

    この場合、COM の参照カウントは y と z それぞれに対して減じねばなりません。
    y と z の両方を RelaseComObject した場合と、どちらか一方しか
    解放しなかった場合とで、Excel の残存性を確認してみてください。

    仮に同一インスタンスを返す仕様であったとしたら、
     Trace.WriteLine(Marshal.ReleaseComObject(z)) 'ア
     Trace.WriteLine(Marshal.ReleaseComObject(y)) 'イ
    において、ReleaseComObject の戻り値から得られる残存参照カウント数が
    「イ = ア - 1」の関係となるはずですが、実際には別インスタンスであるため
    「イ = ア」な戻り値で返されていると思います。
違反を報告
引用返信 削除キー/
■35256 / ResNo.11)  Re[6]: Excel Com オブジェクトの増殖
□投稿者/ radian 一般人(1回)-(2022/11/28(Mon) 11:57:41)
  • アイコンちなみに、OfficeのCOMの自動管理をやってくれるライブラリもあります。
    解放漏れを特定出来ない&コードが煩雑になっている場合、
    導入を検討してみるのも手かもしれません。

    https://www.nuget.org/packages/NetOfficeFw.Excel/
    https://github.com/NetOfficeFw/NetOffice
違反を報告
引用返信 削除キー/
■35257 / ResNo.12)  Re[7]: Excel Com オブジェクトの増殖
□投稿者/ たこ 一般人(19回)-(2022/11/28(Mon) 22:38:47)
  • アイコンNo35252に返信(魔界の仮面弁士さんの記事)
    No35253に返信(魔界の仮面弁士さんの記事)
    No35255に返信(魔界の仮面弁士さんの記事)
    No35256に返信(radianさんの記事)

    魔界の仮面弁士様、radian様、返信ありがとうございます。

    返信が前後しますが、まずはradian様ご紹介ありがとうございます。
    どうしようも無くなった時は検討させて頂きます(^^ゞ

    余談にはなりますが…
    元々私、プログラミングは全部独学でして、スキルアップの為に今回の質問をさせて頂いています(^^ゞ
    (いつもはどうしようも無くなって質問させて頂いていますが…(^^ゞ)
    まだしっかりと解ってる訳では無いですが、VB6.0からオブジェクトの概念を解る様になるまでは苦労しました^^;

    最終的には会社で5年分くらいExcelで見積書が溜まっていますので、これをデータベース化するのが目的です。。。
    最初はExcel VBAで5年分の見積書の内容をまとめようかと思っていましたが、クラスの概念を覚えてしまうとクラスを使った方が楽なので(^^ゞ
    VB6.0やVBAにもクラスはありますが、使い方が良く解りません…
    …と言う訳でVB.NETで…(^^ゞ

    お気持ちは有難く頂戴致しました。
    ありがとうございます。



    今回、いろいろ調べてやっとExcelのオブジェクトモデルを発見出来ました。
    https://learn.microsoft.com/ja-jp/office/vba/api/overview/excel/object-model
    VBAと同じなのですね…
    当たり前と言えば当たり前ですが…

    Option Strict Onも調べました…
    https://atmarkit.itmedia.co.jp/fdotnet/vb6tonet/vb6tonet26/vb6tonet26_03.html

    DirectCast、CTypeの違いも調べました…
    http://vb.navi-ch.net/2015/07/18/post-118/
    https://learn.microsoft.com/ja-jp/dotnet/visual-basic/language-reference/operators/directcast-operator



    さて、本題に戻ります。

    No35252に返信(魔界の仮面弁士さんの記事)
    >>  Private _xlsApplication As Excel.Application = Nothing
    >>  Friend ReadOnly Property xlsApplication As Excel.Application
    >>    Get
    >>      Return _xlsApplication
    >>    End Get
    >>  End Property
    > これではあまり意味が無いと思いますよ。結局のところ、
    >  Friend ReadOnly xlsApplication As Excel.Application
    > な読み取り専用フィールドと、さほど変わらないように見えます。
    >
    >
    > COM オブジェクトを直接公開してしまうと、ExcelEx の外部で
    >  ExEx1.xlsApplication.Workbooks.Add()
    > などと書かれてしまえば、COM オブジェクトの解放漏れに繋がります。
    >
    > IDisposable としてカプセル化するのであれば、Excel の COM オブジェクトは
    > 外部からは直接操作できないようにして、すべてクラス内に隠蔽します。
    > 戻り値と返すのも COM オブジェクトではなく、.NET のマネージオブジェクトにします。

    全てをクラス内でやってしまう…と言う考え方で良いのでしょうか…
    例えば…
    -----------------------------------------------------------------
    Private _xlsApplication as Excel.Application = Nothing

    Private _ExcelVisible As Boolean = True
    Public Property ExcelVisible As Boolean
      Set(Value as Boolean)
        _ExcelVisible = Value
        If value then
          _xlsApplication.Visible = True
        Else
          _xlsApplication.Visible = False
        End If
      End Set
      Get
        Return _ExcelVisible
      End Get
    End Property
    -----------------------------------------------------------------
    …の様な感じで…

    >>  _xlsWorkSheet = xlsApplication.Sheets.Add()
    > この処理には、問題点が 2 つあります。
    >
    > 1 つは「.」による COM オブジェクトの連続呼び出しであり、
    > Sheets プロパティから返されるコレクションの解放が漏れています。
    Excel.Applicationオブジェクト(COMモデル)の中の、Application.Sheetsプロパティ…あれ?w
    https://learn.microsoft.com/ja-jp/office/vba/api/excel.application.sheets
    COMオブジェクトの場合は(COMオブジェクトの)プロパティの解放も必要と理解して良いのでしょうか…

    >  Dim obj1 = 対象ワークブック.Sheets 'これは Sheets 型        …と言う事は Dim obj1 As Excel.Sheets
    >  Dim obj2 = obj1(strSheetName) 'これは Object 型となることに注意   …と言う事は Dim obj2 As Object
    …で合ってますかね^^?
    ここの理解はまだ追い付いていません(>_<)

    > ただし厳密にいえば、xlsSheets によって列挙されるシートが Excel.Worksheet である保証はありません。
    > Excel.Chart や Excel.DialogSheet が列挙される可能性もあることは頭の片隅に置いといてください。

    …と言う事は…
    -------------------------------------------------------------------------------------
    Friend Sub WorkSheet_Select(strSheetName As String)
      Dim check As Boolean = False
      For x As Integer = 1 to _xlsSheets.Count
        If _xlsSheets(x) Is Excel.Worksheet then                '← 怒られました^^;
          If CType(_xlsSheets (x), Excel.Worksheet).Name = strSheetName Then
            check = True
            Exit For
          End If
        End If
      Next
      If Not check Then
        _xlsWorkSheet = CType(_xlsSheets.Add(), Excel.Worksheet)
        _xlsWorkSheet.Name = strSheetName
      Else
        _xlsWorkSheet = CType(_xlsSheets(strSheetName), Excel.Worksheet)
      End If
      If SheetVisible Then _xlsWorkSheet.Select()
    End Sub
    -------------------------------------------------------------------------------------

    下の方に書いてありました^^;
    >   Dim ws = DirectCast(xlsSheets(n), Excel.Worksheet)
    これだと『WorkSheet』以外はNothingになるのでしょうか…

    (DirectCastは明示的な型変換と書いてありました…
    http://vb.navi-ch.net/2015/07/18/post-118/
    -------------------------------------------------------------------------------------
    サンプルソース
    Dim a As Object = 3.14
    Dim b As Integer = DirectCast(a, Integer)
    MsgBox(b)
    End Sub
    End Class
    実行結果
    メッセージーボックスも表示されません。
    -------------------------------------------------------------------------------------

    頭大パニック中…
違反を報告
引用返信 削除キー/
■35258 / ResNo.13)  Re[8]: Excel Com オブジェクトの増殖
□投稿者/ たこ 一般人(20回)-(2022/11/28(Mon) 23:06:02)
  • アイコンNo35257に返信(たこさんの記事)

    ちょっとした実験をしてみました。
    --------------------------------------------------------------------
    Dim w As Excel.Worksheets
    Dim s = w.Item(1)     '←WorkSheetオブジェクトのItemなので、当然WorkSheet型だと思っている
    Dim n = CType(s, Excel.Worksheets).name    '←怒られる…
    --------------------------------------------------------------------

    Dim s As Excel.Worksheets = w.Item(1)   '←やっぱり怒られる…
                         'Option Strict On では'Object'から'Worksheets'への暗黙的な変換は許可されていません。
                         'ReadOnly Property Excel.Wroksheets.Item(Index As Object) As Object

    Dim s As Object = w.Item(1)        'これが正解の様だ…
                         'ここは戻り値がObjectなので理解出来る。

    Dim n = CType(s, Object).name       '←怒られる…
                         'Option Strict On では、遅延バインディングを使用できません。
    一体何の型にキャストすればシート名が取得できるのだろう…

    >『.』の連続」が見えにくくなる
    .を分解するのも苦労します(>_<)

    あ…そう言えばと思い。。。

    Dim w As Excel.Worksheets
    Dim s As Object = w.Item(1)
    Dim sh As Excel.Worksheet = CType(s, Excel.Worksheet)
    Dim n As String = sh.Name

    怒られなくなりました!

    この場合、MRComObjectをしなければならないのは…
    w、s、shで合っていますでしょうか^^?


違反を報告
引用返信 削除キー/

<前のレス10件

スレッド内ページ移動 / << 0 | 1 >>

このスレッドに書きこむ

Mode/  Pass/


- Child Tree -