Capture output written directly to Windows console - c#

I'm trying to capture the output of a Windows executable that's executed from C#. Currently, this is what I have:
private static string Execute(string pathToExe, string pathToInputFile, string pathToOutputFolder)
{
var standardOutput = new StringBuilder();
var errorOutput = new StringBuilder();
var process = new System.Diagnostics.Process();
process.StartInfo.FileName = pathToExe;
process.StartInfo.Arguments = $"\"{pathToInputFile}\" \"{pathToOutputFolder}\"";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.OutputDataReceived += (sender, eventArgs) => standardOutput.AppendLine(eventArgs.Data);
process.ErrorDataReceived += (sender, eventArgs) => errorOutput.AppendLine(eventArgs.Data);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit(-1);
return standardOutput.ToString() + errorOutput.ToString();
}
Instead of returning the console output of the executable, I'm only getting a blank string. Instead, the output is being written to my application's console.
I believe this is because the executable I'm running actually launches a GUI program. When run from the command line, the app exposes a CLI. It's my guess that the application is attaching itself to the current console instead of attaching to a new one that's monitored by my application.
For example, running the following in PowerShell 7 results in the output still being written to the console, as opposed to being redirected to the output.txt file:
external.exe --help *> output.txt
So it seems the executable isn't writing to one of the standard output streams.
Does anyone have any ideas or workarounds that would work in .NET Core 3.1 on Windows?

Related

Prompt user for input from application started via Process.start()

I am currently creating a console application to interact with git. This works fine with redirecting the output of my command to the console application but now some commands prompt for inputs like PassPhrase or y/n for owerwrite. How to redirect these prompts and ask for inputs from the user.
var process = new Process();
process.StartInfo.CreateNoWindow = false;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.WorkingDirectory = workDir;
process.StartInfo.FileName = appName;
process.StartInfo.Arguments = argument;
process.Start();
var output = new List<string>();
while (process.StandardOutput.Peek() > -1)
{
output.Add(process.StandardOutput.ReadLine());
}
while (process.StandardError.Peek() > -1)
{
output.Add(process.StandardError.ReadLine());
}
process.WaitForExit();
foreach (var opt in output)
{
Console.WriteLine(opt);
}
You need to read output and error asynchronously; this will both make it possible for you to read input from the user in the meantime, and avoid a hang when the buffer for standard error gets full (you're only reading it after standard output ends, which is not enough).
Processes don't ask for input; they just read the input stream (when using standard input, of course). The simplest way to take care of this is to keep reading standard input on your side, and posting the received data to the standard input of your "hosted" process.

How can I run a command line app in C# and redirect/display its standard output while preserving the colors from the app?

I am calling git via a command line in C#:
var logger = CreateProcess("git", $"log -n 1 {branchName}");
// some more code here
Console.WriteLine(logger.StandardOutput.ReadToEnd());
private static Process CreateProcess(string exe, string args)
{
var p = new Process();
p.StartInfo.FileName = exe;
p.StartInfo.Arguments = args;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
return p;
}
However the colors that are normally displayed by the command line git app are not displayed when I later go to write the redirected output ot the console; everything is white instead. Is there any way to run the app and redirect the output without losing the colors?
git -c color.ui=always log
See https://git-scm.com/docs/git-config#Documentation/git-config.txt-colorui .

Stop process output from being displayed in project command window

I'm starting an exe via the Process class and I've noticed that the exe's output is being displayed inside my application's command window. *Note - when I start up the exe, I make sure a window is not opened - so, the only window that displays when my application is running is my main application, project.exe.
Is there a way to stop the exe's output from being displayed inside my project.exe command window? Here is my code:
Process process = new Process();
string exePath = System.IO.Path.Combine(workingDir, exeString);
process.StartInfo.FileName = exePath;
process.StartInfo.WorkingDirectory = workingDir;
process.StartInfo.Arguments = args;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += (s, e) => Logger.LogInfo(e.Data);
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
I've even tried setting RedirectStandardOutput to false with:
process.StartInfo.RedirectStandardOutput = false;
and the output is still placed in the command window.
This works when I tried locally on my box. Can you give it a try by replacing the exe path/name.
From MSDN doc's.
"When a Process writes text to its standard stream, that text is typically displayed on the console. By setting RedirectStandardOutput to true to redirect the StandardOutput stream, you can manipulate or suppress the output of a process. For example, you can filter the text, format it differently, or write the output to both the console and a designated log file"
https://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardoutput(v=vs.110).aspx
void Main()
{
Process process = new Process();
string exePath = System.IO.Path.Combine(#"C:\SourceCode\CS\DsSmokeTest\bin\Debug", "DsSmokeTest.exe");
process.StartInfo.FileName = exePath;
process.StartInfo.WorkingDirectory = #"C:\SourceCode\CS\DsSmokeTest\bin\Debug";
process.StartInfo.Arguments = string.Empty;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += (s, e) => Test(e.Data);
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
}
// Define other methods and classes here
public void Test(string input)
{
input.Dump();
}
just add the following line
process.StartInfo.UseShellExecute = true;

Problems getting desired output from Process.Start()

I am working on an application that calls several command line applications to do some post processing on some video files.
Right now I am trying to use Comskip to identify the commercial breaks in a video recording from my cable card tuner. This runs just fine, but I am having problems getting the screen output that I need.
String stdout = null;
using (var process = new Process())
{
var start = new ProcessStartInfo(comskip, cmdLine);
start.WindowStyle = ProcessWindowStyle.Normal;
start.CreateNoWindow = true;
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
process.StartInfo = start;
process.Start();
process.WaitForExit();
stdout = process.StandardOutput.ReadToEnd();
}
I'm expecting stdout to grab what is displayed on the screen the same as when the application is launched manually (screen shot below) which is a continuous feed of what the application is doing, and mixed in the output are lines that give a % progress, which I want to use to update a progress bar
But running the above code only gives me:
The commandline used was:
"C:\Users\Chris\Google Drive\Tools\ComSkip\comskip.exe" "C:\Users\Chris\Desktop\ComSkip Tuning Files\Modern Family.wtv" "--ini=C:\Users\Chris\Desktop\ComSkip Tuning Files\comskip_ModernFamily.ini"
Setting ini file to C:\Users\Chris\Desktop\ComSkip Tuning Files\comskip_ModernFamily.ini as per commandline
Using C:\Users\Chris\Desktop\ComSkip Tuning Files\comskip_ModernFamily.ini for initiation values.
I also tried redirecting the StandardError stream and grabbing process.StandardError.ReadToEnd(); but the process appears to hang if I run with these options.
Am I missing something to capture what I'm hoping for, or is it possible that the output stream for this application is going somewhere else that is not accessible?
You must set following:
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.UseShellExecute = false;
process.OutputDataReceived += new DataReceivedEventHandler(ReadOutput);
process.ErrorDataReceived += new DataReceivedEventHandler(ErrorOutput);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
and catch the output in ReadOutput and ErrorOutput
private static void ErrorOutput(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
stdout = "Error: " + e.Data;
}
}
private static void ReadOutput(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
stdout = e.Data;
}
}
See the docs on RedirectStandardOutput. Waiting for the child process to end before reading the output can cause a hang.
It particular, the example says not to do what you have done:
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "Write500Lines.exe";
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
You should use the events OutputDataReceived and possibly ErrorDataReceived and update the progress bar in the handler.

