Jak wywołać aplikację konsoli z mojej aplikacji .NET i przechwycić wszystkie dane wyjściowe wygenerowane w konsoli?
(Pamiętaj, nie chcę najpierw zapisywać informacji w pliku, a następnie wystawiać ponownie, tak jak chciałbym otrzymać je na żywo).
Jak wywołać aplikację konsoli z mojej aplikacji .NET i przechwycić wszystkie dane wyjściowe wygenerowane w konsoli?
(Pamiętaj, nie chcę najpierw zapisywać informacji w pliku, a następnie wystawiać ponownie, tak jak chciałbym otrzymać je na żywo).
Odpowiedzi:
Można to dość łatwo osiągnąć za pomocą właściwości ProcessStartInfo.RedirectStandardOutput . Pełny przykład znajduje się w połączonej dokumentacji MSDN; jedynym zastrzeżeniem jest to, że może być konieczne przekierowanie standardowego strumienia błędów, aby zobaczyć wszystkie dane wyjściowe aplikacji.
Process compiler = new Process();
compiler.StartInfo.FileName = "csc.exe";
compiler.StartInfo.Arguments = "/r:System.dll /out:sample.exe stdstr.cs";
compiler.StartInfo.UseShellExecute = false;
compiler.StartInfo.RedirectStandardOutput = true;
compiler.Start();
Console.WriteLine(compiler.StandardOutput.ReadToEnd());
compiler.WaitForExit();
Console.Write
zamiast tego.
Jest to nieco lepsze od akceptowanej odpowiedzi z @mdb . W szczególności przechwytujemy również wyjście błędów procesu. Ponadto przechwytujemy te dane wyjściowe za pośrednictwem zdarzeń, ponieważ ReadToEnd()
nie działa, jeśli chcesz przechwytywać zarówno błędy, jak i zwykłe dane wyjściowe. Zajęło mi trochę czasu, aby to zadziałało, ponieważ w rzeczywistości wymaga to również BeginxxxReadLine()
połączeń po Start()
.
Sposób asynchroniczny:
using System.Diagnostics;
Process process = new Process();
void LaunchProcess()
{
process.EnableRaisingEvents = true;
process.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_OutputDataReceived);
process.ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_ErrorDataReceived);
process.Exited += new System.EventHandler(process_Exited);
process.StartInfo.FileName = "some.exe";
process.StartInfo.Arguments = "param1 param2";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
//below line is optional if we want a blocking call
//process.WaitForExit();
}
void process_Exited(object sender, EventArgs e)
{
Console.WriteLine(string.Format("process exited with code {0}\n", process.ExitCode.ToString()));
}
void process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data + "\n");
}
void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data + "\n");
}
Użyj ProcessInfo.RedirectStandardOutput, aby przekierować dane wyjściowe podczas tworzenia procesu konsoli.
Następnie możesz użyć Process.StandardOutput do odczytania danych wyjściowych programu.
Drugi link zawiera przykładowy kod, jak to zrobić.
ConsoleAppLauncher to biblioteka open source stworzona specjalnie po to, aby odpowiedzieć na to pytanie. Przechwytuje wszystkie dane wyjściowe generowane w konsoli i zapewnia prosty interfejs do uruchamiania i zamykania aplikacji konsoli.
Zdarzenie ConsoleOutput jest wywoływane za każdym razem, gdy konsola zapisuje nowy wiersz na wyjście standardowe / błąd. Wiersze są umieszczane w kolejce i na pewno będą zgodne z kolejnością wyjściową.
Dostępne również jako pakiet NuGet .
Przykładowe wywołanie, aby uzyskać pełne dane wyjściowe konsoli:
// Run simplest shell command and return its output.
public static string GetWindowsVersion()
{
return ConsoleApp.Run("cmd", "/c ver").Output.Trim();
}
Próbka z opiniami na żywo:
// Run ping.exe asynchronously and return roundtrip times back to the caller in a callback
public static void PingUrl(string url, Action<string> replyHandler)
{
var regex = new Regex("(time=|Average = )(?<time>.*?ms)", RegexOptions.Compiled);
var app = new ConsoleApp("ping", url);
app.ConsoleOutput += (o, args) =>
{
var match = regex.Match(args.Line);
if (match.Success)
{
var roundtripTime = match.Groups["time"].Value;
replyHandler(roundtripTime);
}
};
app.Run();
}
Dodałem kilka metod pomocniczych do Platformy O2 (projekt Open Source), które pozwalają łatwo skryptować interakcję z innym procesem poprzez wyjście i wejście konsoli (patrz http://code.google.com/p/o2platform/ źródło / przeglądanie / trunk / O2_Scripts / APIs / Windows / CmdExe / CmdExeAPI.cs )
Przydatne może być również API, które umożliwia przeglądanie wyników konsolowych bieżącego procesu (w istniejącej kontrolce lub oknie podręcznym). Zobacz ten post na blogu, aby uzyskać więcej informacji: http://o2platform.wordpress.com/2011/11/26/api_consoleout-cs-inprocess-capture-of-the-console-output/ (ten blog zawiera również szczegółowe informacje o tym, jak konsumować konsolowe dane wyjściowe nowych procesów)
Zrobiłem wersję reaktywną, która akceptuje wywołania zwrotne dla stdOut i StdErr.
onStdOut
i onStdErr
są wywoływane asynchronicznie,
gdy tylko nadejdą dane (przed zakończeniem procesu).
public static Int32 RunProcess(String path,
String args,
Action<String> onStdOut = null,
Action<String> onStdErr = null)
{
var readStdOut = onStdOut != null;
var readStdErr = onStdErr != null;
var process = new Process
{
StartInfo =
{
FileName = path,
Arguments = args,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = readStdOut,
RedirectStandardError = readStdErr,
}
};
process.Start();
if (readStdOut) Task.Run(() => ReadStream(process.StandardOutput, onStdOut));
if (readStdErr) Task.Run(() => ReadStream(process.StandardError, onStdErr));
process.WaitForExit();
return process.ExitCode;
}
private static void ReadStream(TextReader textReader, Action<String> callback)
{
while (true)
{
var line = textReader.ReadLine();
if (line == null)
break;
callback(line);
}
}
Następujące będzie działać executable
z args
i drukować
do konsoli.
RunProcess(
executable,
args,
s => { Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(s); },
s => { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(s); }
);
Z PythonTR - Python Programcıları Derneği, e-kitap, örnek :
Process p = new Process(); // Create new object
p.StartInfo.UseShellExecute = false; // Do not use shell
p.StartInfo.RedirectStandardOutput = true; // Redirect output
p.StartInfo.FileName = "c:\\python26\\python.exe"; // Path of our Python compiler
p.StartInfo.Arguments = "c:\\python26\\Hello_C_Python.py"; // Path of the .py to be executed
Dodano process.StartInfo.**CreateNoWindow** = true;
i timeout
.
private static void CaptureConsoleAppOutput(string exeName, string arguments, int timeoutMilliseconds, out int exitCode, out string output)
{
using (Process process = new Process())
{
process.StartInfo.FileName = exeName;
process.StartInfo.Arguments = arguments;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
output = process.StandardOutput.ReadToEnd();
bool exited = process.WaitForExit(timeoutMilliseconds);
if (exited)
{
exitCode = process.ExitCode;
}
else
{
exitCode = -1;
}
}
}
StandardOutput.ReadToEnd()
go użyjesz , nie wróci do następnej instrukcji aż do końca aplikacji. więc Twój limit czasu w WaitForExit (timeoutMilliseconds) nie działa! (Twój kod się zawiesi!)