I have this code
public static void ExecuteCommand(string command, string workingFolder, int TimeoutMin)
{
int ExitCode;
ProcessStartInfo ProcessInfo;
Process process;
ProcessInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
ProcessInfo.CreateNoWindow = true;
ProcessInfo.UseShellExecute = false;
ProcessInfo.WorkingDirectory = workingFolder;
// *** Redirect the output ***
ProcessInfo.RedirectStandardError = true;
ProcessInfo.RedirectStandardOutput = true;
process = Process.Start(ProcessInfo);
process.WaitForExit(TimeoutMin * 1000 * 60);
// *** Read the streams ***
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
ExitCode = process.ExitCode;
MessageBox.Show("output>>" + (String.IsNullOrEmpty(output) ? "(none)" : output));
MessageBox.Show("error>>" + (String.IsNullOrEmpty(error) ? "(none)" : error));
MessageBox.Show("ExitCode: " + ExitCode.ToString(), "ExecuteCommand");
process.Close();
}
It runs batch file in C#
it is the batch file runs long process which takes few minutes
when I call ExecuteCommand then close the application, the batch file still runs even though app is closed
Is there any way I can control running batch files from within my c# code?
According to C# process hanging due to StandardOutput.ReadToEnd() and StandardError.ReadToEnd()
and Hanging process when run with .NET Process.Start -- what's wrong?
If output is kept filling with info, your thread might be blocked.
Following lines are blocking in your operation
process.WaitForExit(TimeoutMin * 1000 * 60);
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
That is, process.Close is never called before batch is done.
If abortion is required, you need to close process somewhere else while application is exiting.
I reproduce your problem by
Pasting code snippet to a button clicked event on a small winform application.
Press the button.
UI hung <--- That means code snippet is blocking a (UI) thread.
Related
I have a batch file I want to run from a C# windows form. the Batch file is very basic and accepts one parameter
cd C:\Program Files (x86)\Advent\ApxClient
AdvScriptRunner REPRUN -mrgainloss -p%1 -vf -t\\myserver\apx$\pdf\myReport
If i call it in a command prompt, this works fine
C:\Program Files (x86)\Locations\blah>realizedgainloss 123456
that will run just fine, and i get the expected result (it outputs a report run on a third party peice of software). However I cannot for the life of me figure this out with c#. I have the following.
private void button1_Click(object sender, EventArgs e)
{
ExecuteCommand(getCommand());
}
public string getCommand()
{
return "realizedgainloss.bat";
}
static void ExecuteCommand(string command)
{
int exitCode;
ProcessStartInfo processInfo;
Process process;
processInfo = new ProcessStartInfo(command);
//processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
// *** Redirect the output ***
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
processInfo.Arguments = String.Format("{0} {1}", command, "123456");
process = Process.Start(processInfo);
process.WaitForExit();
// *** Read the streams ***
// Warning: This approach can lead to deadlocks, see Edit #2
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
exitCode = process.ExitCode;
Console.WriteLine("output>>" + (String.IsNullOrEmpty(output) ? "(none)" : output));
Console.WriteLine("error>>" + (String.IsNullOrEmpty(error) ? "(none)" : error));
Console.WriteLine("ExitCode: " + exitCode.ToString(), "ExecuteCommand");
process.Close();
}
Its worth noting that if i do not provide a parameter, and change the bat file to be static, with 12345 in place of it's %1, then it runs from C#, so there is something incorrect about how i'm getting the parameters into the bat file...
any thoughts?
You have your batch file name as the command to run and the first parameter of your script. I find it easier and more reliable to use cmd.exe as the command to run and invoke it with the /C argument. Doing it this way you should make sure your working directory is set correctly as well.
processInfo = new ProcessStartInfo("cmd.exe");
processInfo.Arguments = String.Format("/C {0} {1}", command, "123456");
processInfo.WorkingDirectory = yourWorkingDirectory;
I am trying to execute a batch file which runs on its own. I am now trying to automate this by deploying it as a windows service which listens for a folder and invokes the batch file using file watcher event. Here is the code -
void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
ServiceEventLog.WriteEntry(TheServiceName + " Inside fileSystemWatcher_Created() - ");
if (e.Name.Trim().ToUpper().Contains("FU4DGF_TRADES"))
{
try
{
Utilities.SendEmail("IAMLDNSMTP", 25, "desmond.quilty#investecmail.com", "IAMITDevelopmentServices#investecmail.com", "Ben.Howard#investecmail.com", "prasad.matkar#investecmail.com", "StatPro BatchFile Execution Started ", "");
int exitCode;
// ProcessStartInfo processInfo;
ServiceEventLog.WriteEntry(TheServiceName + " Before creation of instance of Batch process - ");
Process process = new Process();
process.StartInfo.FileName = #"C:\Program Files (x86)\StatPro Suite\MonthlyUpload.bat";
process.StartInfo.RedirectStandardOutput = false;
process.StartInfo.RedirectStandardError = false;
process.StartInfo.CreateNoWindow = false;
process.StartInfo.WorkingDirectory = #"C:\Program Files (x86)\StatPro Suite";
process.StartInfo.UseShellExecute = false;
ServiceEventLog.WriteEntry(TheServiceName + " Before start of Batch process - ");
process.Start();
ServiceEventLog.WriteEntry(TheServiceName + " After start of Batch process - ");
process.WaitForExit();
//while (!process.HasExited)
//{
// System.Threading.Thread.Sleep(100);
//}
ServiceEventLog.WriteEntry(TheServiceName + " After process.close - ");
System.Environment.ExitCode = process.ExitCode;
}
I can see from my event log that it goes as far as logging - Before start of Batch process. Presumably after that the process starts by invoking process.Start() but then nothing happens. Nothing in the event log, the service is still running i.e. not crashed. No errors. I can see from task manager that it does invoke the exe that it is supposed to invoke via the batch file but the exe simply remains in memory with constant memory and 0 CPU usage suggesting the exe is not doing anything. If I run the batch file manually it works fine. Any idea what could be going wrong?
You disabled UseShellExecute. This means that you can't use the shell to execute the file. bat files are not executables, they are shell scripts.
Since you're not redirecting standard I/O anyway, just enable UseShellExecute and you should be fine.
I have a wrote a C# code that would read several .xml files(Containing executable, path, parameters. The .xml files contains a series of executable that need to be run) and several batch file will be created associate with the .xml file.
After each batch file is created, the program would execute each batch file in the C# code (using cmd of course). The problem is some of the batch file would run for days and it is a waste of memory/instants to keep the C# program running.
Is it possible to keep the .bat file running and close the C# code? Assuming that we do not need any error reporting from the C# code.
Below is the code for executing a batch file.
//Execute BatchFile
public static void ExecuteCommand(string command, string dummyPath)
{
int exitCode;
ProcessStartInfo processInfo;
Process process;
processInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
processInfo.WorkingDirectory = dummyPath;
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
process = Process.Start(processInfo);
process.WaitForExit();
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
exitCode = process.ExitCode;
Console.WriteLine("output>>" + (String.IsNullOrEmpty(output) ? "(none)" : output));
Console.WriteLine("error>>" + (String.IsNullOrEmpty(error) ? "(none)" : error));
Console.WriteLine("ExitCode: " + exitCode.ToString(), "ExecuteCommand");
process.Close();
}
If I understand you correctly, I think it is possible if you don't need any feedback from the .bat file.
I have tested this line of code on my own machine and it worked fine.
static void Main(string[] args)
{
System.Diagnostics.Process.Start(#"d:\simple.bat");
}
and for testing purpose I simply put PING www.google.com inside my .bat file.
The cmd batch process is belong to your program process, main process stop and sub processes will be killed.
I think you can try to create windows task schedule in your program, run batch in the taski, program stop and task schedule will still runnning.
I was executing BCP command through exe and it stucks after copying 50000 rows.
I looked at some forums and got to know that if we use StandardOuputReader in code than its max limit of output is near to 50000 rows which is happening to me as well
Is there a way i ran redirect output where more than 50000 rows can get out.
This code works here i have
proc.StartInfo.RedirectStandardOutput = false;
but i want to have it as true, to see the outputs.
private static void RunBatch(string Fullfilepath, string BatchFilePathDumpFlatFile)
{
mLogger.Error("RunBatch Start=== >");
mLogger.Error("Batch Filepath " + Fullfilepath + '\n' + "Batch File Directory " + BatchFilePathDumpFlatFile);
Process proc = null;
string targetDir = BatchFilePathDumpFlatFile;
proc = new Process();
proc.StartInfo.WorkingDirectory = targetDir;
proc.StartInfo.FileName = Fullfilepath;
//proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.Arguments = "/c";
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = false;
proc.Start();
proc.WaitForExit();
string output = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
string error = proc.StandardError.ReadToEnd();
proc.WaitForExit();
mLogger.Error("Output from batch " + output);
mLogger.Error("Error From Batch " + error);
}
update 1:
private static void RunBatch(string Fullfilepath, string BatchFilePathDumpFlatFile)
{
mLogger.Error("RunBatch Start=== >");
mLogger.Error("Batch Filepath " + Fullfilepath + '\n' + "Batch File Directory " + BatchFilePathDumpFlatFile);
Process proc = null;
string targetDir = BatchFilePathDumpFlatFile;
proc = new Process();
proc.StartInfo.WorkingDirectory = targetDir;
proc.StartInfo.FileName = Fullfilepath;
//proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.Arguments = "/c";
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.Start();
string output = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
string error = proc.StandardError.ReadToEnd();
proc.WaitForExit();
mLogger.Error("Output from batch " + output);
mLogger.Error("Error From Batch " + error);
}
this is what i am using is there a mistake because still BCP hangs and it starts running when i stop the exe of the code.
This is the classic deadlock condition. You should not call WaitForExit before reading the StandardOutput fully.
When the output is redirected, process will not terminate before all of its StandardOutput stream is read. So calling WaitForExit will wait for the started process to terminate, and child process will wait for the parent process to read the output stream fully before it can finish and thus deadlocks.
Msdn provides the explanation and code sample to avoid deadlock.
Synchronous read operations introduce a dependency between the caller reading from the StandardOutput stream and the child process writing to that stream. These dependencies can result in deadlock conditions. When the caller reads from the redirected stream of a child process, it is dependent on the child. The caller waits on the read operation until the child writes to the stream or closes the stream. When the child process writes enough data to fill its redirected stream, it is dependent on the parent. The child process waits on the next write operation until the parent reads from the full stream or closes the stream. The deadlock condition results when the caller and child process wait on each other to complete an operation, and neither can proceed. You can avoid deadlocks by evaluating dependencies between the caller and child process.
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "Write500Lines.exe";
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
The code 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. The child process would wait indefinitely for the parent to read from the full StandardOutput stream.
There is a similar issue when you read all text from both the standard output and standard error streams. The following C# code, for example, performs a read operation on both streams.
// Do not perform a synchronous read to the end of both
// redirected streams.
// string output = p.StandardOutput.ReadToEnd();
// string error = p.StandardError.ReadToEnd();
// p.WaitForExit();
// Use asynchronous read operations on at least one of the streams.
p.BeginOutputReadLine();
string error = p.StandardError.ReadToEnd();
p.WaitForExit();
Almost everything above taken from msdn, I suggest you read it fully to avoid creating deadlocks further.
I have a user, let's call it "MyUser". It has a password, suppose it is "Password". This user has an SSH key for git. I try to run from my ASP.NET application a batch file which issues git commands, it is at a location which is passed as a parameter. My function is as follows:
private void ExecuteCommand(string path, int timeout)
{
Process process = new Process();
process.StartInfo = new ProcessStartInfo();
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.Arguments = "\"" + path + "\"";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
//processInfo.WorkingDirectory = Config.GitHubRepoPath;
process.StartInfo.UserName = "MyUser";
process.StartInfo.Password = new System.Security.SecureString();
process.StartInfo.Password.AppendChar('P');
process.StartInfo.Password.AppendChar('a');
process.StartInfo.Password.AppendChar('s');
process.StartInfo.Password.AppendChar('s');
process.StartInfo.Password.AppendChar('w');
process.StartInfo.Password.AppendChar('o');
process.StartInfo.Password.AppendChar('r');
process.StartInfo.Password.AppendChar('d');
// *** Redirect the output ***
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
// *** Read the streams ***
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
if (timeout <= 0)
{
process.WaitForExit();
}
else
{
process.WaitForExit(timeout);
}
int exitCode = process.ExitCode;
process.Close();
return new ShellCommandReturn { Error = error, ExitCode = exitCode, Output = output };
}
But when I run this function, the ExitCode is -1073741502 and error and output are empty. How can I fix this behavior?
Please help me, I have tried to solve this literally for days.
I think redirecting both standard error and standard output & attempts to consume both synchronously is wrong. Please see this link:
http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardoutput%28v=vs.100%29.aspx
Allow me to copy excerpt:
A deadlock condition results if the parent process calls p.StandardOutput.ReadToEnd followed by p.StandardError.ReadToEnd and the child process writes enough text to fill its error stream. The parent process would wait indefinitely for the child process to close its StandardOutput stream. The child process would wait indefinitely for the parent to read from the full StandardError stream.
The other thing is ... when you invoke a cmd.exe instance, try adding a "/c" argument too.