运行 两个 Internet Explorer 实例

Running two instances of Internet Explorer

我正在尝试 运行 两个 Internet Explorer 实例来抓取 HTML。目标是为大多数功能提供一个全局 IE。但是,我需要一个用于特定目的(身份验证)的实例,一旦完成就会被销毁。

出现第二个 IE 实例的原因是网站的身份验证过程会抛出一个难以确认和关闭的 alert() Javascript 弹出窗口。在这种情况下,我目前正在终止 IE 的整个实例。

请注意,我一直在另一个 post 此处讨论弹出窗口:Internet Explorer readyState reverts from Complete to Interactive

一旦我使用它的 PID 终止了 IE 的第二个实例,它似乎也会影响 IE 的全局实例。当我 return 到 IE 的全局实例时,我得到: 运行-time error '462': The remote server machine does not exist or is unavailable.

要复制:

  1. 执行函数运行IE1(可以运行多次)
  2. 执行函数运行IE2(可以运行多次)
  3. 执行函数运行IE1得到错误

模块代码:

Option Explicit

Public Declare Function GetWindowThreadProcessId Lib "user32" _
                                                 (ByVal lHWnd As Long, _
                                                  ByRef lProcessId As Long) As Long
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal Milliseconds As LongPtr)

Public ie_browser As New InternetExplorer

Sub runIE1()
    Debug.Print "--- runIE1 ---"
    Debug.Print "ie_browser PID: "; ie_browser.hwnd
    With ie_browser
        .Navigate "http://127.0.0.1/good.html"
        .Silent = True
        .Visible = False
    End With
    Debug.Print "ie_browser1 Navigated..."
    
    Do Until ie_browser.readyState = 4: DoEvents: Loop
    Do Until ie_browser.Busy = False: DoEvents: Loop
    Debug.Print "ie_browser should have parsed and rendered the page at this time"

    Debug.Print "--- runIE1 ---"
End Sub

Sub runIE2()
    Debug.Print "--- runIE2 ---"
    Dim ie_browser2_hwnd As Long
    Dim ie_browser2 As InternetExplorer
    
    Set ie_browser2 = CreateObject("InternetExplorer.Application")
    Debug.Print "ie_browser2 PID: "; ie_browser2.hwnd
    
    With ie_browser2
        .Navigate "http://127.0.0.1:9000/ftw/bad.html"
        .Silent = True
        .Visible = False
    End With
    
    Debug.Print "ie_browser2 Navigated..."
    
    Debug.Print "ie_browser2 Start wait..."
    Call waitForIE(ie_browser2)
    Debug.Print "ie_browser2 End wait..."
        
    'close if found
    If Not ie_browser2 Is Nothing Then
        Debug.Print "ie_browser2 not null..."
        ie_browser2_hwnd = ie_browser2.hwnd
        ie_browser2.Quit
        Set ie_browser2 = Nothing
        Debug.Print "ie_browser2 quit, set to null"
        Call KillHwndProcess(ie_browser2_hwnd)
        Debug.Print "terminated ie_browser2 PID: " & ie_browser2_hwnd
    End If
    Debug.Print "--- runIE2 ---"
End Sub

Public Sub waitForIE(i As InternetExplorer)
    Dim ie_hwnd As Long
    
    'Ensure browser has completed
    Do While i.readyState = 4: DoEvents: Loop
    
    'Sleep to ensure that we let the readyState to flip back
    Sleep (250)
    
    'popup occurred!
    If i.readyState = 3 Then
        Debug.Print "waitForIE - Popup occurred"
        ie_hwnd = i.hwnd
        Debug.Print "waitForIE - ie PID: " & ie_hwnd
        i.Quit
        Set i = Nothing
        Debug.Print "waitForIE - quit IE, set to nothing..."
        Call KillHwndProcess(ie_hwnd)
        Debug.Print "waitForIE - Terminated IE process: " & ie_hwnd
    Else
        Do Until i.readyState = 4: DoEvents: Loop
        Do Until i.Busy = False: DoEvents: Loop
        
        Debug.Print "Browser should have parsed and rendered the page at this time"
        Debug.Print "IE State: " & i.readyState & " IE busy: " & i.Busy
    End If
    
End Sub


 
'---------------------------------------------------------------------------------------
' Procedure : KillHwndProcess
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Terminate a process based on its Windows Handle (Hwnd)
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' lHWnd     : Windows Handle number (Hwnd)
'
' Usage:
' ~~~~~~
' Call KillHwndProcess(Application.hWnd)
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2018-09-09              Initial Website Release
'---------------------------------------------------------------------------------------
Public Function KillHwndProcess(lHWnd As Long)
' https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-process
    On Error GoTo Error_Handler
    Dim oWMI                  As Object
    Dim oProcesses            As Object
    Dim oProcess              As Object
    Dim lProcessId            As Long
    Dim sSQL                  As String
    Const sComputer = "."
 
    'Retrieve the ProcessId associated with the specified Hwnd
    Call GetWindowThreadProcessId(lHWnd, lProcessId)
 
    'Iterate through the matching ProcessId processes and terminate
    '   each one.
    Set oWMI = GetObject("winmgmts:\" & sComputer & "\root\cimv2")
    sSQL = "SELECT * FROM Win32_Process WHERE ProcessId=" & lProcessId
    Set oProcesses = oWMI.ExecQuery(sSQL)
    For Each oProcess In oProcesses
        oProcess.Terminate
    Next
 
Error_Handler_Exit:
    On Error Resume Next
    If Not oProcess Is Nothing Then Set oProcess = Nothing
    If Not oProcesses Is Nothing Then Set oProcesses = Nothing
    If Not oWMI Is Nothing Then Set oWMI = Nothing
    Exit Function
 
Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: KillHwndProcess" & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

立即输出window:

ie_browser PID: 593524
--- runIE1 ---
ie_browser PID:  593524 
ie_browser Navigated...
ie_browser should have parsed and rendered the page at this time
--- runIE1 ---
--- runIE1 ---
ie_browser PID:  593524 
ie_browser Navigated...
ie_browser should have parsed and rendered the page at this time
--- runIE1 ---
--- runIE1 ---
ie_browser PID:  593524 
ie_browser Navigated...
ie_browser should have parsed and rendered the page at this time
--- runIE1 ---
--- runIE2 ---
ie_browser2 PID:  397928 
ie_browser2 Navigated...
ie_browser2 Start wait...
waitForIE - Popup occurred
waitForIE - ie PID: 397928
waitForIE - quit IE, set to nothing...
waitForIE - Terminated IE process: 397928
ie_browser2 End wait...
--- runIE2 ---
--- runIE1 ---

文件 bad.html(删除 good.html 的警报)

<html>
<head>
<title>Bad file</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> 
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>

<body>
Bad!
<script type="text/javascript">
    alert("Hello World!");
</script>
</body>
</html>

在快速测试中,这种 Windows API 方法似乎对我有用:

Option Explicit

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
           (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, _
            ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
        (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
                                                        lParam As Any) As Long

Private Const BM_CLICK As Integer = &HF5
Private Const WM_ACTIVATE As Integer = &H6
Private Const WA_ACTIVE As Integer = 1

Sub TestAPI()

    Dim IE As InternetExplorer, el, hwnd As Long, btn As Long
    Set IE = New InternetExplorerMedium
    
    'open a test document with an auto-alert (using your example)
    With IE
        .Visible = False
        .navigate "http://localhost/testpages/Bad.html"
    End With
    
    Application.Wait Now + TimeSerial(0, 0, 3)
    
    'find the alert
    hwnd = FindWindow("#32770", "Message from webpage")
    
    If hwnd <> 0 Then
        btn = FindWindowEx(hwnd, 0, "Button", "OK") 'find the OK button
        If btn <> 0 Then ' button found
            ' activate the button on dialog first or it
            '   may not acknowledge a click msg on first try
            SendMessage btn, WM_ACTIVATE, WA_ACTIVE, 0
            ' send button a click message
            SendMessage btn, BM_CLICK, 0, 0
        Else
            MsgBox "button not found!"
        End If
    End If
    
    IE.Visible = True 'make visible to ensure the prompt is gone...
    
End Sub