IE操作中にオブジェクトのメモリが異常に膨れる場合の対応

Excel VBAで繰り返しIEを操作していると、IEオブジェクトの使っているメモリ領域が異常に膨れることがある。 それも、どういうわけか特定のサイトを操作している時に限ってだ。 原因は不明だが、この状況に陥るとExcelが固まってしまい、タスクマネージャーから強制終了するしかなくなる。

とりあえずの対策として、ブラウザの操作回数をカウントして一定の回数に達したら一度ブラウザを再起動して続きを行うようにしていた。 ずっとそれで対応してきたが、やはり無駄な再起動は控えたいので、プロセスが確保しているメモリ量を取得して、これが一定のサイズに達したら再起動するような方法に変えることにした。

WMIを使ってプロセス情報を取得する。 WMIで取得できる値を確認するため、試しに以下コードを実行してみた。 IEを操作しているので、IEのプロセス情報だけ表示するようにした。

Sub test()

    Dim WMI As Object
    Set WMI = CreateObject("WbemScripting.SWbemLocator")

    Dim oService As Object
    Set oService = WMI.ConnectServer

    Dim oClassSet As Object
    Set oClassSet = oService.ExecQuery("SELECT * FROM Win32_Process")

    Dim oClass As Object
    DEV_MODE = True
    For Each oClass In oClassSet
        If oClass.Caption = "iexplore.exe" Then
            Debug.Print "Caption:" & oClass.Caption
            Debug.Print "CommandLine:" & oClass.commandLine
            Debug.Print "Handle:" & oClass.Handle
            Debug.Print "PageFaults:" & oClass.PageFaults
            Debug.Print "PageFileUsage:" & oClass.PageFileUsage
            Debug.Print "PeakPageFileUsage:" & oClass.PeakPageFileUsage
            Debug.Print "PrivatePageCount:" & oClass.PrivatePageCount
            Debug.Print "ProcessId:" & oClass.ProcessID
            Debug.Print "WorkingSetSize:" & oClass.WorkingSetSize
            Debug.Print ""
        End If
    Next

    Set oClass = Nothing
    Set oClassSet = Nothing
    Set oService = Nothing
    Set WMI = Nothing
End Sub

実行結果は以下。

Caption:iexplore.exe
CommandLine:"C:\Program Files\Internet Explorer\iexplore.exe"
Handle:4908
PageFaults:14549
PageFileUsage:9172
PeakPageFileUsage:11008
PrivatePageCount:9392128
ProcessId:4908
WorkingSetSize:38666240

Caption:iexplore.exe
CommandLine:"C:\Program Files (x86)\Internet Explorer\IEXPLORE.EXE" SCODEF:4908 CREDAT:17410 /prefetch:2
Handle:9112
PageFaults:177431
PageFileUsage:95496
PeakPageFileUsage:100440
PrivatePageCount:97787904
ProcessId:9112
WorkingSetSize:133042176

この時のタスクマネージャーの表示は以下。

プログラムの出力結果のWorkingSetSizeと、タスクマネージャーのワーキングセット(メモリ)が近い値( 133042176 ÷ 1024 = 129,924 )なので、同じ意味と思われる。

上の結果を参考に、IEプロセスの使用メモリを合計してその値を戻す関数を作った。

Function getIEMemSize() as Long
    On Error GoTo ErrorHandler

    Dim WMI As New WbemScripting.SWbemLocator
    Dim oService As Object, oClassSet As Object, oClass As Object
    Set oService = WMI.ConnectServer
    Set oClassSet = oService.ExecQuery("SELECT * FROM Win32_Process")

    Dim ieMemSize: ieMemSize = 0
    For Each oClass In oClassSet
        If oClass.caption = "iexplore.exe" Then
            ieMemSize = ieMemSize + oClass.WorkingSetSize
        End If
    Next

    Set oClass = Nothing
    Set oClassSet = Nothing
    Set oService = Nothing
    Set WMI = Nothing

    getIEMemSize = cLng(ieMemSize / 1024 / 1024)
    Exit Function
ErrorHandler:
    debugPrintFile "getIEMemSize error! Err.Number=[" & err.Number & "], Err.Description=[" & err.description & "]"
    getIEMemSize = -1
End Function

あとは、ブラウザ操作側でこの関数を呼び出して、一定の値を超えていればIEプロセスをkillすればよい。 以下のようなプロセスをkillする関数を作って、引数に “iexplore.exe” を渡すようにコールする。

Sub pkill(strProcName As String)
    On Error Resume Next

    Dim objProcList ' プロセス一覧
    Dim objProcess  ' プロセス情報
    Dim lngKillNum  ' 終了したプロセス数
    lngKillNum = 0

    Set objProcList = GetObject("winmgmts:").InstancesOf("win32_process")
    For Each objProcess In objProcList
        If LCase(objProcess.Name) = strProcName Then
            objProcess.Terminate
            If err.Number = 0 Then
                lngKillNum = lngKillNum + 1
            Else
                Debug.Print "エラー: " & err.description
            End If
        End If
    Next

    If lngKillNum > 0 Then
        Debug.Print strProcName & " を " & lngKillNum & " 個強制終了しました。"
    Else
        Debug.Print strProcName & " が見つかりませんでした。"
    End If
    Set objProcList = Nothing
End Sub

参考ページ

https://excel-ubara.com/excelvba4/EXCEL_VBA_438.html

関連ページ