简体   繁体   English

在 Pipeline.Invoke 抛出后,在 C# 中捕获 Powershell output

[英]Capturing Powershell output in C# after Pipeline.Invoke throws

I'm running a Powershell test script from a C# application.我正在从 C# 应用程序运行 Powershell 测试脚本。 The script can fail due to a bad cmdlet which causes pipe.Invoke() to throw an exception.该脚本可能会由于错误的 cmdlet 而失败,这会导致 pipe.Invoke() 引发异常。

I'm able to capture all the information I need about the exception, but I'd like to be able to display the script's output up to that point.我能够捕获我需要的有关异常的所有信息,但我希望能够显示脚本的 output 到那时为止。 I haven't had any luck since results appears to be null when an exception is thrown.我没有任何运气,因为抛出异常时结果似乎是 null 。

Is there something I'm missing?有什么我想念的吗? Thanks!谢谢!

m_Runspace = RunspaceFactory.CreateRunspace();
m_Runspace.Open();
Pipeline pipe = m_Runspace.CreatePipeline();
pipe.Commands.AddScript(File.ReadAllText(ScriptFile));
pipe.Commands.Add("Out-String");
try {
   results = pipe.Invoke();
}
catch (System.Exception)
{
   m_Runspace.Close();
   // How can I get to the Powershell output that comes before the exception?
}

Not sure if this is helpful. 不确定这是否有用。 I am guessing you are running V1. 我猜你正在运行V1。 This V2 approach doesn't throw and prints the result: 此V2方法不会抛出并打印结果:

Hello World
67 errors

string script = @"
  'Hello World'
  ps | % {
    $_.name | out-string1
  }
";

PowerShell powerShell = PowerShell.Create();

powerShell.AddScript(script);
var results = powerShell.Invoke();

foreach (var item in results)
{
  Console.WriteLine(item);
}

if (powerShell.Streams.Error.Count > 0)
{
  Console.WriteLine("{0} errors", powerShell.Streams.Error.Count);
}

The solution I ended up using was to implement our own PSHost to handle PowerShell's output. 我最终使用的解决方案是实现我们自己的PSHost来处理PowerShell的输出。 The initial information for this came from http://community.bartdesmet.net/blogs/bart/archive/2008/07/06/windows-powershell-through-ironruby-writing-a-custom-pshost.aspx in the "Building a custom PS host" section. 最初的信息来自“建筑物”中的http://community.bartdesmet.net/blogs/bart/archive/2008/07/06/windows-powershell-through-ironruby-writing-a-custom-pshost.aspx自定义PS主机“部分。

In my case it did require using a custom PSHostRawUserInterface as well. 在我的情况下,它确实需要使用自定义PSHostRawUserInterface。

Here's the quick overview of what was done. 以下是对所做工作的快速概述。 I've only listed the function I actually implimented, but there's many that are just contain throw new NotImplementedException(); 我只列出了我实际上已经实现的功能,但是有许多只是包含throw new NotImplementedException();

private class myPSHost : PSHost
{
   (Same as what the above link mentions)
}
private class myPSHostUI : PSHostUserInterface
{
   private myPSHostRawUI rawui = new myPSHostRawUI();

   public override void Write // all variations
   public override PSHostRawUserInterface RawUI { get { return rawui; } }

}
private class myPSHostRawUI : PSHostRawUserInterface
{
   public override ConsoleColor ForegroundColor
   public override ConsoleColor BackgroundColor
   public override Size BufferSize
}

I have the same problem. 我也有同样的问题。 The easiest way to get output when pipe.Invoke() throws an exception is to use Invoke(IEnumerable input, IList output) pipe.Invoke()抛出异常时获取输出的最简单方法是使用Invoke(IEnumerable输入,IList输出)

Example shows how to get all output, error, waning etc. in the correct order 示例显示如何以正确的顺序获得所有输出,错误,减弱等

PowerShell script PowerShell脚本

Write-Output "Hello world" 
Write-Error "Some error"
Write-Warning "Some warning"
throw "Some exception"

C# C#

List<string> RunLog = new List<string>(); 

using (System.Management.Automation.PowerShell psInstance = System.Management.Automation.PowerShell.Create())

{
    psInstance.AddScript(_Script);

psInstance.Streams.Error.DataAdded += (sender, args) =>
{
    ErrorRecord err = ((PSDataCollection<ErrorRecord>)sender)[args.Index];
    RunLog.Add($"ERROR: {err}");
};

psInstance.Streams.Warning.DataAdded += (sender, args) =>
{
    WarningRecord warning = ((PSDataCollection<WarningRecord>)sender)[args.Index];
    RunLog.Add($"WARNING: {warning}");
};

... etc ...

var result = new PSDataCollection<PSObject>();
result.DataAdded += (sender, args) =>
{
    PSObject output = ((PSDataCollection<PSObject>)sender)[args.Index];
    RunLog.Add($"OUTPUT: {output}");
};

try
{
    psInstance.Invoke(null, result);
}
catch(Exception ex)
{
    RunLog.Add($"EXCEPTION: {ex.Message}");
}                                                
}

If you want to access the objects before the exception and keep using a pipeline instead of a Powershell instance you can use Pipeline.Output.DataReady or Pipeline.Error.DataReady method .如果您想在异常之前访问对象并继续使用管道而不是 Powershell 实例,您可以使用Pipeline.Output.DataReadyPipeline.Error.DataReady 方法 Here is an example redirecting all streams to default output (using MergeMyResults ) and listening to the default pipeline output:这是一个将所有流重定向到默认 output(使用MergeMyResults )并监听默认管道 output 的示例:

Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Output.DataReady += (sender, eventArgs) =>
{
    PSObject obj = ((PipelineReader<PSObject>)sender).Read();
    Console.WriteLine("OUTPUT: "+obj.ToString());
};
pipeline.Commands.AddScript(script);
pipeline.Commands[pipeline.Commands.Count-1]
    .MergeMyResults(PipelineResultTypes.All, PipelineResultTypes.Output);
pipeline.Invoke();
runspace.Close();
}
catch (Exception ex)
{
    Console.WriteLine("ERROR: "+ ex.Message);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM