bat script hangs when run from Process.Start() - c#

I have the following C# code:
private void executeBat(string batfile)
{
ProcessStartInfo psi = new ProcessStartInfo(batfile);
psi.CreateNoWindow = true;
psi.ErrorDialog = false;
psi.UseShellExecute = false;
psi.RedirectStandardError = false;
psi.RedirectStandardOutput = false;
Process p = Process.Start(psi);
p.WaitForExit();
if (p.ExitCode != 0)
{
throw new Exception("bat script failed with exit code " + p.ExitCode + ".");
}
p.Close();
}
string batFile = #"C:\Data\SomeFolder\AnotherFolder\SomeBat.bat";
executeBat(batFile);
That runs a .bat script that looks like this:
"c:\Program Files (x86)\SomeThirdParty\SomeExe" >C:\Data\SomeLog.txt 2>&1
set exitcode=%errorlevel%
echo "error return is %exitcode%" >>C:\Data\SomeLog.txt
exit /b %exitcode%
The SomeLog.txt file looks like this:
"error return is 0"
But it appears that the .bat does not really exit because the c# code never returns from p.WaitForExit().
Other people have experienced this problem but my report is different because I am not reading stdout or stderr.
This does not happen consistently. Perhaps 1 out of 1000 executions.
Here is a big clue...
It only happens when I run the c# code from Task Scheduler. When I run it from the commandline I have never gotten the issue.

Related

executing a bash process that execute npm from c# code

I am writing a c# program and I want to run (npm install) and (npm run build) from the c# code.
I tried
public static Boolean BuildNpm()
{
Console.WriteLine("start process method?");
var proc = new System.Diagnostics.Process();
proc.StartInfo.WorkingDirectory="../../frontend/";
proc.StartInfo.FileName = "/bin/bash";
proc.Start();
//npm install
//npm run build
//exit
proc.WaitForExit();
Console.WriteLine("Done?");
return (proc.ExitCode == 0) ? true : false;
}
I was able to create a shell that ask for command and I add the commands I want but I want the commands in the program automatically.
Why do you need to run bash to run the commands? Shouldn't it work to run npm directly from your program?
public static Boolean BuildNpm()
{
Console.WriteLine("start process method?");
var proc = new System.Diagnostics.Process();
proc.StartInfo.WorkingDirectory="../../frontend/";
proc.StartInfo.FileName = "/path/to/npm";
proc.StartInfo.Arguments = "install";
proc.Start();
proc.WaitForExit();
proc.StartInfo.Arguments = "run build";
Console.WriteLine("Done?");
return (proc.ExitCode == 0) ? true : false;
}
You should add some error handling if either execution of npm fails, as well.

How to pass arguments to an already open terminal via System.Diagnostics.Process()

I have been messing around with triggering a bash script via C#. This all works fine when I first call the "open" command with arguments which in turn opens my .command script via Terminal.
Once the "open" command is used once Terminal or iTerm will remain open in the background, at which point calling the "open" command with arguments then has no further effect. I sadly have to manually quit the application to trigger my script again.
How can I pass arguments to an already open terminal application to restart my script without quitting?
I've searched online ad can't seem to work it out, it already took a good amount of time solve the opening code. Your help is much appreciated.
Here is the C# code I'm using to start the process:
var p = new System.Diagnostics.Process();
p.StartInfo.FileName = "open";
p.StartInfo.WorkingDirectory = installFolder;
p.StartInfo.Arguments = "/bin/bash --args \"open \"SomePath/Commands/myscript.command\"\"";
p.Start();
Thanks
EDIT:
Both answers were correct, this might help others:
ProcessStartInfo startInfo = new ProcessStartInfo("/bin/bash");
startInfo.WorkingDirectory = installFolder;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardInput = true;
startInfo.RedirectStandardOutput = true;
Process process = new Process();
process.StartInfo = startInfo;
process.Start();
process.StandardInput.WriteLine("echo helloworld");
process.StandardInput.WriteLine("exit"); // if no exit then WaitForExit will lockup your program
process.StandardInput.Flush();
string line = process.StandardOutput.ReadLine();
while (line != null)
{
Debug.Log("line:" + line);
line = process.StandardOutput.ReadLine();
}
process.WaitForExit();
//process.Kill(); // already killed my console told me with an error
You can try:
before calling p.Start():
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
// for the process to take commands from you, not from the keyboard
and after:
if (p != null)
{
p.StandardInput.WriteLine("echo helloworld");
p.StandardInput.WriteLine("executable.exe arg1 arg2");
}
(taken from here)
This is what you may be looking for :
Gets a stream used to write the input of the application.
MSDN | Process.StandardInput Property
// This could do the trick
process.StandardInput.WriteLine("..");

