Get Values from Process StandardOutput - c#

I am attempting to get output to show the currently open documents on my machine, but it comes back NULL no matter what.
StringCollection values = new StringCollection();
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "openfiles.exe",
Arguments = "/query /FO CSV /v",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
proc.Start();
while (!proc.StandardOutput.EndOfStream)
{
string line = proc.StandardOutput.ReadLine();
values.Add(line);
}
foreach (string sline in values)
MessageBox.Show(sline);
Edit:
During further review I see that I am getting an exception issue. During my diag run I get the following:
Proc.BasePriority thre an exception of type System.InvalidOperationException
Edit:
Attempted to pull code as:
string val = proc.StandardOutput.ReadToEnd();
MessageBox.Show(val);
Also a NULL value on return, and Proc still had errors even after proc.start();.

You have to read both the standard output and standard error streams. This is because you can't read them both from the same thread.
To achieve this you have to use the eventhandlers that will be called on a separate thread.
Compile the code as anycpu as openfiles comes in a 32-bit and 64-bit variant. It might not find the executable if there is an architecture mismatch.
The lines that are read from the error stream are prepended with ! > so they stand out in the output.
StringCollection values = new StringCollection();
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "openfiles.exe",
Arguments = "/query /FO CSV /v",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = false
}
};
proc.Start();
proc.OutputDataReceived += (s,e) => {
lock (values)
{
values.Add(e.Data);
}
};
proc.ErrorDataReceived += (s,e) => {
lock (values)
{
values.Add("! > " + e.Data);
}
};
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
proc.WaitForExit();
foreach (string sline in values)
MessageBox.Show(sline);

Related

Not possible to set input file in linux command running from .NET Core

I'm running .NET Core app on the linux docker container
When I call the command from the linux terminal it works well:
./darknet detector test -out result.json < data/file-list.txt
But when I start the process from the .NET Core I see error. Process runner method:
public static string RunCommand(string command, string args)
{
var process = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = command,
Arguments = args,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
process.Start();
process.WaitForExit();
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
return #$"{output}{Environment.NewLine}-------------------------------{Environment.NewLine}{error}";
}
Calling code:
string args = #$"detector test -out result.json < data/file-list.txt";
string output = ProcessRunner.RunCommand("./darknet", args);
Here is the part of the output:
Cannot load image "<"
STB Reason: can't fopen
How to fix it?
You can write the process's standard input once you set the RedirectStandartInput to true while starting your process. Here is an example how to write :
var process = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = "./ConsoleApp1.exe",
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true, // here you need
UseShellExecute = false,
CreateNoWindow = true,
}
};
process.Start();
using var file = File.OpenRead("./1.txt");
using var reader = new StreamReader(file);
while (true)
{
var line = await reader.ReadLineAsync();
if (string.IsNullOrWhiteSpace(line)) break; // you can use some other stoping decision
await process.StandardInput.WriteLineAsync(line);
}

Getting the error of a Powershell Script within c# and outputting it with conditional statement?

