How to display the output from cmd in application? - c#

I I need start acmd command in c#, for example: Echo Test.
Next I want to show the output of CMD in an messagebox like this:
MessageBox.Show(output_of_cmd_command);
Is it possible? If so, how?

This will consist of a couple of steps:
start the CMD process with the correct arguments
capture the CMD output
show it in the message box
I recently did something for Python, by using this function:
Keep in mind I explicitly suppressed the CMD dialog itself by setting UseShellExecute and CreateNoWindow. If you like you can alter those.
private string RunCommand(string fileName, string args)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = fileName;
start.Arguments = string.Format("{0}", args);
start.RedirectStandardOutput = true;
start.RedirectStandardError = true;
start.UseShellExecute = false;
start.CreateNoWindow = true;
var sb = new StringBuilder();
using (Process process = new Process())
{
process.StartInfo = start;
process.OutputDataReceived += (sender, eventArgs) =>
{
sb.AppendLine(eventArgs.Data); //allow other stuff as well
};
process.ErrorDataReceived += (sender, eventArgs) => {
};
if (process.Start())
{
process.EnableRaisingEvents = true;
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
//allow std out to be flushed
Thread.Sleep(100);
}
}
return sb.ToString();
}
Usage:
var result = RunCommand("path to your cmd.exe", "/C c:\example.bat");
MessageBox.Show(result);
Here's a listing of the CMD options:
Starts a new instance of the Windows command interpreter
CMD [/A | /U] [/Q] [/D] [/E:ON | /E:OFF] [/F:ON | /F:OFF] [/V:ON | /V:OFF]
[[/S] [/C | /K] string]
/C Carries out the command specified by string and then terminates
/K Carries out the command specified by string but remains
/S Modifies the treatment of string after /C or /K (see below)
/Q Turns echo off
/D Disable execution of AutoRun commands from registry (see below)
/A Causes the output of internal commands to a pipe or file to be ANSI
/U Causes the output of internal commands to a pipe or file to be
Unicode
/T:fg Sets the foreground/background colors (see COLOR /? for more info)
/E:ON Enable command extensions (see below)
/E:OFF Disable command extensions (see below)
/F:ON Enable file and directory name completion characters (see below)
/F:OFF Disable file and directory name completion characters (see below)
/V:ON Enable delayed environment variable expansion using ! as the
delimiter. For example, /V:ON would allow !var! to expand the
variable var at execution time. The var syntax expands variables
at input time, which is quite a different thing when inside of a FOR
loop.
/V:OFF Disable delayed environment expansion.

Related

C# execute, wait, read command's output

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.

Activating conda environment from c# code (or what is the differences between manually opening cmd and opening it from c#?)

I want to run a gpu accelerated python script on windows using conda environment (dlwin36).
I’m trying to activate dlwin36 and execute a script:
1) activate dlwin36
2) set KERAS_BACKEND=tensorflow
3) python myscript.py
If I manually open cmd on my machine and write:"activate dlwin36"
it works.
But when I try opening a cmd from c# I get:
“activate is not recognized as an internal or external command, operable program or batch file.”
I tried using the following methods:
Command chaining:
var start = new ProcessStartInfo();
start.FileName = "cmd.exe";
start.Arguments = "/c activate dlwin36&&set KERAS_BACKEND=tensorflow&&python myscript.py";
Process.Start(start).WaitForExit();
(I’ve tested several variations of UseShellExecute, LoadUserProfile and WorkingDirectory)
Redirect standard input:
var commandsList = new List<string>();
commandsList.Add("activate dlwin36");
commandsList.Add("set KERAS_BACKEND=tensorflow");
commandsList.Add("python myscript.py");
var start = new ProcessStartInfo();
start.FileName = "cmd.exe";
start.UseShellExecute = false;
start.RedirectStandardInput = true;
var proc = Process.Start(start);
commandsList.ForEach(command => proc.StandardInput.WriteLine(command));
(I’ve tested several variations of LoadUserProfile and WorkingDirectory)
In both cases, I got the same error.
It seems that there is a difference between manually opening cmd and opening it from c#.
The key is to run activate.bat in your cmd.exe before doing anything else.
// Set working directory and create process
var workingDirectory = Path.GetFullPath("Scripts");
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
RedirectStandardInput = true,
UseShellExecute = false,
RedirectStandardOutput = true,
WorkingDirectory = workingDirectory
}
};
process.Start();
// Pass multiple commands to cmd.exe
using (var sw = process.StandardInput)
{
if (sw.BaseStream.CanWrite)
{
// Vital to activate Anaconda
sw.WriteLine("C:\\PathToAnaconda\\anaconda3\\Scripts\\activate.bat");
// Activate your environment
sw.WriteLine("activate your-environment");
// Any other commands you want to run
sw.WriteLine("set KERAS_BACKEND=tensorflow");
// run your script. You can also pass in arguments
sw.WriteLine("python YourScript.py");
}
}
// read multiple output lines
while (!process.StandardOutput.EndOfStream)
{
var line = process.StandardOutput.ReadLine();
Console.WriteLine(line);
}
You need to use the python.exe from your environment. For example:
Process proc = new Process();
proc.StartInfo.FileName = #"C:\path-to-Anaconda3\envs\tensorflow-gpu\python.exe";
or in your case:
start.Arguments = "/c activate dlwin36&&set KERAS_BACKEND=tensorflow&&\"path-to-Anaconda3\envs\tensorflow-gpu\python.exe\" myscript.py";
I spent a bit of time working on this and here's the only thing that works for me: run a batch file that will activate the conda environment and then issue the commands in python, like so. Let's call this run_script.bat:
call C:\Path-to-Anaconda\Scripts\activate.bat myenv
set KERAS_BACKEND=tensorflow
python YourScript.py
exit
(Note the use of the call keyword before we invoke the activate batch file.)
After that you can run it from C# more or less as shown above.
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "cmd.exe";
start.Arguments = "/K c:\\path_to_batch\\run_script.bat";
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
start.RedirectStandardError = true;
start.WorkingDirectory = "c:\\path_to_batch";
string stdout, stderr;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
stdout = reader.ReadToEnd();
}
using (StreamReader reader = process.StandardError)
{
stderr = reader.ReadToEnd();
}
process.WaitForExit();
}
I am generating the batch file on the fly in C# to set the necessary parameters.
If this is gonna help anyone in the future. I found that you must run the activation from C:\ drive.