how to open a putty session using C#

I would like to know how to open putty using C# in Visual Basic express. Then execute commands through the ssh session.
You can use plink.exe for SSH an pscp.exe for SCP.
https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html
Donwload the 2 files, then copy them into your Solution. Then under Properties select: Copy if newer.
// SCP
var process = new Process();
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = Directory.GetCurrentDirectory() + $#"\pscp.exe";
processStartInfo.Arguments = $#"-P 22 -pw password filepath.zip root#host:/path ";
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardInput = true;
processStartInfo.CreateNoWindow = true;
process.StartInfo = processStartInfo;
process.OutputDataReceived += (sender, args) => Console.WriteLine(args.Data);
process.ErrorDataReceived += (sender, args) => Console.WriteLine(args.Data);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
Console.WriteLine(process.ExitCode);
// SSH
process = new Process();
processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = Directory.GetCurrentDirectory() + $#"\plink.exe";
processStartInfo.Arguments = $#"-P 22 -pw password root#host command";
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardInput = true;
processStartInfo.CreateNoWindow = true;
process.StartInfo = processStartInfo;
process.OutputDataReceived += (sender, args) => Console.WriteLine(args.Data);
process.ErrorDataReceived += (sender, args) => Console.WriteLine(args.Data);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
I recently had to do something similar with WinSCP and the way I did it was to kick off the process with redirected Standard Input and Output. If Putty use the the standard input/output you might be able to use the same method for that.
The sample on the WinSCP pages is quite good so I'd suggest starting with that, and here's a code project article about something similar: How to redirect Standard Input/Output of an application
What you actually need is an SSH component for .NET, capable of providing shell and command channel access to the remote host. Shell is what you see with PuTTY. You "type" the requests and get some response, which you need to parse then to separate responses from command prompt. Command channel is when the commands are sent one by one and you receive only response(s) back. You don't need to parse anything (besides handling actual responses). Simple components can't send multiple commands in one session.
You can take our SSH component for .NET, that offers both shell and command channels, and supports many types of authentication (so it doesn't matter what authentication type your server uses - our component supports it).

Categories