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

Excelシートコピー時のメモリ解放

環境/言語:[Win2000(SP4), VB.NET, NetFramework1.1, Office2000(SP3)]
分類:[.NET]

小島です。
以前はVB.NETにおけるExcelオートメーション操作でメモリ解放されない
対策のコーディング手法を勉強させていただき、ありがとうございました。

今回は1ページ目の定型シートを設定ページ分だけシートコピーさせたい
ですが、下記コーディングではメモリ解放されません。
どなたかご教示願えませんでしょうか。


Dim xlFilePath As String
Dim xlApp As Excel.Application
Dim xlBooks As Excel.Workbooks
Dim xlBook As Excel.Workbook
Dim xlSheets As Excel.Sheets

xlApp = New Excel.Application
xlApp.Visible = True
xlBooks = xlApp.Workbooks
xlBook = xlBooks.Open(xlFilePath)
xlSheets = xlBook.Worksheets

For sheet_count = 1 To max_sheet Step 1
Dim xlSheets_w100 As Excel.Worksheet = xlSheets(sheet_count)
Dim xlSheets_w101 As Excel.Sheets = xlBook.Worksheets
Dim xlNewSheet As Object = xlSheets_w101(1).Copy(after:=xlSheets_w100)
ReleaseCOM(xlNewSheet)
ReleaseCOM(xlSheets_w101)
ReleaseCOM(xlSheets_w100)
Next

xlApp.DisplayAlerts = False
xlBook.SaveAs(xlFilePath, xlWord.xlWorkbookNormal)

ReleaseCOM(xlSheets)
xlBook.Close(False)
ReleaseCOM(xlBook)
xlBooks.Close()
ReleaseCOM(xlBooks)
xlApp.Quit()
ReleaseCOM(xlApp)
System.Threading.Thread.Sleep(1000)


Public Sub ReleaseCOM(ByVal o As Object)
Try
If System.Runtime.InteropServices.Marshal.IsComObject(o) Then System.Runtime.InteropServices.Marshal.ReleaseComObject(o)
End If
Catch
Finally
o = Nothing
End Try
End Sub
■No14701に返信(小島いさおさんの記事)
> Dim xlNewSheet As Object = xlSheets_w101(1).Copy(after:=xlSheets_w100)

ここが原因です。

目的のメソッド、プロパティへアクセスするまでのオブジェクトは、
すべて参照として取っておくことが基本中の基本です。

  COM オブジェクトを解放する
  http://jeanne.wankuma.com/tips/programing/releasecom.html


_________________________________________________________________________
じゃんぬ Microsoft MVP for Visual Developer - C#
  http://jeanne.wankuma.com/
  http://blogs.wankuma.com/jeanne/
■No14703に返信(じゃんぬねっとさんの記事)

じゃんぬねっと様

小島いさおです。
昨日はVB.NET掲示板ご回答ありがとうございました。

■No14701に返信(小島いさおさんの記事)
> Dim xlNewSheet As Object = xlSheets_w101(1).Copy(after:=xlSheets_w100)

↑こちらのどの箇所を、どのように修正すればよろしいでしょうか。
ちなみにAddメソッド(シート新規追加)は、上記文法でうまく動作します。
とにかくExcelオブジェクトやメソッドを使うときは変数化して参照と解放を
行うことを理解・実践していますが、Copyメソッドは初心者です。
■No14721に返信(小島いさおさんの記事)
> > Dim xlNewSheet As Object = xlSheets_w101(1).Copy(after:=xlSheets_w100)
> ↑こちらのどの箇所を、どのように修正すればよろしいでしょうか。
> ちなみにAddメソッド(シート新規追加)は、上記文法でうまく動作します。
> とにかくExcelオブジェクトやメソッドを使うときは変数化して参照と解放を
> 行うことを理解・実践していますが、Copyメソッドは初心者です。

さて、

> > 目的のメソッド、プロパティへアクセスするまでのオブジェクトは、
> > すべて参照として取っておくことが基本中の基本です。

と書きました。

まず、xlSheets_w101(1) の時点で参照が 1 つあります。
これも変数に参照を取る必要があります。

その変数から Copy メソッドを実行し、戻り値も別の変数で取得してください。
もちろん、どちらも ReleaseComObject しなければなりません。