Access variable set in batch from C# application

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.

Embedding a CMD terminal in a C# Winforms application

What I intend to do is build an application which, among other things, will have a command line embedded in it just like some IDEs do (something I find extremely useful).
This is the code that I have so far, do note that it's a Winforms project:
public partial class Form1 : Form
{
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
info.FileName = "cmd.exe";
info.RedirectStandardInput = true;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.UseShellExecute = false;
info.CreateNoWindow = true;
p.StartInfo = info;
p.Start();
}
private void button1_Click(object sender, EventArgs e) {
using(StreamWriter sw = p.StandardInput) {
if(sw.BaseStream.CanWrite) {
sw.WriteLine(textBox1.Text);
}
}
textBox2.Text = p.StandardOutput.ReadToEnd();
textBox3.Text = p.StandardError.ReadToEnd();
p.WaitForExit();
}
}
}
As you can see there are 3 textboxes and one button:
textbox1 is for entering the command
textbox2 is for stdout
textbox3 is for stderr
On to my problem:
I can only input one command because after executing it, my CMD window vanishes. I know it dies off because I've set info.CreateNoWindow = false; and it indeed vanishes and if I try to enter another command I get an exception.
How would I go on about keeping my CMD window 'alive' so that I can use it as much as I please? In short I want to truly mimic CMD behavior.
Feel free to ask for more information if something is not clear.
Extra info/What I tried:
I've tried adding info.Attributes = "/K"; since I know that /K should keep the CMD alive. I've also read that p.WaitForExit(); should keep the CMD alive, but from what I figured this is only for the purpose of reading the output. Needless to say, I do not need that since I'm already redirecting its output. Neither of these solutions work but it is entirely possible that I'm using them the wrong way.
I need that process alive so I can easily navigate using cd and executing a sequence of commands when needed, such as when accessing ftp or mysql. I know I can work around these two examples with parameters, but not for every application. In short, spawning a new process every time is not something I want. I want that CMD interface to be up at all times.
The cmd process dies after
using(StreamWriter sw = p.StandardInput) {
if(sw.BaseStream.CanWrite) {
sw.WriteLine(textBox1.Text);
}
}
But I cannot pinpoint why.
What CMD console provides is an interface to execute predefined functions (in System32 or in %PATH%). Process class also have same capabilities ,what you can do is as the user enters command text and presses return key in textbox2 (which can be multi-lined, black-background, white text) you can pass the command text to Process p = new Process();and append the result so it looks like single cmd session. Now before passing the whole command text we need to separate arguments (if any) which is text appearing after first space. Example:
SHUTDOWN /S /T 10
where Shutdown will be filename and /S /T 10 will be arguments.
Before executing set default directory of ProcessStartInfo:-
_processStartInfo.WorkingDirectory = #"%Path%";
Otherwise default will be System32 folder.

Command prompt output being read as empty string

I'm trying to execute command prompt commands and read the output in C#. This is my code:
ProcessStartInfo cmdInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
cmdInfo.CreateNoWindow = true;
cmdInfo.RedirectStandardOutput = true;
cmdInfo.UseShellExecute = false;
Process cmd = new Process();
cmd.StartInfo = cmdInfo;
cmd.Start();
string result = cmd.StandardOutput.ReadToEnd();
cmd.WaitForExit();
cmd.Close();
return result;
It works most of the time, but sometimes result="" when that's impossible for the command I'm using (for example, route add should give output on success or failure). Any ideas? I was wondering if maybe I'd created a race condition between the process and the ReadToEnd call?
Not all output is written to StandardOutput; many applications will instead write to StandardError if something goes wrong. You would have to read from both to get all of the output.
As long as the application never blocks for input, it should be safe to call ReadToEnd() on both output streams to get all of the output. A safer option, however, is to hook up an event to the OutputDataReceived and ErrorDataReceived events. You can attach lambda expression to these that close over local variables to make things pretty simple:
var output = new StringBuilder();
var error = new StringBuilder();
cmd.OutputDataReceived += (o, e) => output.Append(e.Data);
cmd.ErrorDataReceived += (o, e) => error.Append(e.Data);
cmd.Start();
cmd.BeginOutputReadLine();
cmd.BeginErrorReadLine();
cmd.WaitForExit();

Categories