C# Powershell->exe->Powershell写入主机控制台日志记录失败
TLDW:为了支持特定的流程,我需要从powershell实例/模块启动一些c magic的已知安全可执行文件,然后在退出之前需要启动并执行powershell脚本 Powershell入口点 { 已知良好Exe { 要做的工作 } } 现在,理想情况下,这将全部从单个控制台实例运行,以便所有输出都易于查看。当使用powershell.Streams时,exe->powershell日志记录都可以正常工作,并且与预期一样。。。。powershell工作区中的写主机都显示在控制台中,我得到了所需的所有信息 powerShell.Streams.Information.DataAdded+=日志消息 引入外部powershell模块时会出现问题。这是必需的,因为运行它的父进程和执行环境是powershell。从powershell实例中启动整个堆栈后,我将从外部powershell和exe获取控制台日志记录。但是内部powershell模块中的所有写主机都消失了 我尝试过禁用流重定向和其他一些事情,但这并没有以我希望的方式解决。我希望有人知道是否有办法让它工作,因为它解决了很多问题,如果它只是想 PowerShell外部:C# Powershell->exe->Powershell写入主机控制台日志记录失败,c#,powershell,logging,C#,Powershell,Logging,TLDW:为了支持特定的流程,我需要从powershell实例/模块启动一些c magic的已知安全可执行文件,然后在退出之前需要启动并执行powershell脚本 Powershell入口点 { 已知良好Exe { 要做的工作 } } 现在,理想情况下,这将全部从单个控制台实例运行,以便所有输出都易于查看。当使用powershell.Streams时,exe->powershell日志记录都可以正常工作,并且与预期一样。。。。powershell工作区中的写主机都显示在控制台中,我得到了所需的
$C#Exe = Join-Path $PSScriptRoot $C#ExePath
$C#Args = @()
Write-Host "Hello world" # will show up
& $C#Exe $C#Args
C Exe代码:
public static void Main(string[] args)
{
Console.WriteLine("Hello world"); #Will show up
var powerShell = PowerShell.Create().AddScript("PowerShellInner.ps1");
powerShell.Streams.Information.DataAdded += LogMessage<InformationRecord>;
powerShell.Streams.Warning.DataAdded += LogMessage<WarningRecord>;
powerShell.Streams.Error.DataAdded += LogMessage<ErrorRecord>;
powerShell.Streams.Verbose.DataAdded += LogMessage<VerboseRecord>;
powerShell.Streams.Debug.DataAdded += LogMessage<DebugRecord>;
StringBuilder resultString = new StringBuilder();
foreach (dynamic item in powerShell.Invoke().ToList())
{
resultString.AppendLine(item.ToString());
}
Console.WriteLine(resultString.ToString());
}
private static void LogMessage<T>(object sender, DataAddedEventArgs e)
{
var data = (sender as PSDataCollection<T>)[e.Index];
Console.WriteLine($"[{typeof(T).Name}] {Convert.ToString(data)}");
}
更新日期:2020年1月22日
我不能完全解释为什么你会经历你所看到的,但是下面的代码是有效的
执行PowerShellOuter.ps1的输出
代码
注:
您的c程序没有显示任何操作输入参数的代码,
所以我没有对任何输入参数建模
您错误地使用了AddScript方法。它需要脚本的文本,而不是脚本名称
下面的代码假定c exe和两个PS脚本位于同一文件夹中
在PowerShellInner.ps1中,使用写入输出。写入主机不会将数据输出到PowerShell Objectflow引擎,而是,顾名思义,直接将数据写入主机,而不会将任何内容发送到PowerShell引擎,以便稍后在管道中转发到命令。看
PowerShellOuter.ps1
PowerShellInner.ps1
PowerShellExecutionSample.exe的代码
2020年1月21日的原始答复
更新您的问题以分解代码很有帮助-谢谢
我认为你有两个问题:
1修改您的c程序以获取来自PowerShell内部的流,并使您的c程序从PowerShell内部输出流重新发出数据。下面是一篇我曾经攻克同样难题的Microsoft博客:
2修改PowerShell Outer以获取来自c程序的流。这里有一篇博客文章似乎破解了这个难题:。其核心是从PowerShell外部执行以下操作:
$C#Exe = Join-Path $PSScriptRoot $C#ExePath
$C#Args = @()
Write-Host "Hello world" # will show up
& $C#Exe $C#Args
cmd/c XXX.exe^>log.txt 2^>^&1
注意:^实际上是反勾号这是否回答了您的问题?不,也许要澄清一下,我真正的问题是,为什么使用外部powershell运行此命令会导致Cps流重定向失败?因此,您有一个powershell脚本调用用c编写的exe,而c程序反过来执行powershell脚本?我很困惑,因为当你说Powershell时,我不确定你是指执行exe的PS,还是指执行exe的PSexecuting@Nova您对调用堆栈的总结完全正确。因此,您在问题中显示的PS是外部PS,是吗?所以我已经在使用powershell流来获取输出,但是我继续从您的第一个链接的c代码中实现了PSDataCollection,行为没有改变。然后我尝试使用第二个建议,但这也没有改变任何事情。此外,如果所有这些日志都是实时的,而不需要在查看之前写入txt,那将是非常棒的。对我来说,两个powershell实例同时运行确实是个问题。这可能是两个不同的psexe无法通信吗?@Ethancris:请参阅我的工作代码更新答案这看起来是rc//您错误地使用了AddScript方法。它需要脚本的文本,而不是脚本name@EthanCriss当您的PowerShell内部脚本使用写主机而不是写输出时,一切正常吗?
cls
$CSharpExe = Join-Path $PSScriptRoot "PowerShellExecutionSample.exe"
Write-Host "Hello from PowerShellOuter.ps1"
&$CSharpExe
pause
Write-Output "Hello from PowerShellInner.ps1"
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Management.Automation;
namespace PowerShellExecutionSample
{
class Program
{
static void Main(string[] args)
{
PowerShellExecutor t = new PowerShellExecutor();
t.ExecuteSynchronously();
}
}
/// <summary>
/// Provides PowerShell script execution examples
/// </summary>
class PowerShellExecutor
{
public void ExecuteSynchronously()
{
using (PowerShell PowerShellInstance = PowerShell.Create())
{
//You mis-used the AddScript method. It needs the text of the script, not the script name
string scriptText = File.ReadAllText(string.Format(@"{0}\PowerShellInner.ps1", Environment.CurrentDirectory));
PowerShellInstance.AddScript(scriptText);
Collection <PSObject> PSOutput = PowerShellInstance.Invoke();
// loop through each output object item
foreach (PSObject outputItem in PSOutput)
{
// if null object was dumped to the pipeline during the script then a null
// object may be present here. check for null to prevent potential NRE.
if (outputItem != null)
{
Console.WriteLine(outputItem.BaseObject.ToString() + "\n");
}
}
}
}
}
}