【问题标题】:Using PowerShell to run Javascript in Internet Explorer on Windows Server使用 PowerShell 在 Windows Server 上的 Internet Explorer 中运行 Javascript
【发布时间】:2019-04-29 21:52:57
【问题描述】:

我有这段 PowerShell 脚本:

$IE = New-Object -com InternetExplorer.Application
$IE.Navigate($URL)
While ($IE.ReadyState -Ne 4) {Start-Sleep -Milliseconds 100}
$IE.Document.ParentWindow.ExecScript("var JSIEVariable = new XMLSerializer().serializeToString(document);", "javascript")
$Obj = $IE.Document.ParentWindow.GetType().InvokeMember("JSIEVariable", 4096, 
$Null, $IE.Document.parentWindow, $Null)
$HTML = $Obj.ToString()
$IE.Quit()

在 Windows 10 上它工作正常,但在 Windows Server 2016 上的第 4、5 和 6 行我收到错误:

您不能在空值表达式上调用方法。

我很确定这与 Windows Server 中防止 IE 运行 Javascript 的额外安全性有关。必须有某种方法来回拨安全性,使其与 Windows 10 更加相提并论,以便该脚本可以正常运行,但我不知道如何。我已关闭 IE 增强安全配置并确保启用了活动脚本。除此之外,我不知道还能做什么。

【问题讨论】:

    标签: javascript powershell internet-explorer windows-server windows-server-2016


    【解决方案1】:

    这可能与 Internet Explorer 的“保护模式”有关。 如果 IE 确实处于保护模式,$IE 对象在.Navigate() 命令之后会丢失,之后的任何操作都会导致错误You cannot call a method on a null-valued expression.

    为了解决这个问题,这里有一个尝试重新连接 $IE 对象的函数。

    function Connect-InternetExplorer {
        # creates a new 'InternetExplorer.Application' object and navigates to the given url.
        # If IE is in 'protected mode', the function tries to reconnect using the window handle
        [CmdletBinding()]
        param(
            [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true, Position = 0)]
            $Url,
    
            [switch]$Visible
        )
        # test if Internet Explorer is in 'Protected Mode'
        # see https://www.lifewire.com/how-to-disable-protected-mode-in-internet-explorer-2624507
        $ieProtectedMode = ((Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\3' -Name '2500').2500 -ne 3)
    
        $ie = New-Object -ComObject 'InternetExplorer.Application' -ErrorAction SilentlyContinue
        $ie.Visible = [bool]$Visible
        $ie.Silent = $true
        $hwnd = $ie.Hwnd
        $ie.Navigate($Url)
    
        if ($ieProtectedMode) {
            $oldErrorActionPreference = $ErrorActionPreference
            $ErrorActionPreference = 'Stop'
    
            $objShell = New-Object -ComObject 'Shell.Application'
            Start-Sleep -Milliseconds 100
            try {
                $ie = $objShell.Windows() | Where-Object {$_.HWND -eq $Hwnd}
                $ie.Visible = [bool]$Visible
            }
            catch {
                # sometimes the Shell.Application does not find the window quickly enough,
                Start-Sleep -Milliseconds 500
                try {
                    $ie = $objShell.Windows() | Where-Object {$_.HWND -eq $Hwnd}
                    $ie.Visible = [bool]$Visible
                }
                catch {
                    Write-Warning "Could not connect to the InternetExplorer ComObject."
                }
            }
            finally {
                $ErrorActionPreference = $oldErrorActionPreference
                # clean up the Com object
                [System.Runtime.Interopservices.Marshal]::ReleaseComObject($objShell) | Out-Null
                [System.GC]::Collect()
                [System.GC]::WaitForPendingFinalizers()
            }
        }
    
        if (!$ie) { return $null }
        while ($ie.Busy -eq $true) { Start-Sleep -Milliseconds 50 }
        return $ie
    }
    
    # this replaces the first three lines of your original code
    $IE = Connect-InternetExplorer -Url $URL
    if ($IE) {
        $IE.Document.ParentWindow.ExecScript("var JSIEVariable = new XMLSerializer().serializeToString(document);", "javascript")
        $Obj = $IE.Document.ParentWindow.GetType().InvokeMember("JSIEVariable", 4096, $Null, $IE.Document.parentWindow, $Null)
        $HTML = $Obj.ToString()
        $IE.Quit()
    
        # clean up the $IE Com object
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($IE) | Out-Null
        [System.GC]::Collect()
        [System.GC]::WaitForPendingFinalizers()
    }
    else {
        Write-Warning "Could not connect Internet Explorer"
    }
    

    希望有帮助

    【讨论】:

    • 这并没有像希望的那样工作。使用你的函数我得到了同样的错误,然后是一些。
    • 错误文本太长,无法在此处发布。我在 Pastebin 上发布了全文。 [链接]pastebin.com/LKS7yvBm
    • @Joel Kolb,我尝试在 Windows Server 2016 上使用您的示例脚本进行测试。我发现我遇到了与您类似的错误。我尝试更改脚本相关设置以启用或提示帮助我解决问题。您可以尝试对每个选项进行测试,并尝试逐个启用它以检查哪个特定选项可以解决问题。
    • @JoelKolb 您可以使用这些instructions 手动关闭保护模式吗?我无法测试自己,因为我没有服务器 2016
    • 保护模式已被禁用。通过 GUI 和组策略进行了尝试。
    【解决方案2】:

    一位同事帮我解决了这个问题。它与 IE 安全性没有任何关系,至少与尚未涵盖的任何内容无关。问题是 GAC 中缺少“Microsoft.mshtml.dll”。它不会出现在 Windows Server 的全新安装中,但安装 Office 或 Visual Studio 之类的东西会添加它。但是,我敢打赌,大多数运行 Windows Server 的人不会仅仅为了让它正常工作而这样做。我所做的是将以下文件夹/文件结构从我的 Windows 10 PC 复制到我的服务器,关闭 PowerShell 和 ISE 的所有实例,当我再次打开 PowerShell 并运行脚本时一切正常。

    C:\Windows\程序集\GAC\Microsoft.mshtml C:\Windows\assembly\GAC\Microsoft.mshtml\7.0.3300.0__b03f5f7f11d50a3a C:\Windows\assembly\GAC\Microsoft.mshtml\7.0.3300.0__b03f5f7f11d50a3a\Microsoft.mshtml.dll C:\Windows\assembly\GAC\Microsoft.mshtml\7.0.3300.0__b03f5f7f11d50a3a__AssemblyInfo__.ini

    【讨论】:

      猜你喜欢
      • 2023-03-31
      • 1970-01-01
      • 2013-03-30
      • 2010-11-29
      • 2022-11-05
      • 2020-05-01
      • 2010-10-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多