VB.NETCOM オブジェクトの参照カウントを解放する

  • C#
  • VB.NET

スポンサーリンク

COM オブジェクトを扱う場合は、参照カウントの解放を意識しなければなりません。使用した COM オブジェクトの参照カウントが、正しくデクリメントされていないと意図したタイミングでプロセスが解放されません。

参照カウントのデクリメントは、System.Runtime.InteropServices.Marshal クラスにある ReleaseComObject メソッドを使用します。COM ラッパ オブジェクト (COM そのものではない) の参照が未到達になれば、ガベージ コレクションを実行して解放することができますが、こちらはあくまでも保険です。

参照カウントのデクリメント解放自体は、ReleaseComObject メソッドを呼び出すだけですので難しくはありません。面倒なのは、ReleaseComObject メソッドを実行するために、参照したものをすべて変数へ格納しておく必要があるという点です。この点、COM 専用の言語である Visual Basic 6 以前とは、全く勝手が違いますので注意する必要があります。

また、処理の途中で例外が発生した場合でも参照カウントをデクリメントするために、try ~ finally を多用することになります。一連の流れを 1 つのメソッドで表現すると、どうしても可読性が悪くなってしまいます。

たとえば、下記は技術系掲示板でよく話題にあがる、Office PIA / Excel Interop 系のコード例です。こんなにネストが深くなってしまった時点で、Coding Horror にあたると思いますが、説明のためにそのままにしています。

個人的には COM を扱う場合は COM 専用の言語で実装することをお勧めします。VBA 側のマクロを .NET アプリケーションから呼び出す方法、VB または VBScript に処理を実装して呼び出す方法、あるいは ScriptControl を利用する方法があります。これらの方法であれば参照カウントの面倒はそれほど面倒ではありません。

とくに ASP.NET (Web アプリケーション) では使用しないことをお勧めします。サーバーサイドのオートメーションは技術的にも多くの問題をかかえており (障害が発生した場合 Web サーバの再立ち上げが必要な場合が多い) Microsoft も動作保証をしていません。

このあたりは @IT 会議室などを中心に私が何度か書いておりましたが、サイトでの説明が不十分なままでした。

サンプルコード

以下にサンプルコードを示します。

VB.NET 全般
    ' 必要な変数は Try の外で宣言する
    Dim xlApplication As Excel.Application

    ' COM オブジェクトの解放を保証するために Try ~ Finally を使用する
    Try
        xlApplication = New Excel.Application()

        ' 警告メッセージなどを表示しないようにする
        xlApplication.DisplayAlerts = False

        Dim xlBooks As Excel.Workbooks = xlApplication.Workbooks

        Try
            Dim xlBook As Excel.Workbook = xlBooks.Add()

            Try
                Dim xlSheets As Excel.Sheets = xlBook.Worksheets

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

                    Try
                        Dim xlCells As Excel.Range = xlSheet.Cells

                        Try
                            Dim xlRange As Excel.Range = DirectCast(xlCells(6, 4), Excel.Range)

                            Try
                                ' Microsoft Excel を表示する
                                xlApplication.Visible = True

                                ' 1000 ミリ秒 (1秒) 待機する
                                System.Threading.Thread.Sleep(1000)

                                ' Row=6, Column=4 の位置に文字をセットする
                                xlRange.Value2 = "あと 1 秒で終了します"

                                ' 1000 ミリ秒 (1秒) 待機する
                                System.Threading.Thread.Sleep(1000)
                            Finally
                                If Not xlRange Is Nothing Then
                                    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange)
                                End If  
                            End Try
                        Finally
                            If Not xlCells Is Nothing Then
                                System.Runtime.InteropServices.Marshal.ReleaseComObject(xlCells)
                            End If
                        End Try
                    Finally
                        If Not xlSheet Is Nothing Then
                            System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheet)
                        End If
                    End Try
                Finally
                    If Not xlSheets Is Nothing Then
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheets)
                    End If
                End Try
            Finally
                If Not xlBook Is Nothing Then
                    Try
                        xlBook.Close()
                    Finally
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBook)
                    End Try
                End If
            End Try
        Finally
            If Not xlBooks Is Nothing Then
                System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBooks)
            End If
        End Try
    Finally
        If Not xlApplication Is Nothing Then
            Try
                xlApplication.Quit()
            Finally
                System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApplication)
            End Try
        End If
    End Try

関連するリファレンス

準備中です。

スポンサーリンク