如何通过System.Management.Automation传递PowerShell远程命令的警告与详细流
解决C#远程调用PowerShell时无法接收Verbose/Warning等流的问题
我之前也踩过这个坑,核心问题在于远程PowerShell会话默认不会自动把所有非输出类流(Verbose/Warning/Information等)转发回本地客户端——哪怕你在脚本块里设置了$VerbosePreference这类变量,也得显式配置流转发规则,同时调整输出缓冲模式才能让事件实时触发。
下面是具体的修复方案和修改后的代码:
关键修复点
- 给
Invoke-Command添加RemoteStreamOptions.ForwardAll参数,强制远程会话把所有类型的流都转发到本地 - 设置命令的
OutputBufferingMode为None,避免流被缓冲,确保DataAdded事件能实时触发 - 通过
SessionStateProxy提前配置远程会话的偏好变量,确保流输出规则在脚本执行前就生效
修改后的完整代码
namespace ConsoleApp2 { using System; using System.Linq; using System.Management.Automation; using System.Management.Automation.Runspaces; using System.Security; using System.Action; class Program { static void Main(string[] args) { var script = @" Write-Host "" ------- $env:computername ------- "" Write-Warning ""Write warning directly in warn script"" Write-Error ""Write error directly in warn script"" Write-Verbose ""Write verbose directly in warn script"" Write-Host ""Write host directly in warn script"" Write-Information ""Write Information in warn script"" "; // 远程虚拟机调用 Call(script, command => { SecureString password = new SecureString(); "admin".ToCharArray().ToList().ForEach(c => password.AppendChar(c)); command.Parameters.Add("Credential", new PSCredential("admin", password)); command.Parameters.Add("VmName", new[] { "190104075839" }); }); // 本地调用对比 Call(script); } private static void Call(string script, Action<Command> addtionalCommandSetup = null) { using (var shell = PowerShell.Create()) { // 订阅所有流的日志事件 shell.Streams.Information.DataAdded += LogProgress<InformationRecord>; shell.Streams.Warning.DataAdded += LogProgress<WarningRecord>; shell.Streams.Error.DataAdded += LogProgress<ErrorRecord>; shell.Streams.Verbose.DataAdded += LogProgress<VerboseRecord>; shell.Streams.Debug.DataAdded += LogProgress<DebugRecord>; var command = new Command("Invoke-Command"); command.Parameters.Add("ScriptBlock", ScriptBlock.Create(script)); // 核心修复1:开启所有远程流的转发 command.Parameters.Add("RemoteStreamOptions", RemoteStreamOptions.ForwardAll); // 核心修复2:关闭输出缓冲,实时传递流数据 command.OutputBufferingMode = OutputBufferingMode.None; // 提前配置远程会话的流偏好规则 shell.Runspace.SessionStateProxy.SetVariable("VerbosePreference", ActionPreference.Continue); shell.Runspace.SessionStateProxy.SetVariable("WarningPreference", ActionPreference.Continue); shell.Runspace.SessionStateProxy.SetVariable("InformationPreference", ActionPreference.Continue); addtionalCommandSetup?.Invoke(command); shell.Commands.AddCommand(command); shell.Invoke(); Console.WriteLine("=== 命令执行完成 ==="); Console.ReadKey(); } } private static void LogProgress<T>(object sender, DataAddedEventArgs e) { var data = (sender as PSDataCollection<T>)[e.Index]; Console.WriteLine($"[{typeof(T).Name}] {Convert.ToString(data)}"); } } }
额外说明
- RemoteStreamOptions.ForwardAll:这是解决远程流不传递的核心设置,它会告诉远程PowerShell把Verbose、Warning、Debug、Information等所有类型的流都转发回本地客户端。
- OutputBufferingMode.None:默认情况下PowerShell会缓冲输出,直到命令执行完毕才一次性返回结果。设置为
None后,流数据会实时发送,你的日志事件就能及时触发。 - SessionStateProxy配置偏好变量:虽然可以在脚本块里设置
$VerbosePreference,但通过SessionStateProxy提前配置远程会话的变量,能确保规则在脚本执行前就生效,避免脚本内设置可能出现的延迟或不生效问题。
这样修改后,无论是通过-VmName连接虚拟机,还是-ComputerName连接网络计算机,所有的Verbose、Warning等流都能正常回传到本地日志了。
内容的提问来源于stack exchange,提问作者Michal Wozniak
相关产品推荐
相关产品推荐

