Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing local HTML file using New-Object -ComObject "HTMLFile" broken?

I have been running a password expiration script for the pass 6 months without any issue. The script will read in a static html file and change around some of the content in memory and then an html email will be sent to all users who have expiring passwords.

The script seems to have broke in the past week or so. Upon further investigation I've narrowed down the errors to the section where Powershell is supposed to create a new ComObject and write that HTML file to the ComObject.

I now get the error :

No coercion operator is defined between types 'System.Array' and 'System.String'
At line:1 char:1
+ $html.write($source);
+ ~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (:) [], InvalidOperationException
    + FullyQualifiedErrorId : System.InvalidOperationException

The above error happens when I run below lines of code :

$html = New-Object -ComObject "HTMLFile"
$src = Get-Content -path "./passwordreminder.html" -Raw
$html.write($src)

When I invoke the write() method I get the error.

Since its been working fine for the last 6 months, the only thing that I can think of that has changed is my version of powershell. I believe when I started running this script I was using Powershell v4.0, but after Windows Updates I guess Powershell is now at v5.0. See below :

Name                           Value
----                           -----
PSVersion                      5.0.10105.0
WSManStackVersion              3.0
SerializationVersion           1.1.0.1
CLRVersion                     4.0.30319.34209
BuildVersion                   10.0.10105.0
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion      2.3

The script is running on Windows Server 2012 R2 OS.

Anyone have any ideas?

I've seen some suggestions in other questions calling to use the IHTMLDocument2_write() method on the ComObject, but this method doesn't exist when I try to invoke it.

Update :

I was able to confirm that this is INDEED BROKEN in my version of Powershell.

I was just able to test the same code on a different server with the same OS but below version of Powershell :

Name                           Value
----                           -----
PSVersion                      4.0
WSManStackVersion              3.0
SerializationVersion           1.1.0.1
CLRVersion                     4.0.30319.34014
BuildVersion                   6.3.9600.17090
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0}
PSRemotingProtocolVersion      2.2

And the code works as expected.

Anybody know what can be used in this new version of Powershell?

like image 349
kevin Avatar asked Jun 20 '15 18:06

kevin


2 Answers

This seems to work properly if you provide a UCS-2 byte array instead of a string:

$html = New-Object -ComObject "HTMLFile"
$src = Get-Content -path "./passwordreminder.html" -Raw
$src = [System.Text.Encoding]::Unicode.GetBytes($src)
try
{
    # This works in PowerShell 4
    $html.IHTMLDocument2_write($src)
}
catch
{
    # This works in PowerShell 5
    $html.write($src)
}
like image 179
jedigo Avatar answered Sep 29 '22 00:09

jedigo


You could try with an Internet Explorer COM object:

$ie = New-Object -COM 'InternetExplorer.Application'

$ie.Navigate("file://$($PWD.Path)/passwordreminder.html")
do {
  Start-Sleep -Milliseconds 100
} until ($ie.ReadyState -eq 4)

# do stuff

I don't have PowerShell v5, though, so I can't test. If HTMLFile is broken, this might be as well.

You can call the Navigate() method (and the loop waiting for it to complete loading the page) in an outer loop if you need to run it repeatedly.

$ie = New-Object -COM 'InternetExplorer.Application'

foreach (...) {
  $ie.Navigate("file://$($PWD.Path)/passwordreminder.html")
  do {
    Start-Sleep -Milliseconds 100
  } until ($ie.ReadyState -eq 4)

  # do stuff
}
like image 43
Ansgar Wiechers Avatar answered Sep 29 '22 02:09

Ansgar Wiechers