I have the following code which opens command window (from WPF interface) and executes code where can take long like # 8-10 minutes:
ProcessStartInfo procStartInfo = new ProcessStartInfo();
procStartInfo.FileName = _exePath;
procStartInfo.Arguments = arguments;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = false;
procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardError = true;
using (Process pr = Process.Start(procStartInfo))
{
pr.WaitForExit();
string result = pr.StandardOutput.ReadToEnd();
string[] split = result.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
int output = 0;
int.TryParse(split[split.Length - 1], out output);
return output;
}
And in Program.cs I have method which update status (show operation status and percent) with:
Console.Title = "Loading ... 5 %";
// do request to server and check status
while(status.InProgress) {
Thread.Sleep(2000); // 2 seconds
status = web.getJsonFromApiServer(url); // {InProgress: "true", Message: "Checking X%";
}
Sometimes the process is hanged and its title is not updated anymore like something goes in infinite loop.
If I use console without starting from WPF ( I mean use command prompt and then set location to exe path and run it with arguments), it works fine, no issue.
Why does this thing happens ?
A deadlock condition can result if the parent process calls
p.WaitForExit before p.StandardOutput.ReadToEnd and the child process
writes enough text to fill the redirected stream. The parent process
would wait indefinitely for the child process to exit. The child
process would wait indefinitely for the parent to read from the full
StandardOutput stream.
To avoid deadlocks you should use asynchronous methods:
var procStartInfo = new ProcessStartInfo
{
FileName = _exePath,
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
var p = new Process { StartInfo = procStartInfo };
p.OutputDataReceived += (sender, eventArgs) => { Console.WriteLine(eventArgs.Data); };
p.Start();
p.BeginOutputReadLine();
p.WaitForExit();
Related
There are two endpoints, I am thinking to add one endpoint to start the process and another is to do process communication(stdin/stdin). Is it possible? Or should I use some other ways to do this like websocket?
I am trying to start a process as below.
Process process = new Process();
ProcessStartInfo procStartInfo = new ProcessStartInfo("/bin/sh");
procStartInfo.RedirectStandardError = true;
procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardInput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.Arguments = "-c " + Constants.CMDName + args;
process.StartInfo = procStartInfo;
Console.WriteLine("Start res: " + process.Start());
Process is getting started but when I am trying to do stdin/out like below I am getting an error saying StandardIn not redirected.
Process[] processes = Process.GetProcessesByName(Constants.VSDebugProcessName);
if (processes.Length == 0)
{
throw new Exception("Process is not running");
}
Console.WriteLine(JsonSerializer.Serialize(processes[0].StartInfo));
var process = processes[0];
StreamWriter sw = process.StandardInput;
await sw.WriteLineAsync(JsonSerializer.Serialize(payload));
Should I combine these two endpoints or is there any other workaround for this issue?
You can set EnableRaisingEvents = true in the ProcessStartInfo, and add a handler on the process’s OutputDataReceived message to collect the output. The following snippet illustrates the procedure. It also handles error output (stderr).
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = fileName,
Arguments = arguments,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
},
EnableRaisingEvents = true,
};
var output = new StringBuilder();
var error = new StringBuilder();
process.OutputDataReceived += (_, args) =>
{
output.AppendLine(args.Data);
};
process.ErrorDataReceived += (_, args) =>
{
error.AppendLine(args.Data);
};
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
ResultsText.Value = output.ToString();
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();
}
I need to call headless chrome from a net core console application. But with this code the aplication run and get stuck doing nothing and printing nothin, also the pdf is not created. The same arguments in the terminal are working as expected.
public static bool TakeScreenshot2()
{
try
{
var procStartInfo = new ProcessStartInfo()
{
FileName = "google-chrome",
Arguments = "--headless --disable-gpu --print-to-pdf=final.pdf http://www.google.com/",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
var proc = new Process { StartInfo = procStartInfo };
proc.Start();
var output = proc.StandardOutput.ReadToEnd();
Console.WriteLine(output);
string error = proc.StandardError.ReadToEnd();
Console.WriteLine(error);
return proc.ExitCode == decimal.Zero ? true : false;
}
finally
{
// do something
}
}
You should wait for the process to finish
var proc = new Process { StartInfo = procStartInfo };
proc.Start();
proc.WaitForExit();
You can check if it was success also with proc.ExitCode after it exit
If you dont want to block the thread unit it finish you can run it with, you function needs to be async
await Task.Run(() => proc.WaitForExit());
or to use the Process event Exited
I just want to run another .exe, and close the caller.
Something like:
var info = new ProcessStartInfo
{
WorkingDirectory = "myDirectory",
FileName = "myFile.exe"
};
Process.Start(info);
Environment.Exit(0);
However, the child process closes at the same time as the parent process.
The parent is an application, it closes after clicking on a button.
The child is an application, it closes at the same time with the parent (unwanted).
The parent is an launcher.
The child is an application that works when you launch it manually.
I have tried other solutions but they all have the same bad behaviour:
var info = new ProcessStartInfo
{
WorkingDirectory = "myDirectory",
FileName = "cmd",
Arguments = $"/C myFile.exe",
WindowStyle = ProcessWindowStyle.Hidden
};
Process.Start(info);
Environment.Exit(0);
var thread = new Thread(() =>
{
var info = new ProcessStartInfo
{
WorkingDirectory = "myDirectory",
FileName = "myFile.exe"
};
Process.Start(info);
});
thread.Start();
Environment.Exit(0);
Task.Factory.StartNew(() =>
{
var info = new ProcessStartInfo
{
WorkingDirectory = "myDirectory",
FileName = "myFile.exe"
};
Process.Start(info);
});
This is stupid... It just doesn't work in debug mode.
It's not a bug, it's intentional (fortunately it's designed that way).
(using Rider JetBrains)
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);