C# Powershell->exe->Powershell写入主机控制台日志记录失败

C# Powershell->exe->Powershell写入主机控制台日志记录失败,c#,powershell,logging,C#,Powershell,Logging,TLDW:为了支持特定的流程,我需要从powershell实例/模块启动一些c magic的已知安全可执行文件,然后在退出之前需要启动并执行powershell脚本 Powershell入口点 { 已知良好Exe { 要做的工作 } } 现在,理想情况下,这将全部从单个控制台实例运行,以便所有输出都易于查看。当使用powershell.Streams时,exe->powershell日志记录都可以正常工作,并且与预期一样。。。。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#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");
                    }
                }
            }
        }
    }
}