0

To start this off, Slenium WebDriver is not an option due to it requiring approval to run and I am locked to the Windows environment. In addition, the site tool I am accessing is old and only works in Internet Exploder.

This script worked just "okay" in PowerShell 5.1 but I need to convert it to 7.1. I'm running into a few strange issues regarding the IE Com object and how I interact with it. I get the error code 0x800A01B6, which references "Execution Stopped" and before Microsoft nerfed the error output, the full error was:

Exception from HRESULT: 0x800A01B6
At line:1 char:1
+ ($ie.document.getElementsByName("email") |select -first 1).value = $u ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : OperationStopped: (:) [], NotSupportedException
+ FullyQualifiedErrorId : System.NotSupportedException

I previously bypassed this error by re-hooking the IE COM window through the Windows Shell, but now it's come back to haunt me. I can't seem to execute a single command series without re-hooking the window each time. It seems like a minor complaint, but would there be an issue re-hooking each time I wanted to run a JavaScript method? (Other than the script now getting more complex as I can't store ANYTHING in reference)

I tried the double-ampersand operator to see if it would help to queue commands in a single line, but no dice.

I have also tried getElementByID, but it gives me the same error message. I made sure to implement a method that waits for the browser to not be busy, but that seems to be where I'm failing. I have had this issue happen in the past when automating IE. While the program sleeps to wait for the browser to finish loading, something happens to where I'd have to re-hook the window.

I know that IE COM has some event triggers that can be registered, but I don't think I can do that from within PowerShell... (E.g. Onload)

Below is the relevant code and what I can provide:

$ie_list = [PSCustomObject]@{
    url1 = "http://test-url1"
    title1 = "INTER Net 1"
    hwnd1 = $false

    url2 = "http://test-url2"
    title2 = "INTER Net 2"
    hwnd2 = $false

    url3 = "http://test-url3"
    title3 = "INTER Net 3"
    hwnd3 = $false

    # First three internet, second three intranet

    url4 = "http://test-url4"
    title4 = "INTRA Net 1"
    hwnd4 = $false

    url5 = "http://test-url5"
    title5 = "INTRA Net 2"
    hwnd5 = $false

    url6 = "http://test-url6"
    title6 = "INTRA Net 3"
    hwnd6 = $false
}
for ($num = 1; $num -le 6; $num++)
{
    $ie_com = new-object -comobject InternetExplorer.Application 
    $ie_list."hwnd$num" = $ie_com.HWND
    $hWnd = $ie_list."hwnd$num"
    Write-Host "`nConnecting using URL $($ie_list."url$num")"
    $ie_com.navigate2($ie_list."url$num")
    Write-Host "Successfully created window $num with HWND $hWnd."
    # We can wait for the page to finish loading all the way!
    # If we did not release and re-hook, the object would null itself. Not cool.
    # Manual release COM object
    $ie_com = $false
    # Re-integrate using Shell COM methods. This typically works a little better but not by much. It's still Internet Explorer.
    $app = New-Object -Com shell.application
    $ie_com = $app.Windows() | Where-Object {$_.HWND -match $hWnd -and $_.Name -match "Internet Explorer"} | Select-Object -Last 1
    # Making sure it's still an object!
    if ($ie_com -isnot [object] -or $ie_com.Document -isnot [object])
    {
        # Something done messed up.
        Write-Host -ForegroundColor Red "FAILED to connect to Com Object $num at $hWnd."
        # Pause
        Continue
    }
    $doc = $ie_com.Document
    
    Write-Host "`tReconnected COM object $num at $hWnd"

    if ($ie_com.Busy -or $ie_com.ReadyState -ne 4)
    {
        Write-Host -NoNewline "Browser busy. Waiting"
        $timeout = 0
        while ($ie_com.Busy -and $ie_com.ReadyState -ne 4 -and $timeout -le 20)
        {
            Start-Sleep -Milliseconds 50
            Write-Host -NoNewline "."
            $timeout++
        }
        # Have to manually add linebreak
        Write-Host " "
    }
    $u_fill = $false
    $p_fill = $false

    # In upgraded to Powershell 7, IHTML3 is not required for reliability when it was previously used in 5.1
    # It's freakin weird. MS sucks.
    Try {
        $link = $doc.getElementsByName("j_username") | Select-Object -First 1 && $link.focus() && ($link.value = $env:USERNAME)
        $u_fill = $true
    }
    Catch {
        Write-Host -ForegroundColor Red "`tAn error occurred."
        Write-Host -ForegroundColor Red "`t$_`n`t$($_.ErrorDetails)"
    }

    if ($false -eq $u_fill)
    {
        Write-Host -ForegroundColor Red "`tUnable to fill in Username. Skipping..."
        Continue
    }

    Write-Host "`tUsername in window $num filled in"

    Try {
        $link = $doc.getElementsByName("j_password") | Select-Object -First 1 && $link.focus() && ($link.value = $pw)
        $p_fill = $true
    }
    Catch {
        Write-Host -ForegroundColor Red "`tAn error occurred."
        Write-Host -ForegroundColor Red "`t$_`n`t$($_.ErrorDetails)"
    }

    if ($false -eq $p_fill)
    {
        Write-Host -ForegroundColor Red "`tUnable to fill in Password. Skipping..."
        Continue
    }

    Write-Host "`tPassword in window $num filled in"

    # No issues with this window. Pull the lever, Kronk!

    $link = $doc.getElementsByName("input") | Where-Object { $_.value -eq "SIGN IN" } && $link.click()
    Write-Host -ForegroundColor Green "`tLogin button in window $num clicked"
}

Output

Connecting using URL http://test-url1
Successfully created window 1 with HWND 921560.
        Reconnected COM object 1 at 921560
Browser busy. Waiting..
        An error occurred.
        You cannot call a method on a null-valued expression.

        Unable to fill in Username. Skipping...

Connecting using URL http://test-url2
Successfully created window 2 with HWND 1314278.
        Reconnected COM object 2 at 1314278
Browser busy. Waiting..
        An error occurred.
        0x800A01B6

        Unable to fill in Username. Skipping...
5
  • Why don't you use UI Automation (learn.microsoft.com/en-us/dotnet/framework/ui-automation/…)? Otherwise, do you have the same kind of reproducing code with a real site on the internet so we can try to reproduce what you see? Commented Nov 28, 2021 at 8:17
  • 1
    You are creating multiple COM objects inside the loop, but never quit and release them from memory. This can lead to a build-up of unsused blocks of memory eventually leading to sometimes weird problems. Setting $ie_com = $false for instance does NOT release the object.
    – Theo
    Commented Nov 28, 2021 at 12:27
  • How do I fully release the object?
    – MBernat
    Commented Nov 28, 2021 at 23:01
  • I think I found how to release the COM object by using [System.Runtime.InteropServices.Marshal]::ReleaseComObject($ie_com) but this doesn't seem to change the behavior. I looked at the UI automation and wasn't sure where to begin. Upon further reading, I think my object is being garbage collected? Default Marshaling Behavior
    – MBernat
    Commented Nov 29, 2021 at 0:02
  • I know in PowerShell 5.1 I had an issue with GetElement methods, and went around that by using IHTML3_getElementsByName() but this method is not available in 7.1
    – MBernat
    Commented Nov 29, 2021 at 8:30

1 Answer 1

0

Previously, in Powershell 5.1 I had to get elements using IHTML3_getElementsByName() method, but PowerShell 7 does not seem to have access to these same methods. The more I test, I am realizing this is an IE security issue.

I have looked into Windows UI Automation, but it seems the module can't be directly imported into PowerShell.

As a test, I saved the HTML form locally on my hard disk, and when I opened it, PowerShell was able to fill out the form successfully. Most solutions to this are years-old, referencing the IHTML3 methods I mentioned, and adding the site as a Trusted site in IE.

Thankfully, the tool requiring Internet Explorer is being retired. Hopefully before we are forced to fully update to PowerShell 7.

1
  • 1
    Thanks for posting the solution for this issue. You can mark your answer as an accepted answer after 48 hrs, when it is available to mark. It can help other community members in future in similar kind of issues. Thanks for your understanding. Commented Nov 29, 2021 at 10:21

Not the answer you're looking for? Browse other questions tagged or ask your own question.