使用 New-Object -ComObject "HTMLFile" 解析本地 HTML 文件损坏了吗?
Parsing local HTML file using New-Object -ComObject "HTMLFile" broken?
我已经 运行 设置了 6 个月的密码过期脚本,没有任何问题。该脚本将读取静态 html 文件并更改内存中的一些内容,然后将向所有密码过期的用户发送一封 html 电子邮件。
脚本似乎在过去一周左右的时间里坏了。经过进一步调查,我已将错误缩小到 Powershell 应该创建新 ComObject 并将该 HTML 文件写入 ComObject 的部分。
我现在得到错误:
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
当我 运行 以下代码行时会发生上述错误:
$html = New-Object -ComObject "HTMLFile"
$src = Get-Content -path "./passwordreminder.html" -Raw
$html.write($src)
当我调用 write()
方法时出现错误。
自从过去 6 个月以来它一直运行良好,我能想到的唯一改变的是我的 powershell 版本。我相信当我开始 运行ning 这个脚本时我使用的是 Powershell v4.0,但是在 Windows 更新之后我猜 Powershell 现在是 v5.0。见下文:
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
脚本在 Windows Server 2012 R2 OS 上 运行ning OS。
有人有什么想法吗?
我在其他问题中看到一些建议调用 ComObject 上的 IHTMLDocument2_write()
方法,但是当我尝试调用它时,此方法不存在。
更新:
我能够确认这在我的 Powershell 版本中 确实损坏。
我只能在具有相同 OS 但低于 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
并且代码按预期工作。
有人知道这个新版本的 Powershell 有什么用吗?
您可以尝试使用 Internet Explorer COM 对象:
$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
不过,我没有 PowerShell v5,所以无法测试。如果 HTMLFile 损坏,这也可能是。
如果需要重复运行,可以在外循环中调用 Navigate()
方法(以及等待它完成页面加载的循环)。
$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
}
通过添加路径引用并指定对象类型解决
Add-Type -Path "C:\Program Files (x86)\Microsoft.NET\Primary Interop Assemblies\Microsoft.mshtml.dll"
$webpage = New-Object mshtml.HTMLDocumentClass
这是完整的代码
$url = 'http://website'
$outFile = 'C:\content.txt'
$showCount = 10;
[net.httpwebrequest]$httpwebrequest = [net.webrequest]::create($url)
[net.httpWebResponse]$httpwebresponse = $httpwebrequest.getResponse()
$reader = new-object IO.StreamReader($httpwebresponse.getResponseStream())
$html = $reader.ReadToEnd()
$reader.Close()
Add-Type -Path "C:\Program Files (x86)\Microsoft.NET\Primary Interop Assemblies\Microsoft.mshtml.dll"
$webpage = New-Object mshtml.HTMLDocumentClass
$webpage.IHTMLDocument2_write($html)
$topicElements = $webpage.documentElement.getElementsByClassName('topic')
$time = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
$content = '[www.hkgalden.com] [' + $time + '] '
$i = 0;
foreach ($topicElement in $topicElements) {
$titleElement = $topicElement.getElementsByClassName('title')[0].getElementsByTagName('a')[0]
$title = $titleElement.innerText
$usernameElement = $topicElement.getElementsByClassName('username')[0]
$username = $usernameElement.innerText
$content += $username + ': ' + $title + ' // '
$i++
if ($i -gt $showCount) {
break
}
}
#$content
$content | Out-File -Encoding utf8 $outFile
如果您提供 UCS-2 字节数组而不是字符串,这似乎可以正常工作:
$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)
}
此代码片段通过 Add-Type -AssemblyName cmdlet 添加 .NET Framework 的 mshtml.HTMLDocumentClass 类型。
Add-Type -AssemblyName "Microsoft.mshtml"
$html = New-Object -ComObject "HTMLFile"
$svc = Get-Service | Select-Object Name, Status | ConvertTo-Html
$svc | Out-File -FilePath .\report.html -Force
$htmlFile = Get-Content -Path .\report.html -Raw
$html.IHTMLDocument2_write($htmlFile)
$html 变量包含 "HTMLFile" 对象引用及其所有方法和属性。
我已经 运行 设置了 6 个月的密码过期脚本,没有任何问题。该脚本将读取静态 html 文件并更改内存中的一些内容,然后将向所有密码过期的用户发送一封 html 电子邮件。
脚本似乎在过去一周左右的时间里坏了。经过进一步调查,我已将错误缩小到 Powershell 应该创建新 ComObject 并将该 HTML 文件写入 ComObject 的部分。
我现在得到错误:
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
当我 运行 以下代码行时会发生上述错误:
$html = New-Object -ComObject "HTMLFile"
$src = Get-Content -path "./passwordreminder.html" -Raw
$html.write($src)
当我调用 write()
方法时出现错误。
自从过去 6 个月以来它一直运行良好,我能想到的唯一改变的是我的 powershell 版本。我相信当我开始 运行ning 这个脚本时我使用的是 Powershell v4.0,但是在 Windows 更新之后我猜 Powershell 现在是 v5.0。见下文:
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
脚本在 Windows Server 2012 R2 OS 上 运行ning OS。
有人有什么想法吗?
我在其他问题中看到一些建议调用 ComObject 上的 IHTMLDocument2_write()
方法,但是当我尝试调用它时,此方法不存在。
更新:
我能够确认这在我的 Powershell 版本中 确实损坏。
我只能在具有相同 OS 但低于 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
并且代码按预期工作。
有人知道这个新版本的 Powershell 有什么用吗?
您可以尝试使用 Internet Explorer COM 对象:
$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
不过,我没有 PowerShell v5,所以无法测试。如果 HTMLFile 损坏,这也可能是。
如果需要重复运行,可以在外循环中调用 Navigate()
方法(以及等待它完成页面加载的循环)。
$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
}
通过添加路径引用并指定对象类型解决
Add-Type -Path "C:\Program Files (x86)\Microsoft.NET\Primary Interop Assemblies\Microsoft.mshtml.dll"
$webpage = New-Object mshtml.HTMLDocumentClass
这是完整的代码
$url = 'http://website'
$outFile = 'C:\content.txt'
$showCount = 10;
[net.httpwebrequest]$httpwebrequest = [net.webrequest]::create($url)
[net.httpWebResponse]$httpwebresponse = $httpwebrequest.getResponse()
$reader = new-object IO.StreamReader($httpwebresponse.getResponseStream())
$html = $reader.ReadToEnd()
$reader.Close()
Add-Type -Path "C:\Program Files (x86)\Microsoft.NET\Primary Interop Assemblies\Microsoft.mshtml.dll"
$webpage = New-Object mshtml.HTMLDocumentClass
$webpage.IHTMLDocument2_write($html)
$topicElements = $webpage.documentElement.getElementsByClassName('topic')
$time = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
$content = '[www.hkgalden.com] [' + $time + '] '
$i = 0;
foreach ($topicElement in $topicElements) {
$titleElement = $topicElement.getElementsByClassName('title')[0].getElementsByTagName('a')[0]
$title = $titleElement.innerText
$usernameElement = $topicElement.getElementsByClassName('username')[0]
$username = $usernameElement.innerText
$content += $username + ': ' + $title + ' // '
$i++
if ($i -gt $showCount) {
break
}
}
#$content
$content | Out-File -Encoding utf8 $outFile
如果您提供 UCS-2 字节数组而不是字符串,这似乎可以正常工作:
$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)
}
此代码片段通过 Add-Type -AssemblyName cmdlet 添加 .NET Framework 的 mshtml.HTMLDocumentClass 类型。
Add-Type -AssemblyName "Microsoft.mshtml"
$html = New-Object -ComObject "HTMLFile"
$svc = Get-Service | Select-Object Name, Status | ConvertTo-Html
$svc | Out-File -FilePath .\report.html -Force
$htmlFile = Get-Content -Path .\report.html -Raw
$html.IHTMLDocument2_write($htmlFile)
$html 变量包含 "HTMLFile" 对象引用及其所有方法和属性。