_________________________________________________________________________
じゃんぬ Microsoft MVP for Visual Developer - C#
  http://jeanne.wankuma.com/
  http://blogs.wankuma.com/jeanne/
>>> Dim xlNewSheet As Object = xlSheets_w101(1).Copy(after:=xlSheets_w100)
>
>>> 目的のメソッド、プロパティへアクセスするまでのオブジェクトは、
>>> すべて参照として取っておくことが基本中の基本です。
>
> まず、xlSheets_w101(1) の時点で参照が 1 つあります。
> これも変数に参照を取る必要があります。

xlSheets_w101(1)→WorkSheets.Item(xxx) As Objectだからですね。
私もうまく動くまで苦労しましたが
使うメンバが(a.b.c.d...のどこにあっても)、Member As xxxxxでxxxxxがCollectionや値型?以外のものは保持しておくという捉え方でいいのかな?
何度もすみません。思うように動作しないのでしつこく質問します。
下記コーディング例でCopyメソッド使う場合、Excelメモリ解放するには
どのように改善すればいいでしょう。
自分の力量でこれ以上は策が思いつきません。
すべてメモリ変数に代入し、メソッド終了直後に変数解放しているつもり
ですが、何が足りないのでしょうか。
具体的なお返事いただけないでしょうか。

For sheet_count = 1 To max_sheet - 1 Step 1

Dim xlSheets_w100 As Excel.Sheets = xlBook.Worksheets

Dim xlNewSheet As Object = _
xlSheets_w100(1).Copy(after:=xlSheets_w100(sheet_count))

ReleaseCOM(xlNewSheet)

ReleaseCOM(xlSheets_w100)
Next
dim a = xlSheets_w100(1)
Dim xlNewSheet As Object = a.Copy(after:=xlSheets_w100(sheet_count))
■No14734に返信(小島いさおさんの記事)
> 下記コーディング例でCopyメソッド使う場合、Excelメモリ解放するには
> どのように改善すればいいでしょう。

先の投稿から変わってませんね。(^^)

> すべてメモリ変数に代入し、メソッド終了直後に変数解放しているつもりですが、何が足りないのでしょうか。

「すべて」でないので、それが足らないです。

> 具体的なお返事いただけないでしょうか。

うーん、ソースを書けになるんでしょうけど、

> Dim xlNewSheet As Object = _
> xlSheets_w100(1).Copy(after:=xlSheets_w100(sheet_count))

先にも書きましたが、ここが原因です。

xlSheets_w100(1) の時点で Sheets (複数形) から Sheet を取り出しています。
それに対していきなり Copy メソッドを実行しているため、Sheet の参照を解放するタイミングを失っています。

リンク先に答えがありますが、

Dim xlSheet As Excel.Worksheet = DirectCast(xlSheets(1), Excel.Worksheet)

です。

この xlSheet から Copy メソッドを実行します。
もちろん、この xlSheet も解放する必要があります。

  COM オブジェクトを解放する
  http://jeanne.wankuma.com/tips/programing/releasecom.html


_________________________________________________________________________
じゃんぬ Microsoft MVP for Visual Developer - C#
  http://jeanne.wankuma.com/
  http://blogs.wankuma.com/jeanne/
■No14735に返信(中博俊さんの記事)
> dim a = xlSheets_w100(1)
> Dim xlNewSheet As Object = a.Copy(after:=xlSheets_w100(sheet_count))

型が明示化されてませんね...
遅延バインディング...


_________________________________________________________________________
じゃんぬ Microsoft MVP for Visual Developer - C#
  http://jeanne.wankuma.com/
  http://blogs.wankuma.com/jeanne/
■No14736に返信(じゃんぬねっとさんの記事)

じゃんぬねっと様、こまめなお返事ありがとうございます。

Dim xlSheets_w100 As Excel.Sheets = xlBook.Worksheets
>> Dim xlNewSheet As Object = _
>> xlSheets_w100(1).Copy(after:=xlSheets_w100(sheet_count))

問題はCopyメソッド記述・動作条件のような気がしてきました。
すなわち「1番目のシートを任意シート直後に追加コピーしたい」とき、
Copyメソッドが動作する条件がありまして上記例の場合、
xlSheets_w100(左辺)は、Sheets型オブジェクトしかもシート番号が必須で
この条件を満たさないと「式が値を返しません」文法エラー、または実行時に
「オブジェクトが足りません」等のエラーになってしまいます。
したがって現在はメモリ変数解放とCopyメソッド動作条件の板挟みです。
ちなみにxlSheets_w100(右辺)は、Sheets、Worksheets、Worksheet、
いずれのオブジェクト型でも良いようです。
何か良策ございますか。
> 問題はCopyメソッド記述・動作条件のような気がしてきました。

