QA@IT

COMオブジェクトの解放について

5175 PV

いつもお世話になっております。
散々既出の内容で申し訳ないのですが、下記のコードを実行すると、
どうしてもエクセルのプロセスが残ってしまいます。
下記の書き方では不十分なのでしょうか?
'''

    '=======================================================
   ' 宣言部
    '=======================================================
    Dim xlAplication As New MyExcel.Application
    Dim xlWorkbooks As MyExcel.Workbooks
    Dim xlWorkbook As MyExcel.Workbook
    Dim xlSheets As MyExcel.Sheets
    Dim xlWorkSheet As MyExcel.Worksheet
    Dim xlRange As MyExcel.Range
    Dim xlStartCell As MyExcel.Range
    Dim xlEndCell As MyExcel.Range

    Dim sTemplatePath As String = ""
    Dim i As Long = 0

    Dim nColumn As Long = 0
    Dim nRow As Long = 0

    Dim nRtn As Integer

    MakeExcelFile = 0

    Try
        '-------------------------------------------------------
        ' テンプレートパスの設定
        '-------------------------------------------------------
        sTemplatePath = <<省略>>         

        '-------------------------------------------------------
        ' Excelを開いてオブジェクトを設定
        '-------------------------------------------------------
        'xlAplication = New MyExcel.Application
        xlWorkbooks = xlAplication.Workbooks

        Try
            ' テンプレート(WorkBook)を開く
            xlWorkbook = xlWorkbooks.Open(sTemplatePath)
            Try
                ' シートの設定
                xlSheets = xlAplication.Worksheets
                Try
                    ' シートを開く
                    xlWorkSheet = xlSheets(1)
                    Try
                        ' 開始セルを設定
                        xlStartCell = DirectCast(xlWorkSheet.Cells(1, 18), MyExcel.Range)
                        Try
                            ' 終了セルを設定
                            nColumn = objModels.GetLength(0)
                            nRow = objModels.GetLength(1)
                            xlEndCell = DirectCast(xlWorkSheet.Cells(nRow, 18 + nColumn), MyExcel.Range)
                            Try
                                '=======================================================
                                ' ここから値の出力処理
                                '=======================================================
                                '-------------------------------------------------------
                                ' 出力 (ここが問題となっている箇所です)
                                '-------------------------------------------------------
                                xlRange = xlWorkSheet.Range(xlStartCell, xlEndCell)
                                xlRange.Value = objModels
                                Try
                                    '-------------------------------------------------------
                                    ' エクセル保存
                                    '-------------------------------------------------------
                                    xlAplication.DisplayAlerts = False
                                    xlWorkbook.SaveAs(<<省略>>)
                                    xlAplication.DisplayAlerts = True
                                Finally
                                    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlRange)
                                End Try
                            Finally
                                System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlEndCell)
                            End Try
                        Finally
                            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlStartCell)
                        End Try

                    Finally
                        System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlWorkSheet)
                    End Try
                Finally
                    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlSheets)
                End Try
            Finally
                '-------------------------------------------------------
                ' ワークブックを閉じる
                '-------------------------------------------------------
                xlAplication.DisplayAlerts = False
                xlWorkbook.Close()
                xlAplication.DisplayAlerts = True
                System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlWorkbook)
            End Try
        Finally
            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlWorkbooks)
        End Try

    Catch ex As Exception

        MakeExcelFile = -1
        Throw ex

    Finally
        '-------------------------------------------------------
        ' Excelを閉じる
        '-------------------------------------------------------
        xlAplication.DisplayAlerts = False
        xlAplication.Quit()
        xlAplication.DisplayAlerts = True
        System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlAplication)

    End Try

'''

ご回答の程、よろしくお願い致します。

