I have c# console app for ffmpeg command invocation. Here is it
class Program
{
static void Main(string[] args)
{
ProcessStartInfo cmd = new ProcessStartInfo("cmd.exe");
cmd.RedirectStandardInput = true;
cmd.RedirectStandardOutput = true;
cmd.RedirectStandardError = true;
cmd.UseShellExecute = false;
cmd.CreateNoWindow = true;
cmd.WindowStyle = ProcessWindowStyle.Hidden;
Process console = Process.Start(cmd);
console.StandardInput.WriteLine(#"cd C:\Users\vishnu.aravind");
console.StandardInput.WriteLine(#"ffmpeg -i sunp.avi -i Alarm03.wav -c:v copy -c:a aac -strict experimental output.avi");
string errors = console.StandardError.ReadToEnd();
Console.WriteLine("Video file is created.");
Console.Read();
}
}
if I remove the line of code
string errors = console.StandardError.ReadToEnd();
this program will work fine. Else it hangs. I need the error information if any, what to do, please help.
Cause of a problem
Your program hangs because
cmd is an interactive command line application and thus continually produces output for std. out and std. error stream
Process.StandardError.ReadToEnd() reads the output of std. error stream of a process till it reaches end of stream
as cmd is an interactive command line application - it will not end it's std. error stream until process of cmd terminates - and that's why Process.StandardError.ReadToEnd() hangs when it is invoked on running cmd process.
Solution for a problem
To get output for each command you execute there are at least two following options:
Start each command with separate Process instance - and read output with standard methods of StreamReader class (ReadToEnd() and Read() methods).
Use single Process instance for cmd application - to read and write to it interactively:
Start process as in following example:
Process interactiveProcess = new Process();
string processOutput = "";
interactiveProcess.StartInfo.FileName = this.processPath;
interactiveProcess.StartInfo.Arguments = commandLineParameters;
interactiveProcess.StartInfo.UseShellExecute = false;
interactiveProcess.StartInfo.CreateNoWindow = false;
interactiveProcess.StartInfo.RedirectStandardInput = true;
interactiveProcess.StartInfo.RedirectStandardError = true;
interactiveProcess.Start();
interactiveProcess.EnableRaisingEvents = true;
interactiveProcess.ErrorDataReceived += new DataReceivedEventHandler((process, outputEventArgs) => processOutput += outputEventArgs.Data);
interactiveProcess.BeginErrorReadLine();
Write commands to cmd with following code
interactiveProcess.StandardInput.WriteLine(command);
To read response you will need to use System.Threading.Thread.Sleep() method and wait till processOutput variable is populated with output during execution of started commands.
Try to wait for exit before reading errors:
cmd.WaitForExit();
Related
I'm trying to read all system information on a windows pc, using C#. Here is my code :
public static string GetSystemInfo()
{
String command = "systeminfo";
ProcessStartInfo cmdsi = new ProcessStartInfo("cmd.exe");
cmdsi.Arguments = command;
Process cmd = Process.Start(cmdsi);
cmd.WaitForExit();
return cmd.StandardOutput.ReadToEnd();
}
But it just opens a console, doesn't execute systeminfo command.
How this can be solved?
The following snippet will work
public static string GetSystemInfo()
{
var command = "/c systeminfo";
var cmdsi = new ProcessStartInfo("cmd.exe");
cmdsi.Arguments = command;
cmdsi.RedirectStandardOutput = true;
cmdsi.UseShellExecute = false;
var cmd = Process.Start(cmdsi);
var output = cmd.StandardOutput.ReadToEnd();
cmd.WaitForExit();
return output;
}
You should set RedirectStandardOutput to true and read output before calling WaitForExit, otherwise you can get a deadlock, per MSDN
The example avoids a deadlock condition by calling
p.StandardOutput.ReadToEnd before p.WaitForExit. 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.
/c means terminating command line after execution
You need to prepend "/c" to the command
String command = "/c systeminfo";
/c indicates that you want to execute a command that follows
Update
ProcessStartInfo.RedirectStandardOutput needs to be set to true as mentioned in Pavel's Answer.
I have a batch file setEnv.bat.
#echo off
set input=C:\Program Files\Java\jdk1.8.0_131
SET MY_VAR=%input%
I want to run this batch file from C# application and want to access the newly set value of MY_VAR from c# application.
C#:
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName= "D:\\Check\\SetJavaHome.bat";
proc.StartInfo.WorkingDirectory =
System.Environment.CurrentDirectory;
proc.Start();
string myVar = Environment.GetEnvironmentVariable("MY_VAR");
Can someone help me in getting this working as expected?
Thanks in advance.
Check out this answer with the sample code:
https://stackoverflow.com/a/51189308/9630273
Getting the Environment variables directly from another process is not possible, but a simple workaround can be like:
Create a dummy bat file (env.bat) that will execute the required bat and echo the environment variable.
Get the output of this env.bat inside the process execution of C#.
The reason why you want to do this is a bit vague but if your only option is to run that batchfile from a call to Process.Start then the following trickery will let you promote the environment vars from the batch file to your own process.
This is the batch file I use:
set test1=fu
set test2=bar
The followng code opens a standard Command Prompt and then uses the StandardInput to send commands to the command prompt and receive the results with OutputDataReceived event. I basically caputure the output of the SET command and the parse over its result. For each line that contains an environment var and value I call Environment.SetEnvironmentVaruable to set the environment in our own process.
var sb = new StringBuilder();
bool capture = false;
var proc = new Process();
// we run cms on our own
proc.StartInfo.FileName = "cmd";
// we want to capture and control output and input
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardInput = true;
// get all output from the commandline
proc.OutputDataReceived += (s, e) => { if (capture) sb.AppendLine(e.Data); };
// start
proc.Start();
proc.BeginOutputReadLine(); // will start raising the OutputDataReceived
proc.StandardInput.WriteLine(#"cd \tmp"); // where is the cmd file
System.Threading.Thread.Sleep(1000); // give it a second
proc.StandardInput.WriteLine(#"setenv.cmd"); // run the cmd file
System.Threading.Thread.Sleep(1000); // give it a second
capture = true; // what comes next is of our interest
proc.StandardInput.WriteLine(#"set"); // this will list all environmentvars for that process
System.Threading.Thread.Sleep(1000); // give it a second
proc.StandardInput.WriteLine(#"exit"); // done
proc.WaitForExit();
// parse our result, line by line
var sr = new StringReader(sb.ToString());
string line = sr.ReadLine();
while (line != null)
{
var firstEquals = line.IndexOf('=');
if (firstEquals > -1)
{
// until the first = will be the name
var envname = line.Substring(0, firstEquals);
// rest is the value
var envvalue = line.Substring(firstEquals+1);
// capture what is of interest
if (envname.StartsWith("test"))
{
Environment.SetEnvironmentVariable(envname, envvalue);
}
}
line = sr.ReadLine();
}
Console.WriteLine(Environment.GetEnvironmentVariable("test2")); // will print > bar
This will bring the environment variables that are set by a command file into your process.
Do note you can achieve the same by creating a command file that first calls your batchfile and then start your program:
rem set all environment vars
setenv.cmd
rem call our actual program
rem the environment vars are inherited from this process
ConsoleApplication.exe
The latter is easier and works out of the box, no brittle parsing code needed.
I am writing a C# program to execute a python script with some arguments. The program is not executed(it is supposed to not only print out a message, but also to write to a file), even though there is no error and the Process ExitCode is 0(checked via the debugger). Where am I going wrong?
static private string ExecutePython(string sentence) {
// full path of python interpreter
string python = #"C:\Python27\python.exe";
// python app to call
string myPythonApp = #"C:\Users\user_name\Documents\pos_edit.py";
// Create new process start info
ProcessStartInfo myProcessStartInfo = new ProcessStartInfo(python);
// make sure we can read the output from stdout
myProcessStartInfo.UseShellExecute = false;
myProcessStartInfo.RedirectStandardOutput = true;
myProcessStartInfo.Arguments = string.Format("{0} {1}", myPythonApp, sentence);
Process myProcess = new Process();
// assign start information to the process
myProcess.StartInfo = myProcessStartInfo;
// start process
myProcess.Start();
// Read the standard output of the app we called.
StreamReader myStreamReader = myProcess.StandardOutput;
string myString = myStreamReader.ReadToEnd();
// wait exit signal from the app we called
myProcess.WaitForExit();
// close the process
myProcess.Close();
return myString;
}
You've put myProcess.WaitForExit(); in the wrong place; wait till Python has executed the script:
...
myProcess.Start();
StreamReader myStreamReader = myProcess.StandardOutput;
// first, wait to complete
myProcess.WaitForExit();
// only then read the results (stdout)
string myString = myStreamReader.ReadToEnd();
...
Works perfectly fine with me. I think there's something with your user_name containing spaces.
I'm running PSExec Microsoft tool with Process class executing a remote command with its own output like this:
Process p = new Process();
string args = #"\\remotemachine -u someuser -p somepass wmic product get name";
ProcessStartInfo ps = new ProcessStartInfo();
ps.Arguments = args;
ps.FileName = psExecFileName;
ps.UseShellExecute = false;
ps.CreateNoWindow = true;
ps.RedirectStandardOutput = true;
ps.RedirectStandardError = true;
p.StartInfo = ps;
p.Start();
StreamReader output = p.StandardOutput;
string output = output.ReadToEnd();
where wmic product get name is WMI tool running remotely with its own output listing all installed applications on the remote machine.
So, in the output I don't see the output of wmic, at the same time when I'm running PSExec in the command line locally, I can fully see the output of both PSExec and started remotely WMIC.
The question is, how can I capture all the output on the local machine? Should I run it in a separate console and try to attach to the console to capture all the output?
More generally, if put plainly, why is the output in the process StandardOutput and in the console when running PSExec directly not the same?
ReadToEnd will wait till the process exit. e.g. a Console.ReadLine() in the psExecFile could block your reading. But you can get the already written stream,
StreamReader output = p.StandardOutput;
string line;
while ((line = output.ReadLine()) != null)
{
Console.WriteLine(line);
}
In the console, data written to both StandardOutput and StandardError is displayed in the console.
Within your program you need to look at each individually...try adding something like this at the end:
string error = p.StandardError.ReadToEnd();
I am new to C# so please sorry if i make no sense in my question. In my application which is C# DLL need to open command prompt, give a plink command for Linux system to get a system related string and set that string as environment variable. I am able to do this when i create C# console application, using plink command to get the string on command prompt and use to set it environment variable using process class in C# to open plink as separate console process. But, in C# DLL i have to open cmd.exe 1st and then give this command which i don't know how can i achieve? I tried through opening cmd.exe as process and then trying to redirect input and output to process and give command and get string reply, but no luck. Please let me know any other way to solve this.
Thanks for answers,
Ashutosh
Thanks for your quick replys. It was my mistake in writing code sequence. Now few changes and the code is working like charm. Here is code,
string strOutput;
//Starting Information for process like its path, use system shell i.e. control process by system etc.
ProcessStartInfo psi = new ProcessStartInfo(#"C:\WINDOWS\system32\cmd.exe");
// its states that system shell will not be used to control the process instead program will handle the process
psi.UseShellExecute = false;
psi.ErrorDialog = false;
// Do not show command prompt window separately
psi.CreateNoWindow = true;
psi.WindowStyle = ProcessWindowStyle.Hidden;
//redirect all standard inout to program
psi.RedirectStandardError = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
//create the process with above infor and start it
Process plinkProcess = new Process();
plinkProcess.StartInfo = psi;
plinkProcess.Start();
//link the streams to standard inout of process
StreamWriter inputWriter = plinkProcess.StandardInput;
StreamReader outputReader = plinkProcess.StandardOutput;
StreamReader errorReader = plinkProcess.StandardError;
//send command to cmd prompt and wait for command to execute with thread sleep
inputWriter.WriteLine("C:\\PLINK -ssh root#susehost -pw opensuselinux echo $SHELL\r\n");
Thread.Sleep(2000);
// flush the input stream before sending exit command to end process for any unwanted characters
inputWriter.Flush();
inputWriter.WriteLine("exit\r\n");
// read till end the stream into string
strOutput = outputReader.ReadToEnd();
//remove the part of string which is not needed
int val = strOutput.IndexOf("-type\r\n");
strOutput = strOutput.Substring(val + 7);
val = strOutput.IndexOf("\r\n");
strOutput = strOutput.Substring(0, val);
MessageBox.Show(strOutput);
I explained the code so far..., thanks a lot