じゃんぬさんや中さんの提示されているズバリのことを試されましたか?

私も書きましたが、

> Dim xlSheets_w100 As Excel.Sheets = xlBook.Worksheets
> >> Dim xlNewSheet As Object = _
> >> xlSheets_w100(1).Copy(after:=xlSheets_w100(sheet_count))

xlSheets_w100(1)はWorkSheets.Item(1)の省略形です。
そしてItemはメソッドでありObject型でWorkSheetを返す関数です。
「参照するものは解放するために保持しておく必要がある」ので返ってくるObjectを保持しておく必要があります。
つまり、xlSheets_w100(1).Copyのままでは受け取って保持していない状態ですので
面倒ですが、一旦変数へItemメソッドの返すオブジェクトを代入し、その変数でCopyメソッドを実行します。
#右辺にも同様のことが言えます。
■No14763に返信(まどかさんの記事)

まどかさん、じゃんぬねっとさん他のみなさんよりご教示頂きました
「シート追加コピー時におけるExcelメモリ解放」について
下記コーディングで無事解決しました。ありがとうございました。

原因はItemメソッドの存在や利用法や効果を知らなかった点と、
xlsheets_w101s 代入など、暗示的に参照するオブジェクトも
変数化して解放するのを怠った点にあります。
とにかく実際に経験しないと暗示的な参照の部分に気づかないのが
困ります。

For sheet_count = 1 To max_sheet - 1 Step 1

Dim xlSheets_w100 As Excel.Sheets = xlBook.Worksheets
Dim xlsheets_w101 As Object = _
DirectCast(xlSheets_w100.Item(1), Excel.Worksheet)
Dim xlsheets_w101s As Excel.Worksheet = _
DirectCast(xlsheets_w101, Excel.Worksheet)
Dim xlsheets_w199 As Object = _
DirectCast(xlSheets_w100.Item(sheet_count), Excel.Worksheet)
Dim xlsheets_w199s As Excel.Worksheet = _
DirectCast(xlsheets_w199, Excel.Worksheet)

Dim xlNewSheet As Object = xlsheets_w101.Copy(after:=xlsheets_w199)
ReleaseCOM(xlNewSheet)

ReleaseCOM(xlsheets_w199s)
ReleaseCOM(xlsheets_w199)
ReleaseCOM(xlsheets_w101s)
ReleaseCOM(xlsheets_w101)
ReleaseCOM(xlSheets_w100)
Next
解決済み!
■No14771に返信(小島いさおさんの記事)
> 原因はItemメソッドの存在や利用法や効果を知らなかった点と、
> xlsheets_w101s 代入など、暗示的に参照するオブジェクトも
> 変数化して解放するのを怠った点にあります。
> とにかく実際に経験しないと暗示的な参照の部分に気づかないのが
> 困ります。

私を含め 3 名の方が同じことを指して回答をしていたわけです。(^^)

> ReleaseCOM(xlsheets_w199s)
> ReleaseCOM(xlsheets_w199)
> ReleaseCOM(xlsheets_w101s)
> ReleaseCOM(xlsheets_w101)
> ReleaseCOM(xlSheets_w100)

Finally に書いてください。
例外発生時にプロセスが居残ります。

> とにかく実際に経験しないと暗示的な参照の部分に気づかないのが困ります。

そんなことはないです。
COM の内部的な参照カウントはともかく、基本的に以下のようなルールであると覚えていれば良いです。

限定子である「.」 が 2 つ続くステートメントがあれば NG。
例 : object1.object2.Method()

コレクションからアイテムを取り出しているものがあれば NG。
例 : object1(1).Method()
これは、object1.Items(1).Method() と同じだとイメージする。

Excel 配下すべてのメンバを把握する必要などありません。


_________________________________________________________________________
じゃんぬ Microsoft MVP for Visual Developer - C#
  http://jeanne.wankuma.com/
  http://blogs.wankuma.com/jeanne/
解決済み!

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