服务器升级至2022后PowerShell脚本运行异常求助
问题:PowerShell脚本在Windows Server 2022上运行异常,邮件内容为空且报错
我有一段脚本曾在Windows Server 2012 R2连接代理上稳定运行,将连接代理升级至2022后,脚本运行异常:虽能发送邮件,但邮件内容为空。手动运行脚本时出现如下错误:
You cannot call a method on a null-valued expression.
At C:\PS-Scripts\rdstats.ps1:28 char:5
$ob = [PSCustomObject]@{~~~~~~~~~~~~~~~~~~~~~~~~
- CategoryInfo : InvalidOperation: (:) [], RuntimeException
- FullyQualifiedErrorId : InvokeMethodOnNull
我推测是$ob = [PSCustomObject]@{部分被判定为空,但将以下循环代码单独在PowerShell窗口运行时无报错:
foreach ( $rdhost in $rdhosts ) { $rdp = Test-NetConnection -ComputerName $rdhost.SessionHost -Port 3389 $ob = [PSCustomObject]@{ CollectionName = $rdhost.CollectionName SessionHost = $rdhost.SessionHost InCluster = $rdhost.NewConnectionAllowed RemotePort = $rdp.RemotePort PortAlive = $rdp.TcpTestSucceeded PingSucceeded = $rdp.PingSucceeded PingReplyDetails = $rdp.PingReplyDetails.RoundtripTime.ToString() + " ms" } $objs += $ob }
另外,在2012R2机器上针对2022连接代理运行该脚本可成功,因此问题可能并非出在连接代理,而是2012R2与2022的PowerShell版本差异(4.0 vs 5.1)。请问为何PowerShell 5.1会判定$ob = [PSCustomObject]@{为空?以下是完整脚本:
###BEGIN TODO CONFIG EMAIL #$Username = 'user@domain.com' #$Password = '' $emailTo = "user@domain.com" $bccTo = "dg@domain.com" $emailFrom = "status@domain.com" ###END TODO CONFIG EMAIL #$pass = ConvertTo-SecureString -AsPlainText $Password -Force # Users you password securly #$MySecureCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Username,$pass Import-Module RemoteDesktop $rdhosts = Get-RDSessionHost -CollectionName "RD Collection 1" $objs = @() foreach ( $rdhost in $rdhosts ) { $rdp = Test-NetConnection -ComputerName $rdhost.SessionHost -Port 3389 $ob = [PSCustomObject]@{ CollectionName = $rdhost.CollectionName SessionHost = $rdhost.SessionHost InCluster = $rdhost.NewConnectionAllowed RemotePort = $rdp.RemotePort PortAlive = $rdp.TcpTestSucceeded PingSucceeded = $rdp.PingSucceeded PingReplyDetails = $rdp.PingReplyDetails.RoundtripTime.ToString() + " ms" } $objs += $ob } $str = Out-String -InputObject $objs -Width 100 Send-MailMessage -From $emailFrom -To $emailTo -BCC $bccTo -Subject "RD Collection TS Audit" -Body $str -SmtpServer mail.domain.com -Port 25
问题原因与解决方法
错误提示的InvokeMethodOnNull并非是[PSCustomObject]@{...}本身为空,而是构建对象时,某属性调用了空值的方法。具体问题出在$rdp.PingReplyDetails.RoundtripTime.ToString()这一行:
- PowerShell 4.0(Windows Server 2012 R2)中,即使Ping失败,
$rdp.PingReplyDetails会返回默认对象(比如RoundtripTime为0),不会为null; - PowerShell 5.1(Windows Server 2022)中,Ping失败时
$rdp.PingReplyDetails直接为null,此时调用.RoundtripTime就会触发空值调用错误,导致$ob对象创建失败,最终$objs数组为空,邮件内容无数据。
单独运行循环时无报错,大概率是测试环境中所有主机都能Ping通,$rdp.PingReplyDetails不为null,未触发问题。
解决步骤
- 添加空值判断:在访问
$rdp.PingReplyDetails.RoundtripTime前先检查是否为null,避免空对象调用:
PingReplyDetails = if ($rdp.PingReplyDetails) { $rdp.PingReplyDetails.RoundtripTime.ToString() + " ms" } else { "Ping Failed" }
- 优化数组构建:PowerShell中
+=扩展数组效率低,建议直接将循环输出的对象赋值给变量:
$objs = foreach ( $rdhost in $rdhosts ) { $rdp = Test-NetConnection -ComputerName $rdhost.SessionHost -Port 3389 [PSCustomObject]@{ CollectionName = $rdhost.CollectionName SessionHost = $rdhost.SessionHost InCluster = $rdhost.NewConnectionAllowed RemotePort = $rdp.RemotePort PortAlive = $rdp.TcpTestSucceeded PingSucceeded = $rdp.PingSucceeded PingReplyDetails = if ($rdp.PingReplyDetails) { $rdp.PingReplyDetails.RoundtripTime.ToString() + " ms" } else { "Ping Failed" } } }
- 验证运行:修改后重新执行脚本,即使有主机Ping失败,也能正常生成
PSCustomObject,$objs不会为空,邮件内容可正常展示。
内容的提问来源于stack exchange,提问作者SubnetMask
相关产品推荐
相关产品推荐

