2012-06-13 66 views
4

我有一个类似但不同的问题,如How to redirect Powershell output from a script run by TaskScheduler and override default width of 80 characters如何从Powershell重定向输出

我有一个很早以前写的自定义安装程序框架。在它内部,我可以执行“任务”。我最近不得不添加一个任务来执行PowerShell脚本。现在,即使任务是用C#编写的,我也无法直接在PowerShell脚本中调用这些命令。不幸的是,这不在场。

简而言之,我想从C#调用PowerShell可执行文件并将其输出重定向回我的应用程序。这是我到目前为止已经完成:

我可以使用下面的代码(由我创建了一个测试项目)成功地调用的PowerShell:

string powerShellExeLocation = null; 

    RegistryKey localKey = Registry.LocalMachine; 

    RegistryKey subKey = localKey.OpenSubKey(@"SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine"); 
    powerShellExeLocation = subKey.GetValue("ApplicationBase").ToString(); 

    if (!Directory.Exists(powerShellExeLocation)) 
    throw new Exception("Cannot locate the PowerShell dir."); 

    powerShellExeLocation = Path.Combine(powerShellExeLocation, "powershell.exe"); 

    if (!File.Exists(powerShellExeLocation)) 
    throw new Exception("Cannot locate the PowerShell executable."); 

    string scriptLocation = Path.Combine(Environment.CurrentDirectory, "PowerShellScript.ps1"); 

    if (!File.Exists(scriptLocation)) 
    throw new Exception("Cannot locate the PowerShell script."); 

    ProcessStartInfo processInfo = new ProcessStartInfo(); 
    processInfo.Verb = "runas"; 
    processInfo.UseShellExecute = false; 
    processInfo.RedirectStandardOutput = true; 
    processInfo.RedirectStandardOutput = true; 
    processInfo.WorkingDirectory = Environment.CurrentDirectory; 

    processInfo.FileName = powerShellExeLocation; 
    processInfo.Arguments = "-NoLogo -OutputFormat Text -NonInteractive -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + scriptLocation + "\" "; 

    Process powerShellProcess = new Process(); 
    powerShellProcess.StartInfo = processInfo; 
    powerShellProcess.OutputDataReceived += new DataReceivedEventHandler(powerShellProcess_OutputDataReceived); 
    powerShellProcess.ErrorDataReceived += new DataReceivedEventHandler(powerShellProcess_ErrorDataReceived); 

    powerShellProcess.Start(); 

    while (!powerShellProcess.HasExited) 
    { 
    Thread.Sleep(500); 
    } 

我的处理程序重定向输出从来不被称为:

void powerShellProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e) 

void powerShellProcess_OutputDataReceived(object sender, DataReceivedEventArgs e) 

我相当确信脚本运行宁。现在我只有一个输出PATH内容的测试脚本。

$a = $env:path; $a.Split(";") 

我在PowerShell实例中测试了它并正确输出。

我能做些什么来获得PowerShell输出重定向到我的C#代码?我不想依靠我将执行的脚本来处理他们自己的日志记录。我宁愿收集他们的输出,并在框架的日志记录机制中处理它。

编辑 实现我留下了奇怪的循环等待退出该代码示例。我有过的过程 “WaitForExit”:

powerShellProcess.WaitForExit(); 

编辑
使用从@MiniBill的建议。我想出了以下代码snippit:

powerShellProcess.Start(); 
while (!powerShellProcess.HasExited) 
{ 
    string line; 
    while (!string.IsNullOrEmpty((line = powerShellProcess.StandardOutput.ReadLine()))) 
    this.LogInfo("PowerShell running: " + line); 
} 