I have a console application and a method that executes a PowerShell script within the console application. So I'm trying to grab an error text that it outputs in the application and do something with it.
Example/What I'm trying to do:
If Error.contains("Object")
{
// do something here
}
Here is my current method
public void ExecutePowershellScript()
{
var file = #"C:\Path\filename.ps1";
var start = new ProcessStartInfo()
{
FileName = "powershell.exe",
Arguments = $"-NoProfile -ExecutionPolicy unrestricted -file \"{file}\"",
UseShellExecute = false
};
Process.Start(start);
}
Process.start: how to get the output?
When you create your Process object set StartInfo appropriately:
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "program.exe",
Arguments = "command line arguments to your executable",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
then start the process and read from it:
proc.Start();
while (!proc.StandardOutput.EndOfStream)
{
string line = proc.StandardOutput.ReadLine();
// do something with line
}
You can use int.Parse() or int.TryParse() to convert the strings to numeric values. You may have to do some string manipulation first if there are invalid numeric characters in the strings you read.
You can set RedirectStandardError = true and access any errors from process.StandardError
public static void ExecutePowershellScript()
{
var file = #"C:\Path\filename.ps1";
var start = new ProcessStartInfo()
{
FileName = "powershell.exe",
Arguments = $"-NoProfile -ExecutionPolicy unrestricted -file \"{file}\"",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
using Process process = Process.Start(start);
string output = process.StandardOutput.ReadToEnd();
string errors = process.StandardError.ReadToEnd();
}
Okay, scratch the above suggestion.
After being corrected by mklement0,
This is a perfectly reasonable attempt, but, unfortunately, it can lead to hangs (while waiting for one's stream end, the other, when exceeding the buffer size, may cause process execution to block). If you need to capture both streams, you must collect the output from one of them via events. – mklement0
I changed the solution to use the ErrorDataReceived event
public static async Task ExecutePowershellScript()
{
var file = #"C:\Path\filename.ps1";
var start = new ProcessStartInfo
{
FileName = "powershell.exe",
Arguments = $"-NoProfile -ExecutionPolicy unrestricted -file \"{file}\"",
UseShellExecute = false,
// redirect standard error stream to process.StandardError
RedirectStandardError = true
};
using var process = new Process
{
StartInfo = start
};
// Subscribe to ErrorDataReceived event
process.ErrorDataReceived += (sender, e) =>
{
// code to process the error lines in e.Data
};
process.Start();
// Necessary to start redirecting errors to StandardError
process.BeginErrorReadLine();
// Wait for process to exit
await process.WaitForExitAsync();
}
start.Start();
while (!start.StandardOutput.EndOfStream)
{
string line = start.StandardOutput.ReadLine();
}

Redirect input and output for cmd.exe

I wanna redirect cmd.exe output somewhere, below code works when the command is a line:
Process p = new Process()
{
StartInfo = new ProcessStartInfo("cmd")
{
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
CreateNoWindow = true,
Arguments = String.Format("/c \"{0}\"", command),
}
};
p.OutputDataReceived += (s, e) => Messagebox.Show(e.Data);
p.Start();
p.BeginOutputReadLine();
p.WaitForExit();
But how about a series commands like WriteLine():
p.StandardInput.WriteLine("cd...");
p.StandardInput.WriteLine("dir");
how to get output in this situation?
To achieve such behavior you should use /k switch to run cmd.exe in interactive mode.
The problem is to separate inputs from different commands.
To do this you could change the standard prompt using prompt command:
prompt --Prompt_C2BCE8F8E2C24403A71CA4B7F7521F5B_F659E9F3F8574A72BE92206596C423D5
So now it is pretty easy to determine the end of command output.
Here is the complete code:
public static IEnumerable<string> RunCommands(params string[] commands) {
var process = new Process {
StartInfo = new ProcessStartInfo("cmd") {
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
CreateNoWindow = true,
Arguments = "/k",
}
};
process.Start();
const string prompt = "--Prompt_C2BCE8F8E2C24403A71CA4B7F7521F5B_F659E9F3F8574A72BE92206596C423D5 ";
// replacing standard prompt in order to determine end of command output
process.StandardInput.WriteLine("prompt " + prompt);
process.StandardInput.Flush();
process.StandardOutput.ReadLine();
process.StandardOutput.ReadLine();
var result = new List<string>();
try {
var commandResult = new StringBuilder();
foreach (var command in commands) {
process.StandardInput.WriteLine(command);
process.StandardInput.WriteLine();
process.StandardInput.Flush();
process.StandardOutput.ReadLine();
while (true) {
var line = process.StandardOutput.ReadLine();
if (line == prompt) // end of command output
break;
commandResult.AppendLine(line);
}
result.Add(commandResult.ToString());
commandResult.Clear();
}
} finally {
process.Kill();
}
return result;
}
It works well but it looks like one big hack.
I'd recommend you to use process per command instead.

Getting GCC's output from command line when running it through C#

I'm currently compiling a C program through my C# app. If gcc outputs anything it means that there was an error. I want to retrieve this error and display it to the user.
Currently, I'm doing this...
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = _cCompilerPath,
Arguments = " ./file.c",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
proc.Start();
string message = string.Empty;
while (!proc.StandardOutput.EndOfStream)
{
string line = proc.StandardOutput.ReadLine();
message = string.Concat(message, line);
}
MessageBox.Show("OUTPUT :" + message);
But even when there is an error in the program, nothing gets redirected to the Standard Output, and the EndOfStream always returns true.
I would bet that it's writing to StandardError instead.
RedirectStandardError = true,
....
while (!proc.StandardError.EndOfStream)
{
string line = proc.StandardError.ReadLine();
message = string.Concat(message, line);
}
Use Console.WriteLine("message"); if you are working with the console. The MessageBox is for WinForms applications.
Console.WriteLine("OUTPUT: {0}", message);
Console.WriteLine writes to the standard output.

Process with ProcessWindowStyle.Hidden still shows Press any key to exit?

I have this:
var startInfo = new ProcessStartInfo
{
FileName = _pathToExe,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UseShellExecute = false,
WorkingDirectory = FilepathHelper.GetFolderFromFullPath(_pathToExe),
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
};
try
{
using (_proc = Process.Start(startInfo))
{
_proc.EnableRaisingEvents = true;
_proc.ErrorDataReceived += proc_DataReceived;
_proc.OutputDataReceived += proc_DataReceived;
_proc.BeginErrorReadLine();
_proc.BeginOutputReadLine();
var myStreamWriter = _proc.StandardInput;
var allArgs = "";
foreach (var arg in _args)
allArgs += arg + Environment.NewLine;
myStreamWriter.Write(allArgs);
_proc.WaitForExit();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
And I am executing an *.exe that someone else wrote. For this particular *.exe - even though you can clearly see above that I have set ProcessWindowStyle.Hidden, I still see a black window appear with the words "Press any key to exit.". This *.exe - if I run from the command line (instead of calling it from my C# code) produces a tremendous amount of console output text. I do not see this output text when I run my C# code, which is what I want and also means the redirection is working.
I checked and the process is finished - it's as if the command window itself is adding this extra (undesirable) step.
Has anyone encountered this before and if so how can I get rid of this?
It seems that the program you are starting is calling system("PAUSE") at the end thus spawning a new process which prints the "Press Any Key to Continue..." message and waits for user input. I cannot reproduce the exact situation of yours but you can try this.
var startInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UseShellExecute = false,
CreateNoWindow = true,
};
try
{
Process _proc;
using (_proc = Process.Start(startInfo))
{
_proc.EnableRaisingEvents = true;
_proc.BeginErrorReadLine();
_proc.BeginOutputReadLine();
var myStreamWriter = _proc.StandardInput;
myStreamWriter.WriteLine("D:\\your.exe"); //write your.exe to cmd and press enter :)
_proc.WaitForExit();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
From Document:
To use System.Diagnostics.ProcessWindowStyle.Hidden, the system.Diagnostics.ProcessStartInfo.UseShellExecute property must be true.

Categories

Resources