複数Excelファイルオープンにおける、オブジェクト解放について

解決


はる  2010-05-11 23:13:22  No: 146855

VB2008 初心者のはるです。初投稿させて頂きます。

環境は以下の通りです。

     Excel:Excel2007
    VB.net:VB2008express SP1
        OS:WinXPsp3

以下はExcelのオープンから解放までのコーディングを抜粋しております。

    Private Sub btnSelect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSelect.Click
        Dim exlExcelApp As New Excel.Application
        Dim exlBooks As Excel.Workbooks = exlExcelApp.Workbooks
        Dim exlExcelBook As Excel.Workbook
        Dim exlExcelSheet As Excel.Worksheet

        For Each intFileList In Me.lstTargetFile.SelectedIndices
            'lstTargetFileリストボックスで選択されているExcelファイル分処理
            exlExcelBook = exlExcelApp.Workbooks.Open(strDirPath & "\" & Me.lstTargetFile.Items(intFileList))
            Dim exlSheets As Excel.Sheets = exlExcelBook.Worksheets
            exlExcelApp.Visible = True
            exlExcelApp.DisplayAlerts = False

            For i = 1 To exlExcelBook.Sheets.Count
                exlExcelSheet = exlExcelBook.Sheets(i)
                Dim exlCells As Excel.Range = exlExcelSheet.Cells
                For iRows = intEmployeeDataFromRows To intEmployeeDataToRows
                    '全シートのexlCellsに対する参照処理
                Next
                MRComObject(exlCells)
                exlCells = Nothing
                MRComObject(exlExcelSheet)
                exlExcelSheet = Nothing
            Next
            If Not exlExcelBook Is Nothing Then
                Try
                    exlExcelBook.Close(False)
                Finally
                    MRComObject(exlExcelBook)
                End Try
            End If
            exlExcelBook = Nothing
            MRComObject(exlSheets)
            exlSheets = Nothing
            MRComObject(exlBooks)
            exlBooks = Nothing
            exlExcelApp.Quit()

            intFileCnt = intFileCnt + 1
        Next
        
        'Excelの結果に応じて、画面のListViewの編集処理

        If Not exlExcelApp Is Nothing Then
            Try
                exlExcelApp.Quit()
            Finally
                MRComObject(exlExcelApp)
            End Try
        End If

        exlExcelApp = Nothing

    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
        End If
        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)
                End If
            End If
        Finally
            objCom = Nothing
        End Try
    End Sub

[質問]
  上記の処理で数回処理した場合は閉じるボタン等でExcel.EXEがプロセスから消えてくれます。
  しかし、10回起動を越したぐらいからかプロセスにExcel.EXEが5個以上残ってしまった時、
  終了してもプロセスから消えてくれません。
  たぶん、複数のCOMがオープンの度にExcel.EXEを起動してて、残骸がどんどん増えてるようですが・・。
  いろいろ試してみたのですが、どこかおかしいのかわからない状態に陥ってしまったので、
  ご教授をお願いしたく投稿させて頂きました。

  宜しくお願い致します。


魔界の仮面弁士  2010-05-12 01:17:53  No: 146856