这将处理输出正常。它切断了我的台词,在80个字符:(,但我可以忍受的,除非有人能提供建议,以解决这个问题!

UPDATE 一个解决方案

/* 
    * The next few lines define the process start info for the PowerShell executable. 
    */ 
ProcessStartInfo processInfo = new ProcessStartInfo(); 
processInfo.Verb = "runas"; 
processInfo.UseShellExecute = false; 
processInfo.RedirectStandardError = true; 
processInfo.RedirectStandardOutput = true; 
processInfo.WorkingDirectory = Environment.CurrentDirectory; 

//this FileName was retrieved earlier by looking in the registry for key "HKLM\SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine" and the value for "ApplicationBase" 
processInfo.FileName = powerShellExeLocation; 

//if we're going to use script arguments build up the arguments from the process start correctly. 
if (!string.IsNullOrEmpty(this.ScriptArguments)) 
    processInfo.Arguments = "-NoLogo -NonInteractive -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + ScriptLocation + "\" '" + ScriptArguments + "'"; 
else 
    processInfo.Arguments = "-NoLogo -NonInteractive -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + ScriptLocation + "\""; 

//create the Process object, set the start info, and start the process 
Process powerShellProcess = new Process(); 
powerShellProcess.StartInfo = processInfo; 

powerShellProcess.Start(); 

/* 
    * While the PowerShell process hasn't exited do the following: 
    * 
    * Read from the current position of the StandardOutput to the end by each line and 
    * update the tasks progress with this information 
    * 
    * Read from the current position of StandardError to the end by each line, update 
    * the task's progress with this information and set that we have an error. 
    * 
    * Sleep for 250 milliseconds (1/4 of a sec) 
    */ 
bool isError = false; 
while (!powerShellProcess.HasExited) 
{ 
    string standardOuputLine, standardErrorLine; 
    while (!string.IsNullOrEmpty((standardOuputLine = powerShellProcess.StandardOutput.ReadLine()))) 
    { 
    this.UpdateTaskProgress(standardOuputLine); 
    } 


    while (!string.IsNullOrEmpty((standardErrorLine = powerShellProcess.StandardError.ReadLine()))) 
    { 
    this.UpdateTaskProgress(standardErrorLine); 
    isError = true; 
    } 

    Thread.Sleep(250); 
} 

/* 
    * Now that the process has completed read to the end of StandardOutput and StandardError. 
    * Update the task progress with this information. 
    */ 

string finalStdOuputLine = powerShellProcess.StandardOutput.ReadToEnd(); 
string finalStdErrorLine = powerShellProcess.StandardError.ReadToEnd(); 

if (!string.IsNullOrEmpty(finalStdOuputLine)) 
    this.UpdateTaskProgress(finalStdOuputLine); 

if (!string.IsNullOrEmpty(finalStdErrorLine)) 
{ 
    this.UpdateTaskProgress(finalStdErrorLine); 
    isError = true; 
} 

// there was an error during the run of PowerShell that was output to StandardError. This doesn't necessarily mean that 
// the script error'd but there was a problem. Throw an exception for this. 
if (isError) 
    throw new Exception(string.Format(CultureInfo.InvariantCulture, "Error during the execution of {0}.", this.ScriptLocation)); 
+0

'$ Host.UI.RawUI'具有一个'BufferSize'和'WindowSize'属性,即使它没有写入任何可见的东西,它仍然可以工作。 – Shibumi

+0

@Kiquenet我已经提供了我的解决方案t我在我的“任务”结构中使用了帽子,它通过脚本调用PowerShell可执行文件并记录输出。 “UpdateTaskProgress”方法只是处理记录到我的日志记录结构和更新我的用户界面。 –

+0

我敢肯定,Windows也不会允许您在管理员/非管理员安全边界上重定向标准输入/输出/错误。你必须找到一种不同的方式来获得以管理员身份运行的程序的输出 - 参考:stackoverflow.com/a/8690661 – Kiquenet

回答

4

你想使用Process.StandardOutput属性这是一个可以打开以获得程序输出的流

+0

当控制台输出发生时,是否没有办法让我得到警报(事件)?看来,如果我使用StandardOutput属性,我将不得不等待流程退出。 –

+0

你可以产生一个新的线程,当它找到一些输出时会引发一个事件 – miniBill

+0

你认为在我发布的HasExited上进行等待循环是可以接受的吗?我应该睡在那个圈内还是让它飞? –