0

I have this piece of a PowerShell script:

$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()

On Windows 10 it works fine but on Windows Server 2016 for lines 4, 5 and 6 I get the error:

You cannot call a method on a null-valued expression.

I'm pretty sure it has something to do with extra security in Windows Server preventing IE from running Javascript. There must be some way to dial back that security to be more on par with Windows 10 so that this script can run properly, but I can't figure out how. I've turned off IE Enhanced Security Configuration and ensured that Active Scripting is enabled. Aside from that I don't know what else to do.

2 Answers 2

1

This may have something to do with Internet Explorer 'protected mode'. If IE indeed is in protected mode, the $IE object gets lost after the .Navigate() command and any action after that will result in the error You cannot call a method on a null-valued expression.

To handle this, here's a function that tries to reconnect the $IE object.

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"
}

Hope that helps

Sign up to request clarification or add additional context in comments.

5 Comments

That didn't work as hoped. Using your function I'm getting the same errors and then some.
The error text is too long to post here. I posted the full text on Pastebin. [link]pastebin.com/LKS7yvBm
@Joel Kolb, I try to make a test with your sample script on Windows Server 2016. I find that i am getting similar error like yours. I try to change the script related settings to enable or prompt helps me to solve the issue. You can try to make a test with each option and try to enable it one by one to check which specific option solves the issue.
@JoelKolb Can you turn off Protected mode manually using these instructions ? I cannot test myself as I don't have server 2016
Protected mode is already disabled. Tried through the GUI and through Group Policy.
0

A coworker helped me figure this out. It doesn't have anything to do with IE security, at least not as far as anything that hasn't already been covered. The problem is 'Microsoft.mshtml.dll' is missing from the GAC. It won't be present on a clean install of Windows Server but installing something like Office or Visual Studio will add it. However, i would bet that most people running a Windows Server wouldn't want to do that just for the sake of getting this working. What I did was copy the following folder/file structure from my Windows 10 PC to my server, closed out all instances of PowerShell and ISE and when I opened PowerShell again and ran the script everything worked.

C:\Windows\assembly\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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.