>   exlExcelBook = exlExcelApp.Workbooks.Open(strDirPath & "\" & Me.lstTargetFile.Items(intFileList))
exlExcelApp.Workbooks.Open ではなく、
exlBooks.Open を使うべきかと。せっかく変数に取ってあるのですし。

>   exlExcelApp.Visible = True
>   exlExcelApp.DisplayAlerts = False
For Each ループの内側で、これらを毎回実行する必要は無い気がします。

>   For i = 1 To exlExcelBook.Sheets.Count
exlExcelBook.Sheets.Count ではなく、
exlSheets.Count を使うべきかと。

>     exlExcelSheet = exlExcelBook.Sheets(i)
exlExcelBook.Sheets(i) ではなく、
exlSheets(i) を使うべきかと。

>     Dim exlCells As Excel.Range = exlExcelSheet.Cells
>     For iRows = intEmployeeDataFromRows To intEmployeeDataToRows
>       '全シートのexlCellsに対する参照処理
>     Next
この中で、ReleaseComObject が行われていない場所はありませんか?

>     MRComObject(exlCells)
>     exlCells = Nothing
変数への Nothing 代入は、既に MRComObject 内で行われているようですが、
それだけでは不足だったのでしょうか?

>     If Not exlExcelBook Is Nothing Then
この時点で、exlExcelBook が Nothing になる事は無いように思えます。

>     If Not exlExcelBook Is Nothing Then
>         Try
>             exlExcelBook.Close(False)
>         Finally
>             MRComObject(exlExcelBook)
>         End Try
>     End If
>     exlExcelBook = Nothing
>     MRComObject(exlSheets)
>     exlSheets = Nothing
>     MRComObject(exlBooks)
>     exlBooks = Nothing
Range → Sheet → Workbook → Sheets → Workbooks の順に解放していますが、
念のため、下位のオブジェクトから行っていった方が良いと思います。
Range → Sheet → Sheets → Workbook → Workbooks の順に書き換えてみてください。

>     exlExcelApp.Quit()
>     intFileCnt = intFileCnt + 1
>   Next
>   'Excelの結果に応じて、画面のListViewの編集処理
>   If Not exlExcelApp Is Nothing Then
>     Try
>       exlExcelApp.Quit()

ループ内の exlExcelApp.Quit は問題があるように思えます。

Quit するなら、ReleaseComObject もセットで行うべきですし、
Quit した後で、2回目以降に exlExcelApp を使い続けるべきではありません。

>   If Not exlExcelApp Is Nothing Then
>     Try
>       exlExcelApp.Quit()
>     Finally
>       MRComObject(exlExcelApp)
>     End Try
>   End If
>   exlExcelApp = Nothing
最後の Nothing 代入は、何を意図しているのでしょうか?

「If Not exlExcelApp Is Nothing Then」の内側では、
MRComObject によって、既に Nothing になっているようですし、
If ブロック内を処理していないなら、既に Nothing になっているハズですよね。

> Public Shared Sub MRComObject(Of T As Class)(ByRef objCom As T, Optional ByVal force As Boolean = False)
どこかで見たコード…。


はる  2010-05-12 02:03:26  No: 146857

魔界の仮面弁士さん

オブジェクトの使い方に慣れていなくて、単純な指摘までして頂き、ありがとうございます。
インラインにてコメントします。
>>   exlExcelBook = exlExcelApp.Workbooks.Open(strDirPath & "\" &  Me.lstTargetFile.Items(intFileList))
> exlExcelApp.Workbooks.Open ではなく、
> exlBooks.Open を使うべきかと。せっかく変数に取ってあるのですし。
おっしゃるとおりですね。修正しました。

>>   exlExcelApp.Visible = True
>>   exlExcelApp.DisplayAlerts = False
> For Each ループの内側で、これらを毎回実行する必要は無い気がします。
了解です。オブジェクト定義後に1度だけ設定するように修正しました。

>>   For i = 1 To exlExcelBook.Sheets.Count
> exlExcelBook.Sheets.Count ではなく、
> exlSheets.Count を使うべきかと。
了解です。修正しました。

>>     exlExcelSheet = exlExcelBook.Sheets(i)
> exlExcelBook.Sheets(i) ではなく、
> exlSheets(i) を使うべきかと。
了解です。修正しました。

>>     Dim exlCells As Excel.Range = exlExcelSheet.Cells
>>     For iRows = intEmployeeDataFromRows To intEmployeeDataToRows
>>       '全シートのexlCellsに対する参照処理
>>     Next
> この中で、ReleaseComObject が行われていない場所はありませんか?
exlCellsでセルのvalueを参照して値の抽出処理を行っているだけでReleaseComObjectは行っておりません。

>>     MRComObject(exlCells)
>>     exlCells = Nothing
>変数への Nothing 代入は、既に MRComObject 内で行われているようですが、
>それだけでは不足だったのでしょうか?
不足ではないですが、ウォッチリストで変数を見ると何やら残骸が残っている
ように見えていたため、Nothingして綺麗にしていました。
不要ということで削除しました。

>>     If Not exlExcelBook Is Nothing Then
>この時点で、exlExcelBook が Nothing になる事は無いように思えます。
了解です。削除しました。

>>     If Not exlExcelBook Is Nothing Then
>>         Try
>>             exlExcelBook.Close(False)
>>         Finally
>>             MRComObject(exlExcelBook)
>>         End Try
>>     End If
>>     exlExcelBook = Nothing
>>     MRComObject(exlSheets)
>>     exlSheets = Nothing
>>     MRComObject(exlBooks)
>>     exlBooks = Nothing
>Range → Sheet → Workbook → Sheets → Workbooks の順に解放していますが、
>念のため、下位のオブジェクトから行っていった方が良いと思います。
>Range → Sheet → Sheets → Workbook → Workbooks の順に書き換えてみてください。
了解です。書き換えました。

>>     exlExcelApp.Quit()
>>     intFileCnt = intFileCnt + 1
>>   Next
>>   'Excelの結果に応じて、画面のListViewの編集処理
>>   If Not exlExcelApp Is Nothing Then
>>     Try
>>       exlExcelApp.Quit()

>ループ内の exlExcelApp.Quit は問題があるように思えます。

>Quit するなら、ReleaseComObject もセットで行うべきですし、
>Quit した後で、2回目以降に exlExcelApp を使い続けるべきではありません。
なるほど。これはちょっと試してみたいと思います。
例えば、exlExcelAppをオブジェクト型で定義して、その変数をExcelファイル毎に
ReleaseComObjectの解放とReleaseComObjectのセット(?)でやっていくということでしょうか?

>>   If Not exlExcelApp Is Nothing Then
>>     Try
>>       exlExcelApp.Quit()
>>     Finally
>>       MRComObject(exlExcelApp)
>>     End Try
>>   End If
>>   exlExcelApp = Nothing
>最後の Nothing 代入は、何を意図しているのでしょうか?
>
>「If Not exlExcelApp Is Nothing Then」の内側では、
>MRComObject によって、既に Nothing になっているようですし、
>If ブロック内を処理していないなら、既に Nothing になっているハズですよね。
はい。前述したとおり、Nothingは削除するようにします。

>> Public Shared Sub MRComObject(Of T As Class)(ByRef objCom As T, Optional ByVal force As Boolean = False)
>どこかで見たコード…。
VBレスキュー(花ちゃん)の方で参考にさせて頂きました・・・。


魔界の仮面弁士  2010-05-12 05:24:37  No: 146858

> exlCellsでセルのvalueを参照して値の抽出処理を行っているだけでReleaseComObjectは行っておりません。
それはどのようなコードですか?

たとえば、
  data = exlCells(行, 列).Value
などの記述は NG ですが、その点は大丈夫でしょうか。

>> Quit するなら、ReleaseComObject もセットで行うべきですし、
>> Quit した後で、2回目以降に exlExcelApp を使い続けるべきではありません。
> なるほど。これはちょっと試してみたいと思います。

Quit は、Excel アプリ本体を終了させるためのメソッドですよね。
(Form に対する Close メソッドのようなもの)

ループ終了後に閉じている部分に関しては分かりますが、ループ中で
(ブックでは無く)Excel 本体を毎回終了させているのは、あまり
意味が無い様に思えました。

終了させる必要が無いのであれば、Quit すべきでは無いと思いますし、
終了させる必要があるのなら、Quit 後には新たに Excel.Application を
起動しなおすべきでしょう。

> 例えば、exlExcelAppをオブジェクト型で定義して、その変数をExcelファイル毎に
> ReleaseComObjectの解放とReleaseComObjectのセット(?)でやっていくということでしょうか?

ごめんなさい。質問の意味が分かりませんでした。

急に「オブジェクト型」という言葉が出てきたので困惑していますが、
それは As Object で宣言するという意味でしょうか。もしも現状の
  Dim exlExcelApp As New Excel.Application()
というコードを
  Dim exlExcelApp As Object = New Excel.Application()
にするという意味だとしたら、そのような事はすべきではありません。

また、「ReleaseComObjectの解放とReleaseComObjectのセット」という文も
何を意味しているのか分かりませんでした。『(?)』と書かれていますので、
御自身でも内容を伝え切れていないのだとは想像しますが…。

# ReleaseComObjectの解放というのは分かりますが、
# ReleaseComObjectのセットとは一体?

で、解放処理についてですが、それぞれをどのタイミングで行うべきかは
事前に考えておいてください。

たとえば、今回のコードは複数のファイルを順に読み出しているようですが、
  (案1) Excel をひとつだけ起動し、その中で、ファイルを順次加工していく。
  (案2) ファイルを編集するたびに Excel 自体も再起動する。
のいずれかで、解放のタイミングは異なってくるかと思います。

案1で実装する場合は、このような流れになるでしょう。
Excel の起動および解放は、ListBox のループよりも外側で行います。

    Excel本体 起動
    Workbooks 取得
    ≫ループ開始[ファイル名]
      Workbook 取得
      Sheets 取得
      ≫ループ開始[Sheet番号]
        Sheet 取得
        Range 取得
        (各セルの内容を読み取る)
        Range 解放
        Sheet 解放
      ≪ループ終了[Sheet番号]
      Sheets 解放
      Workbook.Close
      Workbook 解放
    ≪ループ終了[ファイル名]
    Workbooks 解放
    Excel本体.Quit
    Excel本体 解放

案2 の場合は、こんな感じ。
Excel の起動および解放が、ListBox のループの内側で行われます。

    ≫ループ開始[ファイル名]
      Excel本体 起動
      Workbooks 取得
      Workbook 取得
      Sheets 取得
      ≫ループ開始[Sheet番号]
        Sheet 取得
        Range 取得
        (各セルの内容を読み取る)
        Range 解放
        Sheet 解放
      ≪ループ終了[Sheet番号]
      Sheets 解放
      Workbook.Close
      Workbook 解放
      Workbooks 解放
      Excel本体.Quit
      Excel本体 解放
    ≪ループ終了[ファイル名]

>>> Public Shared Sub MRComObject(Of T As Class)(ByRef objCom As T, Optional ByVal force As Boolean = False)
>>どこかで見たコード…。
> VBレスキュー(花ちゃん)の方で参考にさせて頂きました・・・。
http://hanatyan.sakura.ne.jp/dotnet/Excel08.htm あたりでしょうか。
(あちらにも掲示板はありますよね?)

なお、無断転載(≠引用)は禁止されていますのでご注意あれ。
http://hanatyan.sakura.ne.jp/hazimeni.htm


はる  2010-05-12 18:53:24  No: 146859

魔界の仮面弁士さん

分かりやす説明と丁寧なご返信をありがとうございます。
インラインにて、解答させて頂きます。
>> exlCellsでセルのvalueを参照して値の抽出処理を行っているだけで ReleaseComObjectは行っておりません。
>それはどのようなコードですか?
>
>たとえば、
>  data = exlCells(行, 列).Value
>などの記述は NG ですが、その点は大丈夫でしょうか。
そうなんですね…。NGがありました。
Valueで設定しているだけでもその変数の解放が必要なんですね。
例でいう、MRComObject(data)を追加しますね。

>>> Quit するなら、ReleaseComObject もセットで行うべきですし、
>>> Quit した後で、2回目以降に exlExcelApp を使い続けるべきではありません。
>> なるほど。これはちょっと試してみたいと思います。
>
>Quit は、Excel アプリ本体を終了させるためのメソッドですよね。
>(Form に対する Close メソッドのようなもの)
>
>ループ終了後に閉じている部分に関しては分かりますが、ループ中で
>(ブックでは無く)Excel 本体を毎回終了させているのは、あまり
>意味が無い様に思えました。
>
>終了させる必要が無いのであれば、Quit すべきでは無いと思いますし、
>終了させる必要があるのなら、Quit 後には新たに Excel.Application を
>起動しなおすべきでしょう。
やりたい事は案1の方でした。
確かにおっしゃるとおりQuitは必要ないですね。削除します。

>> 例えば、exlExcelAppをオブジェクト型で定義して、その変数をExcelファイル毎に
>> ReleaseComObjectの解放とReleaseComObjectのセット(?)でやっていくということでしょうか?
>
>ごめんなさい。質問の意味が分かりませんでした。
>
>急に「オブジェクト型」という言葉が出てきたので困惑していますが、
>それは As Object で宣言するという意味でしょうか。もしも現状の
>  Dim exlExcelApp As New Excel.Application()
>というコードを
>  Dim exlExcelApp As Object = New Excel.Application()
>にするという意味だとしたら、そのような事はすべきではありません。
>
>また、「ReleaseComObjectの解放とReleaseComObjectのセット」という文も
>何を意味しているのか分かりませんでした。『(?)』と書かれていますので、
>御自身でも内容を伝え切れていないのだとは想像しますが…。
>
># ReleaseComObjectの解放というのは分かりますが、
># ReleaseComObjectのセットとは一体?
こちらこそ、困惑させてしまい、申し訳ありません。
オブジェクト型は「As Object」の意味でして、以下のように
魔界の仮面弁士がおっしゃる案2のイメージでした。

Dim exlExcelApp As Object

[ループ1 開始]
exlExcelApp = new Excel.Application()  ←こことReleaseComObjectのセットと勘違い
exlbook = exlExcelApp.books.open("")
[ループ2 開始]

[ループ2 終了]
exlbook 解放
exlExcelApp 解放
[ループ1 終了]

>
>で、解放処理についてですが、それぞれをどのタイミングで行うべきかは
>事前に考えておいてください。
>
>たとえば、今回のコードは複数のファイルを順に読み出しているようですが、
>  (案1) Excel をひとつだけ起動し、その中で、ファイルを順次加工していく。
>  (案2) ファイルを編集するたびに Excel 自体も再起動する。
>のいずれかで、解放のタイミングは異なってくるかと思います。
>
>案1で実装する場合は、このような流れになるでしょう。
>Excel の起動および解放は、ListBox のループよりも外側で行います。
>
>    Excel本体 起動
>    Workbooks 取得
>    ≫ループ開始[ファイル名]
>      Workbook 取得
>      Sheets 取得
>      ≫ループ開始[Sheet番号]
>        Sheet 取得
>        Range 取得
>        (各セルの内容を読み取る)
>        Range 解放
>        Sheet 解放
>      ≪ループ終了[Sheet番号]
>      Sheets 解放
>      Workbook.Close
>      Workbook 解放
>    ≪ループ終了[ファイル名]
>    Workbooks 解放
>    Excel本体.Quit
>    Excel本体 解放
>
>案2 の場合は、こんな感じ。
>Excel の起動および解放が、ListBox のループの内側で行われます。
>
>    ≫ループ開始[ファイル名]
>      Excel本体 起動
>      Workbooks 取得
>      Workbook 取得
>      Sheets 取得
>      ≫ループ開始[Sheet番号]
>        Sheet 取得
>        Range 取得
>        (各セルの内容を読み取る)
>        Range 解放
>        Sheet 解放
>      ≪ループ終了[Sheet番号]
>      Sheets 解放
>      Workbook.Close
>      Workbook 解放
>      Workbooks 解放
>      Excel本体.Quit
>      Excel本体 解放
>    ≪ループ終了[ファイル名]
>
案1の方が処理的に軽そうなので、そちらで試してみます。
ありがとうございました。

>>>> Public Shared Sub MRComObject(Of T As Class)(ByRef objCom As T, Optional ByVal force As Boolean = False)
>>>どこかで見たコード…。
>> VBレスキュー(花ちゃん)の方で参考にさせて頂きました・・・。
>http://hanatyan.sakura.ne.jp/dotnet/Excel08.htm あたりでしょうか。
>(あちらにも掲示板はありますよね?)
ありますが、ここの掲示板の情報を見てた時に質問したくなったので・・・。
かなり場当たりです^^;

>
>なお、無断転載(≠引用)は禁止されていますのでご注意あれ。
>http://hanatyan.sakura.ne.jp/hazimeni.htm
個人利用なので、いいかなと思っていましたが、断りを入れるようにします。
ありがとうございました。

また、ちゃんと解放まで出来るようになったら、報告しますね。


はる  2010-05-12 19:08:38  No: 146860

魔界の仮面弁士さん

ご指摘の内容を適用したところ、プロセスにExcel.exeが残らないようになりました!
ありがとうございます!


はる  2010-05-13 01:15:32  No: 146861

解決チェックを忘れていました。

魔界の仮面弁士さん、ありがとうございます。


魔界の仮面弁士  2010-05-13 05:20:51  No: 146862

>>  data = exlCells(行, 列).Value
>>などの記述は NG ですが、その点は大丈夫でしょうか。
> そうなんですね…。NGがありました。
先の回答で、「exlExcelSheet = exlExcelBook.Sheets(i)」ではなく
「exlExcelSheet = exlSheets(i)」の形式を使うように指摘していますが、
それとこれは、いずれも同種の理由によるものです。

exlCells(行, 列) は、exlCells.Default(行, 列) の省略表記です。
そして、exlCells.Default(行, 列) は、Range オブジェクトを返しますから、
この Range オブジェクトも解放すべき対象となります。ゆえに、
  foo = exlCells(行, 列)
  data = foo.Value
  Marshal.ReleaseComObject(foo)
が求められます。

> オブジェクト型は「As Object」の意味でして、
それは、避けておいた方が無難かと思います。

As Object な変数を使って操作すると、特定の条件化においては、
内部的な型変換のタイミングで参照カウントが増加してしまい、
解放処理を複雑にしてしまうケースが存在します。

例えば、
    Dim Hs As Excel.HPageBreaks = objSheet1.HPageBreaks
    Dim H As Excel.HPageBreak = Hs.Add(objRange)
    Marshal.ReleaseComObject(H)
    Marshal.ReleaseComObject(Hs)
    Marshal.ReleaseComObject(objRange)
    Marshal.ReleaseComObject(objSheet1)
ならば正しく解放されたコードが、参照設定無しだと
    Dim Hs As Object = objSheet1.HPageBreaks
    Dim H As Object = Hs.Add(objRange)  'ここで参照カウントが増加してしまう。
    Marshal.ReleaseComObject(H)
    Marshal.ReleaseComObject(Hs)
    Marshal.ReleaseComObject(objRange)  'そのため、ここで解放してもまだ一つ残っている…。
    Marshal.ReleaseComObject(objSheet1)
という結果になってしまった事がありました。
http://madia.world.coocan.jp/cgi-bin/VBBBS2/wwwlng.cgi?print+200512/05120042.txt


はる  2010-05-14 19:27:14  No: 146863

魔界の仮面弁士さん

返事が遅くなりました。
>>>  data = exlCells(行, 列).Value
>>>などの記述は NG ですが、その点は大丈夫でしょうか。
>> そうなんですね…。NGがありました。
>先の回答で、「exlExcelSheet = exlExcelBook.Sheets(i)」ではなく
>「exlExcelSheet = exlSheets(i)」の形式を使うように指摘していますが、
>それとこれは、いずれも同種の理由によるものです。
>
>exlCells(行, 列) は、exlCells.Default(行, 列) の省略表記です。
>そして、exlCells.Default(行, 列) は、Range オブジェクトを返しますから、
>この Range オブジェクトも解放すべき対象となります。ゆえに、
>  foo = exlCells(行, 列)
>  data = foo.Value
>  Marshal.ReleaseComObject(foo)
>が求められます。
なるほど。了解です。

> オブジェクト型は「As Object」の意味でして、
>それは、避けておいた方が無難かと思います。
>
>As Object な変数を使って操作すると、特定の条件化においては、
>内部的な型変換のタイミングで参照カウントが増加してしまい、
>解放処理を複雑にしてしまうケースが存在します。
>
>例えば、
>    Dim Hs As Excel.HPageBreaks = objSheet1.HPageBreaks
>    Dim H As Excel.HPageBreak = Hs.Add(objRange)
>    Marshal.ReleaseComObject(H)
>    Marshal.ReleaseComObject(Hs)
>    Marshal.ReleaseComObject(objRange)
>    Marshal.ReleaseComObject(objSheet1)
>ならば正しく解放されたコードが、参照設定無しだと
>    Dim Hs As Object = objSheet1.HPageBreaks
>    Dim H As Object = Hs.Add(objRange)  'ここで参照カウントが増加してしまう。
>    Marshal.ReleaseComObject(H)
>    Marshal.ReleaseComObject(Hs)
>    Marshal.ReleaseComObject(objRange)  'そのため、ここで解放してもまだ一つ残っている…。
>    Marshal.ReleaseComObject(objSheet1)
>という結果になってしまった事がありました。
参照カウント増加を招くとは、Excelオブジェクトの解放も奥が深いですね。
まぁ、使った元をちゃんと元の場所に戻すのは当たり前ですけど、
正直な気持ちは魔法を使って一気に元の場所へ戻すように上位層オブジェクト
を解放するだけで下位層オブジェクトも解放してもらいたいですけど(笑)

とにかく、ありがとうございました!


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加