How to read message from batch file from C# application

I have a batch file that copy file from one folder to another folder. So I would like to run this file from a C# windows services then I would like to read if the script generate an error or it works correctly.
This is my code for lunch it but I don't Know how to read the message of the script:
SCRIPT CODE:
REM
REM This script moves files with results from GOLD server and saves them on MES06 server on .
REM IMPORT folder.
REM
REM Robocopy Options:
REM /R:2 Two retries on failed copies (default is 1 million)
REM /W:5 Wait 5 seconds between retries (default is 30 sec).
REM
REM GOLD QAS Inbound Folder: \\goldqas01.app.pmi\limscihome$\RootDirectory
REM
for /f "delims=: tokens=2,3" %%j in (F:\MES2GOLD\copy_list_test.txt) do ROBOCOPY.EXE %%j %%j\..\BACKUP *.* /R:2 /W:5 /log+:%%j\..\LOGS\MES2GOLD.log & ROBOCOPY.EXE %%j %%k *.* /R:2 /W:5 /MOV /log+:%%j\..\LOGS\MES2GOLD.log
PAUSE
C# Code:
public void execute(string workingDirectory, string command)
{
// create the ProcessStartInfo using "cmd" as the program to be run, and "/c " as the parameters.
// Incidentally, /c tells cmd that we want it to execute the command that follows, and then exit.
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd.exe", #"/c C:\Users\mcastrio\Desktop\GOLD2MES.bat");
procStartInfo.WorkingDirectory = workingDirectory;
//This means that it will be redirected to the Process.StandardOutput StreamReader.
procStartInfo.RedirectStandardOutput = true;
//This means that it will be redirected to the Process.StandardError StreamReader. (same as StdOutput)
procStartInfo.RedirectStandardError = true;
procStartInfo.UseShellExecute = false;
// Do not create the black window.
procStartInfo.CreateNoWindow = true;
// Now we create a process, assign its ProcessStartInfo and start it
System.Diagnostics.Process proc = new System.Diagnostics.Process();
//This is importend, else some Events will not fire!
proc.EnableRaisingEvents = true;
// passing the Startinfo to the process
proc.StartInfo = procStartInfo;
// The given Funktion will be raised if the Process wants to print an output to consol
proc.OutputDataReceived += DoSomething;
// Std Error
proc.ErrorDataReceived += DoSomethingHorrible;
// If Batch File is finished this Event will be raised
proc.Exited += Exited;
}
Can we help me?
You can use the code below inside your loop:
var startInfo = p.StartInfo;
startInfo.CreateNoWindow = true;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.UseShellExecute = false;
startInfo.StandardOutputEncoding = Encoding.GetEncoding("ibm850");
startInfo.RedirectStandardOutput = true;
startInfo.FileName = filebatch;
startInfo.Arguments = arguments;
p.Start();
var output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Or just use ProcessHelper.Run from TestSharp library:
ProcessHelper.Run(string exePath, string arguments = "", bool waitForExit = true)

C# process can not execute batch file (contain timeout command) correctly

I'd like to execute a batch file without showing terminal window
and I need to get the standard output contents.
Here is the batch file:
timeout /T 5
exit 0
Below is my partial C# code:
static void Main(string[] args)
{
bool ShowTerminal = true;
Process proc = new Process();
proc.StartInfo.FileName = "cmd.exe";
string ExecContent = "\"" + args[0] + "\"";
proc.StartInfo.Arguments = "/c " + ExecContent;
proc.StartInfo.UseShellExecute = ShowTerminal;
proc.StartInfo.RedirectStandardError = !ShowTerminal;
proc.StartInfo.RedirectStandardInput = !ShowTerminal;
proc.StartInfo.RedirectStandardOutput = !ShowTerminal;
proc.Start();
proc.WaitForExit();
}
I found that variable ShowTerminal set true, then everything is going well
except I can not get standard output contents
But if variable ShowTerminal set false, timeout command will be skipped
Is there any solution for this problem? Thanks!
If you specify "redirect" options you should also provide their redirection streams (STDIN, STDOUT) and ensure they are being emptied/read/processed as CMD would expect.
Chances are good that 'timeout' is failing to detect any STDIN/STDOUT and therefore performs a noop.

Git Pull from Batch file with different user

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.

Categories