Hellp. In my 'plumbing' I have 3 command that should performed sequentially, and each request must wait until the end of the previous command. Now I have done the 1st request, but 2nd and 3rd just skips...
Could you please suggest how to change this 'plumbing'?
string strCmdText = s1;
var startInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
RedirectStandardInput = true,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
var process = new Process { StartInfo = startInfo };
process.Start();
process.StandardInput.WriteLine(strCmdText);
process.WaitForExit();
string strCmdText1 = s2;
process.StandardInput.WriteLine(strCmdText1);
process.WaitForExit();
string strCmdText2 = s3;
process.StandardInput.WriteLine(strCmdText2);
process.StandardInput.WriteLine("exit");
Thank you.
Let's go through your code:
You start an instance of cmd.exe:
var process = new Process { StartInfo = startInfo };
process.Start();
You write a command to its standard input:
process.StandardInput.WriteLine(strCmdText);
And then you wait for cmd.exe to exit:
process.WaitForExit();
Now, you write another command to its standard input:
string strCmdText1 = s2;
process.StandardInput.WriteLine(strCmdText1);
Wait, what? cmd.exe exited in the previous step, so there's no more process you could send a command to in the first place.
Then you wait for the process to exit, but it's already dead a long time ago:
process.WaitForExit();
And you repeat the same non-working code:
string strCmdText2 = s3;
process.StandardInput.WriteLine(strCmdText2);
process.StandardInput.WriteLine("exit");
You should better understand what's the the problem now. It looks like cmd.exe quits after executing your first command.
There's a couple things you can try:
Get rid of cmd.exe altogether. Unless you execute some batch script you can directly call the intended executable (like python.exe).
Start 3 different instances of cmd.exe for your 3 commands.
Try to pass some arguments to cmd.exe, like /Q.
Try the first approach first, it's the cleanest one.
Related
I have some script files that would usually be edited and run through a ui, but the offer to run on the command line when using the syntax: program.exe scriptfile.fmw --userparameter "some parameter".
If I simply write
arguments = "program.exe scriptfile.fmw --userparameter \"some parameter\"";
Process process = Process.Start("cmd.exe", $"/K {arguments};
process.WaitForExit();
the commandline starts, calls the correct exe and runs the script.
Now I want to retrieve the response of the script after it ran to catch possible error messages.
As far as I can see this requires to use ProcessStartInfo and that's basically where my problem seems to start.
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = "cmd.exe",
Arguments = $"/K {arguments}",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
Process process = Process.Start(startInfo);
process.BeginErrorReadLine();
process.WaitForExit();
The code above opens a commandline window, but never uses the arguments given.
It escapes me on how I should hand the argument line over to the cmd.exe - process.
I was playing around with RedirectStandardInput and its StreamWriter, but never managed to get anything written into the cmd-window.
The same goes for the RedirectStandardOutput, where I would like to gather the script response in the cmd-window as a string or string[], to parse for specific exit codes of the script.
I think this is what you want, have a look below :
Process process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = "program.exe", //You must use your program directly without invoking through cmd.exe
Arguments = "scriptfile.fmw --userparameter \"some parameter\"", //Add parameters and arguments here needed by your application
UseShellExecute = false,
EnableRaisingEvents = true, //You are missing this
RedirectStandardOutput = true,
RedirectStandardError = true
};
process.ErrorDataReceived += process_ErrorDataReceived; //You should listen to its output error data by subscribing to this event
process.BeginErrorReadLine();
process.Start(startInfo);
process.WaitForExit(); // You may now avoid this
Then at here do anything with your received error data!
private static void process_ErrorDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
{
DoSomething(e.Data); // Handle your error data here
}
EDIT-(1) : Please try this solution and ping me if it works or if you need some extra help. Everyone in comments is suggesting that you must not use cmd.exe to invoke your program as it may causes debugging overhead, performance issue and you might not get error details as well.
Cheers!
I´m trying to get the return code of a batch script, which intern calls a couple other batch and exe files. When I execute the script in a CMD window and print the errorlevel, I get the correct error code, however when I do the same in C# with a process, I always get 0 as the error code.
This is my C# code
private Process ExecuteBatchFile(string batchFile)
{
Process process = new Process
{
StartInfo = new ProcessStartInfo
{
RedirectStandardOutput = false,
RedirectStandardError = false,
UseShellExecute = true,
FileName = "CMD.exe",
WorkingDirectory = Constants.ToolsPath,
Arguments = $"/c \"{batchFile} & pause\""
}
};
process.Start();
return process;
}
batchFile = $"testScript.bat -tns {Project.TnsName} & echo Error: %errorlevel%";
The output I get for the echo is Error: 0 and the process.ExitCode value is 0
If I open a CMD window and enter
cmd.exe /c "testScript.bat -tns MYTNS & echo Error: %errorlevel% & pause"
I get the correct errorlevel value.
I´m guessing it has something to do with the batch script but I don´t understand why it works in a CMD window but not in a C# Process, especially since I´m using the same method to connect a network drive and to execute an exe file.
Edit: Code without using CMD:
private Process ExecuteBatchFile(string batchFile, string args)
{
Process process = new Process
{
StartInfo = new ProcessStartInfo
{
RedirectStandardOutput = false,
RedirectStandardError = false,
UseShellExecute = true,
FileName = batchfile,
WorkingDirectory = Constants.ToolsPath,
Arguments = args
}
};
process.Start();
return process;
}
batchFile = "testScript.bat";
args = $"-tns {Project.TnsName}";
I suspect your .bat file has a line somewhere like exit /b 1. When running this .bat file from a cmd instance, the /b flag allows the .bat file to exit without closing the parent cmd process (great for running manually!).
Unfortunately, that means that when you do Process.Start("testScript.bat");, under the hood C# uses cmd to run your bat. The .bat file exits with the /b flag, signaling to the parent cmd process it shouldn't crash and therefore it exits "successfully" (exit code 0).
You have two options:
Remove the /b flag (which will correctly return the exit code .bat > cmd > process.ExitCode) - though this will mean that executing the .bat manually via a cmd instance will terminate the parent cmd upon exit
Add to your C# code to write a wrapper .bat that will pipe all args to your bat file and properly return %exitcode%:
private int ExecuteBatWithWrapper(string batFile, string args){
string runnerPath = Path.Combine(Path.GetDirectoryName(batFile), "runner.bat");
File.WriteAllText(runnerPath, $"call {batFile} %*\nexit %ERRORLEVEL%");
Process process = Process.Start(runnerPath, args); //Alternatively construct with ProcessStartInfo
File.Delete(runnerPath);
return process.ExitCode;
}
(of course, if this is used in any sort of important environment, you should incorporate that into some sort of using statement that upon disposal deletes the runner, but this is just proof-of-concept)
See https://bytes.com/topic/c-sharp/answers/511381-system-diagnostics-process-bat-file-always-returns-exit-code-0-a#post1989782 for a similar post
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'm trying to check if username and password for git repository is valid. In console I run:
git clone http://username:password#server/test.git
And I get:
fatal: Authentication failed for ...
So now I know username and password are not valid. I'm trying to run this command as a process:
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "git.exe",
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
WorkingDirectory = "some_directory"
CreateNoWindow = true,
Arguments = "git clone http://username:password#server/test.git"
},
};
process.Start();
I'd like to access the result of this command. Both process.StandardError and process.StandardOutput are equals string.Empty. Is there any way to read the result?
Normally you should read the exit code of the process.
process.ExitCode
If the process failed, the return value should be non 0. Of course you can only retrieve the exit code after the process completes.
So:
if (process.ExitCode != 0)
//error
Please note: I haven't tested it but it is standard convention.
To read the output, one normally uses:
string output = process.StandardOutput.ReadToEnd();
string err = process.StandardError.ReadToEnd();
Console.WriteLine(output);
Console.WriteLine(err);
What you really need is your standard output, which can be accessed as below:
string stdout = p.StandardOutput.ReadToEnd();
and use p.WaitForExit(); right after because sometimes it takes a while to give error message.
thanks in advance for your time.
I'm working on an application to automate some of the things that I have to check each day.
One of them is making sure that all of our daily Robocopies took place successfully.
I'm having issues figuring out the best way to communicate this to my program. I've looked into the various Exit codes that Robocopy uses which would be very useful, but is there anyway to output this to a file, and would there be anyway to know which backup it was associated with?
Sorry if this is vague, please let me know of any other info that could be of use.
Thanks,
Will
If you start the robocopy process from your application you could check the exit code from there.
Process myProcess = null;
// Start the process.
myProcess = Process.Start("robocopy ....");
myProcess.WaitForExit(1000);
Console.WriteLine("Process exit code: {0}", myProcess.ExitCode);
http://msdn.microsoft.com/en-us/library/system.diagnostics.process.exitcode(v=vs.110).aspx
You could write the Robocopy output to a file and read that in, like
robocopy c:\Test\ c:\temp /LOG+:myLogFile
Then experiment with the log options to get a useful verbosity ( see http://ss64.com/nt/robocopy.html )
You could do something like this:
public static void Main()
{
// Prepare the process.
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = "robocopy.exe";
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = "WHATEVER YOU NEED"
// Start process and wait for it to end
Process exeProcess = Process.Start(startInfo)
exeProcess.WaitForExit();
// Display what the exit code was
Console.WriteLine();
Console.WriteLine("Process exit code: {0}", exeProcess.ExitCode);
}
Create a Robocopy-process like this:
string command = $"Robocopy {sourcePath} {targetPath} /MIR /w:1 /r:10";
var copyTask = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = "CMD.exe"
Arguments = $"/c {command}",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
Use it like this:
copyTask.Start()
string Output = copyTask.StandardOutput.ReadToEnd();
copyTask.WaitForExit();
Now you have the whole log in the "string output". This is useful if you want to find out WHAT went wrong.