【问题标题】:Parsing local HTML file using New-Object -ComObject "HTMLFile" broken?使用 New-Object -ComObject "HTMLFile" 解析本地 HTML 文件损坏?
【发布时间】:2018-08-04 05:12:56
【问题描述】:

在过去的 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 版本。我相信当我开始运行这个脚本时,我使用的是 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 操作系统上运行。

有人有什么想法吗?

我在其他要求在 ComObject 上使用 IHTMLDocument2_write() 方法的问题中看到了一些建议,但是当我尝试调用它时,该方法不存在。

更新:

我能够在我的 Powershell 版本中确认这是确实损坏了

我只能在具有相同操作系统但低于 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 可以使用什么吗?

【问题讨论】:

  • 您可以尝试使用 IE 对象:$ie = New-Object -COM 'InternetExplorer.Application'; $ie.Navigate("file://$($PWD.Path)/passwordreminder.html"。不过,我没有 PowerShell v5,所以无法测试。如果HTMLFile 坏了,这可能也是。
  • 在工作服务器和非工作服务器上,您能否将违规行更改为$html.Write;,运行它并比较结果?这将输出Write 方法的哪些重载在$html 对象上可用。也许有一个新的可用,而 PowerShell 现在正试图调用“错误”的。
  • @Ansgar 感谢它仍然适用于 v5.0!!!!我能够使用这种方法重写部分脚本。但是,这种方法肯定不理想。该脚本将为每个用户编辑内存中的 HTML 文档,该用户的密码即将到期。这意味着我必须为每个人启动一个新的 IE 进程。在某些日子里,这可能是多达 20-30 多个进程。我可以通过运行stop-process 来杀死其中的一些来缓解这种情况,但与以前的工作解决方案相比,开销仍然是巨大的。我将使用它,直到找到更好的解决方法。谢谢!!!!
  • @BACON 我不确定我是否按照你告诉我的去做。你能给我你希望我在两台服务器上运行的代码吗?这可能有助于我更好地理解。我能想到的唯一想法是您希望我做的是将$html 对象导入Get-Member 以查看方法是否看起来不同。是这个意思吗?
  • @Ansgar 实际上我可以重复使用 $ie 对象,这样我就不必每次都启动一个新进程。这样就行了!抱歉,在我想更多之前快速回复。

标签: html powershell powershell-5.0


【解决方案1】:

如果您提供 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)
}

【讨论】:

  • 我试过了,但抛出了异常:使用“1”参数调用“write”的异常:“类型不匹配。
  • 我已将其更改为使用我的工作代码(适用于 PowerShell 4 和 5)。我猜你没有使用 PowerShell 5?
【解决方案2】:

您可以尝试使用 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
}

【讨论】:

  • Powershell v4.0 : MemberType : Method OverloadDefinitions : {void write (SAFEARRAY(Variant)) TypeNameOfValue : System.Management.Automation.PSMethod Value : void write (SAFEARRAY( Variant)) 名称:写入 IsInstance:True Powershell v5.0: MemberType:方法 OverloadDefinitions:{void write (SAFEARRAY(Variant))} TypeNameOfValue:System.Management.Automation.PSMethod 值:void write ( SAFEARRAY(Variant)) 名称:写 IsInstance:真
【解决方案3】:

此代码 sn-p 通过 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”对象引用及其所有方法和属性。

【讨论】:

  • 添加程序集是不必要的,这对 COM 对象没有任何作用。但是使用.IHTMLDocument2_write() 而不是.write() 可以解决问题。所有其他尝试都导致“类型不匹配”错误。
【解决方案4】:

通过添加路径引用并指定对象类型解决

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

【讨论】:

  • 谢谢。你用这行拯救了我的一天:$topicElements = $webpage.documentElement.getElementsByClassName('topic')
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-17
  • 2015-07-15
  • 2013-03-31
  • 1970-01-01
  • 2013-08-09
  • 2015-02-14
相关资源
最近更新 更多