我正在尝试创建一个控制台应用程序,它可以包含几乎任何打开控制台的应用程序。
一些简单的事实:
到目前为止,这是我所拥有的,除了最后几行之外,一切正常:
ProcessStartInfo startInfo = new ProcessStartInfo("someBatchThingy.cmd");
startInfo.WorkingDirectory = Environment.CurrentDirectory;
startInfo.UseShellExecute = false;
Process process = new Process();
process.StartInfo = startInfo;
process.Start();
Thread.Sleep(5000);
Stream s = Console.OpenStandardOutput();
byte[] stopCommand = new byte[] { 115, 116, 111, 112, 13 };
s.Write(stopCommand, 0, stopCommand.Length);
process.WaitForExit();
由于性能很重要,我真的想重新分配流程输出的位置,而不是手动将数据从隐藏的控制台传输到包装器控制台。
有没有人知道怎么做/如果可能的话?
答案 0 :(得分:2)
据我所知,如果不通过应用程序挖掘数据,就无法实现这一目标。
执行此操作的代码如下:
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
namespace TestApp
{
internal static class Program
{
[MTAThread]
public static void Main(string[] args)
{
const string fileName = @"..\..\..\ChildConsoleApp\bin\Debug\ChildConsoleApp.exe";
// Fires up a new process to run inside this one
var process = Process.Start(new ProcessStartInfo
{
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
FileName = fileName
});
// Depending on your application you may either prioritize the IO or the exact opposite
const ThreadPriority ioPriority = ThreadPriority.Highest;
var outputThread = new Thread(outputReader) { Name = "ChildIO Output", Priority = ioPriority};
var errorThread = new Thread(errorReader) { Name = "ChildIO Error", Priority = ioPriority };
var inputThread = new Thread(inputReader) { Name = "ChildIO Input", Priority = ioPriority };
// Set as background threads (will automatically stop when application ends)
outputThread.IsBackground = errorThread.IsBackground
= inputThread.IsBackground = true;
// Start the IO threads
outputThread.Start(process);
errorThread.Start(process);
inputThread.Start(process);
// Demonstrate that the host app can be written to by the application
process.StandardInput.WriteLine("Message from host");
// Signal to end the application
ManualResetEvent stopApp = new ManualResetEvent(false);
// Enables the exited event and set the stopApp signal on exited
process.EnableRaisingEvents = true;
process.Exited += (e, sender) => { stopApp.Set(); };
// Wait for the child app to stop
stopApp.WaitOne();
// Write some nice output for now?
Console.WriteLine();
Console.Write("Process ended... shutting down host");
Thread.Sleep(1000);
}
/// <summary>
/// Continuously copies data from one stream to the other.
/// </summary>
/// <param name="instream">The input stream.</param>
/// <param name="outstream">The output stream.</param>
private static void passThrough(Stream instream, Stream outstream)
{
byte[] buffer = new byte[4096];
while (true)
{
int len;
while ((len = instream.Read(buffer, 0, buffer.Length)) > 0)
{
outstream.Write(buffer, 0, len);
outstream.Flush();
}
}
}
private static void outputReader(object p)
{
var process = (Process)p;
// Pass the standard output of the child to our standard output
passThrough(process.StandardOutput.BaseStream, Console.OpenStandardOutput());
}
private static void errorReader(object p)
{
var process = (Process)p;
// Pass the standard error of the child to our standard error
passThrough(process.StandardError.BaseStream, Console.OpenStandardError());
}
private static void inputReader(object p)
{
var process = (Process)p;
// Pass our standard input into the standard input of the child
passThrough(Console.OpenStandardInput(), process.StandardInput.BaseStream);
}
}
}
我的孩子应用程序代码如下:
using System;
namespace ChildConsoleApp
{
internal static class Program
{
public static void Main()
{
Console.WriteLine("Hi");
string text; // Echo all input
while ((text = Console.ReadLine()) != "stop")
Console.WriteLine("Echo: " + text);
Console.WriteLine("Stopped.");
}
}
}
与往常一样,这有一些开销,但在任何非常重要的应用程序中,它可能都是微不足道的。
如果您要发送大量数据,并且希望避免超出必要的刷新次数,则可以将缓冲区大小从4KB增加到适合您的任何数据。
答案 1 :(得分:1)
老问题,但刚刚提出以下解决类似问题。
public class ConsoleAppWrapper
{
private Process _process;
public ConsoleAppWrapper(string exeFilename)
{
// Start the Player console app up with hooks to standard input/output and error output
_process = new Process()
{
StartInfo = new ProcessStartInfo(exeFilename)
{
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true
}
};
_process.Start();
}
public StreamReader StandardOutput => _process.StandardOutput;
public StreamReader StandardError => _process.StandardError;
public StreamWriter StandardInput => _process.StandardInput;
~ConsoleAppWrapper()
{
// When I used this class to help with some SpecFlow feature tests of a console app I found it wasn't destroying the console window correctly when assertions failed
// using a destructor ensures the windows is destroyed when the wrapper goes out of scope.
Kill();
}
public void Kill()
{
Dispose();
}
}
使用此类,您可以在没有控制台应用程序的情况下控制交互。
var c = new ConsoleAppWrapper("App.exe");
Assert.That(c.StandardOutput.ReadLine(), Is.StringContaining("Enter some data:"));
c.StandardInput.WriteLine("SOME DATA\n");
Assert.That(c.StandardOutput.ReadLine(), !Is.StringContaining("Error"));
答案 2 :(得分:0)
它的新版本会很漂亮..但至少这不依赖于sendkeys。
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "myLegacyConsoleApp.exe",
UseShellExecute = false,
RedirectStandardOutput = true,
WorkingDirectory = @"C:\Program Files\myAppDirectory\",
}
};
process.OutputDataReceived += new DataReceivedEventHandler(process_OutputDataReceived);
process.Start();
process.BeginOutputReadLine();
// You need to wait a bit.. Im sorry..
System.Threading.Thread.Sleep(5000);
process.StandardInput.WriteLine("Welcome to Ûberland");
process.WaitForExit();