PowerShell中try/catch无法捕获System.Net.NetworkInformation.Ping异常排查
以下是PowerShell 5.1中使用System.Net.NetworkInformation.Ping发送带超时Ping请求的Test-Ping函数:
Function Test-Ping { # Use this instead of Test-Connection to allow increasing timeout Param( [Parameter(Mandatory=$true)][String]$ComputerName, [Parameter()][Int32]$Timeout = 15, [Parameter()][Int32]$Retries = 5 ) $Ping = New-Object System.Net.NetworkInformation.Ping while ($Retries -ne 0) { try { $PingReply = $Ping.Send($ComputerName, $Timeout * 1000) if ($PingReply.Status -eq 'Success') { return $true } } catch [System.Management.Automation.MethodInvocationException] { # Sometimes get error 'Exception calling "Send" with "2" argument(s): "An exception occurred during a Ping request."' - treat this as a failure return $false } $Retries-- } $Ping.Dispose() return $false }
该函数多数情况下运行正常,但偶尔第9行的$Ping.Send会抛出如下异常:
Exception calling "Send" with "2" argument(s): "An exception occurred during a Ping request." At <script path, redacted> + $PingReply = $Ping.Send($ComputerName, $Timeout * 1000) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : PingException
希望将该随机网络问题导致的异常视为Ping失败并忽略,但try/catch循环未生效。已尝试捕获System.Management.Automation.MethodInvocationException和System.Net.NetworkInformation.PingException,请问脚本存在什么问题导致异常仍被抛出?
核心问题是PowerShell 5.1中,调用.NET方法抛出的非终止异常默认不会被try/catch捕获。当$Ping.Send抛出异常时,PowerShell默认将其标记为非终止错误,不会触发catch块逻辑。
要解决这个问题,需要在调用方法时添加-ErrorAction Stop参数,强制将非终止错误转为终止错误,让try/catch能够正常捕获。同时建议优先捕获底层实际抛出的System.Net.NetworkInformation.PingException,保留MethodInvocationException作为兜底。
另外原函数存在资源泄漏风险:如果在try块内通过return $true提前退出,会跳过$Ping.Dispose(),导致Ping对象未被正确释放。可以用try/finally块确保资源无论成功失败都能释放。
修改后的函数示例:
Function Test-Ping { Param( [Parameter(Mandatory=$true)][String]$ComputerName, [Parameter()][Int32]$Timeout = 15, [Parameter()][Int32]$Retries = 5 ) $Ping = New-Object System.Net.NetworkInformation.Ping try { while ($Retries -ne 0) { try { # 强制将非终止错误转为终止错误,确保catch能捕获 $PingReply = $Ping.Send($ComputerName, $Timeout * 1000) -ErrorAction Stop if ($PingReply.Status -eq 'Success') { return $true } } catch [System.Net.NetworkInformation.PingException] { # 捕获Ping底层异常,继续重试 } catch [System.Management.Automation.MethodInvocationException] { # 捕获包装的调用异常,继续重试 } $Retries-- } return $false } finally { # 确保Ping对象始终被释放 $Ping.Dispose() } }
内容的提问来源于stack exchange,提问作者Minkus