追記いたします。
xlRange~の2行に加え、xlStartCell = DirectCast(xlWorkSheet.Cells(1, 18), MyExcel.Range)の「(1,18)」、xlEndCell = DirectCast(xlWorkSheet.Cells(nRow, 18 + nColumn), MyExcel.Range)
の「(nRow, 18 + nColumn)」をコメントアウトすると、プロセスが解放されました。
セルの範囲指定をすることが何かまずいのでしょうか?

  • 突っ込みどころはいくつかあるんですが、xlRange = DirectCast(xlWorkSheet.Range(xlStartCell, xlEndCell),xlRange) でも残りますか? あとこのコードは省略値を適当な値に埋めてobjModelsという二次元配列つくれば動きますか?(値の設定など削除してあるこのレベルまで削ったコードでrororo1235さんの環境で再現しますか?) -
  • 間違えました xlRange = DirectCast(xlWorkSheet.Range(xlStartCell, xlEndCell),MyExcel.Range) です。あとMyExcelってなんですか?Imports使った別名? -
  • コメントありがとうございます。
    テンプレートとなるエクセルと、2次元配列さえあれば、このままでも動作するかと思います。
    MyExcelは、おっしゃる通りImportsの別名です。

    ご回答頂いた内容については、
    明日確認して、結果を追記させていただきます。
    -
  • コメント頂いた内容で試してみたのですが、残念ながらプロセスは残ったままになっておりました。
    他に何か考えられる点はあるでしょうか?
    -

回答

xlWorkSheet.Cells

ReleaseComObject
されていない為ではないでしょうか?

xlWorkSheet.Cellsはメソッドではなく
Cellsというワークシート全体を表すRange型のプロパティです。

編集 履歴 (0)
  • ご回答ありがとうございます。
    まさしくその通りだったようです。
    xlWorkSheet.Cellsをいったん変数に入れて、
    最後に解放してやるとうまくプロセスも解放されました!
    .が2つ続いてなければ良いと安易に考えていたのがまずかったようですね。

    しかし、このCOMオブジェクトの解放ってややこしいですね。
    もっとシンプルな記述方法は無い物なのでしょうか。
    -
  • Tryのネストにならない程度なら解放すべきオブジェクトをListに追加していって最後にまとめて解放するとスマートになります。Close,Quitのタイミング部分だけは注意する必要があるかも。 -

オブジェクトの解放処理を明示的にする必要が有る場合は、Usingステートメントをつくか、COMオブエジェクトをラップしたクラスを作成し、その中でCOMオブジェクトの解放処理を行うのが良いと思います。要するに一々意識せずシステムにお任せできるようにするということです。

編集 履歴 (0)

MakeExcelFile は多分関数名だと思いますが、MakeExcelFile の呼出し後に
GC.Collect()
してみてください。

ちなみに私の環境だとコメントアウトしなくてもExcel残りました。
(VS2012, win8 x64, Excel2013)

追記

解決がマークされてしまったので手短にしておきますが、

このサポートに基づく
http://support.microsoft.com/kb/317109
やり方もありますが(今回解決になったパターンですね)

ベストプラクティスとしてはこちらだそうで、
http://blogs.msdn.com/b/office_client_development_support_blog/archive/2012/02/10/office-part1.aspx

このサンプルコードでは両方を使用して解決しています。
http://code.msdn.microsoft.com/office/VBAutomateExcel-b6ecaff3

あと気になった点は、変数宣言の位置とFinallyでの解放タイミング、nullにセットしてない点です。

編集 履歴 (2)
  • 回答ありがとうございます。
    関数呼び出し後にGC.Collect()の記述を追加いたしましたが、依然プロセスは残ったままでした。

    (ちなみに、xlRangeをコメントアウトしたらプロセスが消える、といったのは私の勘違いだったようです。
    何か全体的にまずいことをしてしまっているのでしょうか・・・?
    -

コメントありがとうございます。
テンプレートとなるエクセルと、2次元配列さえあれば、このままでも動作するかと思います。
MyExcelは、おっしゃる通りImportsの別名です。

ご回答頂いた内容については、
明日確認して、結果を追記させていただきます。

編集 履歴 (0)
ウォッチ

この質問への回答やコメントをメールでお知らせします。