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

No35245 の記事


■35245 / )  Re[2]: Excel Com オブジェクトの増殖
□投稿者/ たこ 一般人(16回)-(2022/11/27(Sun) 10:59:37)
  • アイコン魔界の仮面弁士様
    いつも素早いご回答ありがとうございます。


    > たとえば、使用例のところで
    >>    ExEx1 = New ExcelEx()
    >>    ExEx1.WorkBook = path & "Test.xlsx"
    >>    ExEx1.xlsApplication.WindowState = Excel.XlWindowState.xlMaximized
    >>    ExEx1.Sheets(1)
    >>    ExEx1.WorkBook = "Close"
    > と書かれていますが、Sheets メソッドの実装が不明瞭ですし。


    Sheetsの実装はこうなっております。
    ----------------------------------------------------------------------------------------------------
      Public Sub Sheets(strSheetName As String)
        xlsWorkSheet = xlsApplication.Sheets(strSheetName)
        If SheetVisible Then xlsWorkSheet.Select()
      End Sub
    ----------------------------------------------------------------------------------------------------


    >>Friend xlsApplication As Excel.Application = Nothing
    > これらの COM オブジェクトを非 Private として公開されていますが、
    > Friend で公開するにしても、ReadOnly な Property にしておくべきかと。
    >
    > ただし ReadOnly だとしても、ExcelEx 外から Open や Close 等、あるいは Cells 等への
    > アクセスは無制限に可能となってしまいますので、COM オブジェクトを直接公開すると、
    > ExcelEx だけでは、参照と解放の管理をまかないきれなくなることがあります。

    なるほどです…


    > 解放まですべてクラス側で管理するなら、委譲している Excel のオブジェクトを
    > 直接公開するのではなく、必要な操作のみを再公開するようにしたうえで、
    > IDisposable パターンも追加実装しておくのが安全です。実装量は爆発的に増えますけれどね。
    > https://learn.microsoft.com/ja-jp/dotnet/standard/garbage-collection/implementing-dispose
    > https://ufcpp.net/study/csharp/rm_disposable.html
    >
    > 逆に、そこまでの実装コストをかけられないというのであれば、
    > ヘルパーライブラリとしての実装に留めるという手もあろうかと。

    明記はしませんでしたが、IDisposableも実装しています。(実装パターンそのまま)
    ----------------------------------------------------------------------------------------------------
      Private disposedValue As Boolean
      Protected Overridable Sub Dispose(disposing As Boolean)
        If Not disposedValue Then
          If disposing Then
            ' TODO: マネージド状態を破棄します (マネージド オブジェクト)
            WorkBook_Close()
          End If

          ' TODO: アンマネージド リソース (アンマネージド オブジェクト) を解放し、ファイナライザーをオーバーライドします
          ' TODO: 大きなフィールドを null に設定します
          disposedValue = True
        End If
      End Sub

      〜〜〜

    ----------------------------------------------------------------------------------------------------


    >>Private _WorkBookIsNew As Boolean = Nothing
    > = False と書いた方が素直ではありませんか?

    確かにそうですね(^^ゞ


    >>Public Property WorkBook As String
    > 今回の COM 解放の問題からは外れますが、こういったものはプロパティではなく、
    > String 引数を持ったメソッドとして実装する方が望ましいです。
    >
    > クラス設計的には、プロパティは "状態" を表すものであり、
    > "動作" を行うことを目的としたプロパティを実装すべきではありません。
    > "動作" を目的とするのはメソッドの役目ですよね。

    確かに…(^^ゞ



    >>Private Sub WorkBook_Open(Optional ByVal strFileName As String = Nothing)
    >>    'Excel アプリケーション起動
    >>    xlsApplication = New Excel.Application
    > WorkBook_Open が 2 回呼ばれると、xlsApplication 変数が
    > 以前に編集していたインスタンスを破棄することなく、
    > 新しい Application インスタンスを生成することになりそうですが、
    > 何らかの再入防止策が組み込まれていますか?

    2回呼ばない様にしています…が…(^^ゞ


    > インスタンス管理を考えると、Application の起動は
    > ExcelEx のコンストラクタで行った方が良いと思います。

    なるほどそうですね…(^^ゞ


    >>    MRComObject(xlsRange, True)
    > ExcelEx の他の部分の処理がどうなっているのかにもよりますが、
    > Range オブジェクトは複数使われることも多いので、
    > 自分ならコレクション管理するかな…?

    なるほど思いつきもしなかったです…
    Cellオブジェクトもコレクション化した方が良さそうですね…


    >>    If Not xlsWorkbook Is Nothing Then xlsWorkbook.Close()
    > 省略された部分のコードによって、この時点で Nothing になっていることがあるということですか?
    >
    > 提示された範囲のコードを見るだけでは、ここで Nothing になっているケースとは、外部から
    >  ExEx1.xlsWorkbook = Nothing
    > とされた場合か、あるいは WorkBook_Open がまだ呼ばれていない時ぐらいしかなさそうですが…。
    > (または、存在しないファイル名を指定するなどして、WorkBook_Open が失敗した場合とか)


    閉じないのでこんなコードを書いて実験していました…
    ----------------------------------------------------------------------------------------------------
      ExEx2.WorkBook = "Close"
      ExEx2.Dispose()
      ExEx2 = Nothing
    ----------------------------------------------------------------------------------------------------

    > あと、xlsWorkbook.Saved が False の時に Close を呼び出すと、
    > DisplayAlerts が True に戻されていた場合、保存確認のダイアログが表示されることになります。
    > そして保存確認でキャンセルされた場合、実際には Close されません。
    >
    > もしも終了時の保存が常に不要な場合は、Close 前に Saved プロパティを True にしておくか、
    > もしくは Close メソッドの SaveChanges 引数に False を指定するようにします。

    一番の問題はここそうです!
    開いたExcelファイルにSheetの初期化の為にシートごとコピーしてくるVBAコードがあります。

    (以下、Excel側VBAコード)
    ----------------------------------------------------------------------------------------------------
    Private Sub Workbook_Open()
      Dim row As Integer
      Dim work As String
      Dim No As String
      Dim FolderName As String
      Dim i As Integer

      On Error Resume Next

      If ActiveWorkbook.Name = "御見積書_原紙_1.xlsm" Or ActiveWorkbook.Name = "御見積書_原紙_2.xlsm" Then
        Check = True
        If Sheets("【設計用】見積書").Range("H1").Value <> "No." Then
          Sheets("【原紙】【設計用】材料一覧表").Select
          Cells.Select
          Selection.Copy
          Sheets("【設計用】材料一覧表").Select
          Range("A1").Select
          ActiveSheet.Paste
          Sheets("【原紙】【設計用】見積書").Select
          Cells.Select
          Selection.Copy
          Sheets("【設計用】見積書").Select
          Range("A1").Select
          ActiveSheet.Paste
        End If

        NewNo
        Check = False
      End If

      UserForm1.Show (False)

      Range("A5").Select
    End Sub
    ----------------------------------------------------------------------------------------------------

    上記を踏まえ、作り直します。
    ありがとうございます。
違反を報告
返信 削除キー/


Mode/  Pass/


- Child Tree -