You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PowerShell使用BeginGetHostEntry异步获取主机名报错求助

解决PowerShell异步DNS解析的Runspace未绑定异常

问题重现

尝试使用[System.Net.Dns]::BeginGetHostEntry()异步解析IP地址的主机名,运行脚本时触发未处理异常,PowerShell进程直接退出:

An error has occurred that was not properly handled. Additional information is shown below. The PowerShell process will exit.
Unhandled exception. System.Management.Automation.PSInvalidOperationException: There is no Runspace available to run scripts in this thread. You can provide one in the DefaultRunspace property of the System.Management.Automation.Runspaces.Runspace type. The script block you attempted to invoke was: param($ar)

原脚本代码如下:

$GetHostEntryFinished = [System.Threading.ManualResetEvent]::new($false);
    
class ResolveState
{
  hidden $hostname;
  hidden $resolvedIPs;
    
  ResolveState($host1)
  {
    $this.hostname = $host1;

    # add getter and setter for the member IPs:
    $this | Add-Member -Name IPs -MemberType ScriptProperty -Value {
      return $this.resolvedIPs
    } -SecondValue {
      param($value)
      $this.resolvedIPs = $value
    }

    # add getter and setter for the member host:
    $this | Add-Member -Name host -MemberType ScriptProperty -Value {
      return $this.hostname
    } -SecondValue {
      param($value)
      $this.hostname = $value
    }
  }
}

# Record the IPs in the state object for later use.
function GetHostEntryCallback($ar)
{
  $ioContext = $ar.AsyncState;
  $ioContext.IPs = [System.Net.Dns]::EndGetHostEntry($ar);
  $GetHostEntryFinished.Set();
}

# Determine the Internet Protocol (IP) addresses for this host asynchronously.
function DoGetHostEntryAsync($hostname)
{
  $ioContext = [ResolveState]::new($hostname)

  $GetHostEntryFinished.Reset();
  [System.Net.Dns]::BeginGetHostEntry($ioContext.host, `
    [AsyncCallback]$function:GetHostEntryCallback, $ioContext);

  # Wait here until the resolve completes (the callback calls .Set())
  $GetHostEntryFinished.WaitOne();

  Write-Output("EndGetHostEntry($($ioContext.host)) returns:");

  foreach ($address in $ioContext.IPs.AddressList)
  {
    Write-Output($address);
  }
}

# main():
DoGetHostEntryAsync "192.168.1.10"

问题根源

异步回调函数GetHostEntryCallback会在后台线程执行,而PowerShell的函数/脚本块必须绑定到一个Runspace(PowerShell的执行上下文)才能运行。后台线程默认没有关联任何Runspace,因此触发PSInvalidOperationException。

修复方案

核心修改点

  1. 在主线程提前保存当前的默认Runspace
  2. 在回调函数中,显式使用保存的Runspace来执行需要PowerShell上下文的操作
  3. 简化ResolveState类的属性定义(PowerShell类支持直接声明属性,无需手动添加ScriptProperty)

修复后的完整代码

using namespace System.Threading
using namespace System.Management.Automation.Runspaces

# 初始化等待事件
$GetHostEntryFinished = [ManualResetEvent]::new($false)
# 主线程保存当前Runspace
$currentRunspace = [Runspace]::DefaultRunspace

# 简化ResolveState类,直接声明属性
class ResolveState {
    [string]$Hostname
    [System.Net.IPHostEntry]$ResolvedIPs

    ResolveState([string]$host1) {
        $this.Hostname = $host1
    }
}

# 异步回调函数,使用保存的Runspace执行逻辑
function GetHostEntryCallback($ar) {
    $ioContext = $ar.AsyncState
    # 使用主线程的Runspace执行需要PowerShell上下文的操作
    $currentRunspace.Invoke({
        param($context, $asyncResult)
        $context.ResolvedIPs = [System.Net.Dns]::EndGetHostEntry($asyncResult)
        $script:GetHostEntryFinished.Set()
    }, $ioContext, $ar)
}

# 异步解析函数
function DoGetHostEntryAsync([string]$hostname) {
    $ioContext = [ResolveState]::new($hostname)

    $GetHostEntryFinished.Reset()
    # 启动异步解析
    [System.Net.Dns]::BeginGetHostEntry($ioContext.Hostname, [AsyncCallback]$function:GetHostEntryCallback, $ioContext)

    # 等待解析完成
    $GetHostEntryFinished.WaitOne()

    Write-Output "EndGetHostEntry($($ioContext.Hostname)) returns:"
    foreach ($address in $ioContext.ResolvedIPs.AddressList) {
        Write-Output $address
    }
}

# 执行解析
DoGetHostEntryAsync "192.168.1.10"

额外优化建议

如果不需要手动管理异步回调的底层细节,可以使用PowerShell原生的异步模式,结合.NET TaskAPI简化代码,避免Runspace问题:

function GetHostEntryAsync([string]$hostname) {
    $task = [System.Net.Dns]::GetHostEntryAsync($hostname)
    $task.Wait()
    $result = $task.Result

    Write-Output "GetHostEntryAsync($hostname) returns:"
    foreach ($address in $result.AddressList) {
        Write-Output $address
    }
}

GetHostEntryAsync "192.168.1.10"

这种方式利用.NET的异步API,PowerShell会自动处理Runspace绑定,代码更简洁且不易出错。

内容的提问来源于stack exchange,提问作者jacouh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 16:32:47