Execute batch script from .NET (unexpected output) - c#

I'm trying to execute a simple batch script from a C# program.
Here's a minimal example:
Batch file
TIMEOUT /T 30 >nul
EXIT /B 1
C# program
static void Main(string[] args)
{
Process process = new Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.CreateNoWindow = false;
process.StartInfo.FileName = "D:\\xxx.bat";
process.Start();
string output = process.StandardOutput.ReadToEnd();
bool exit = process.WaitForExit(120000);
Console.WriteLine(output);
}
Output
"\r\nC:\\Users\\xxx\\bin\\Debug>TIMEOUT /T 30 \r\n\r\nWaiting for 30 seconds, press a key to continue ...\r\n\r\nC:\\Users\\xxx\\bin\\Debug>EXIT /B 1 \r\n"
I'd expect the batch script to produce no output (due to nul redirection of the timeout command) but it still does. The output contains the program path and the command text. Why is that? (Is it outputting the script trace eg. like bash -x? Google doesn't tell me how/if it could be disabled)

Redirecting output to nul should only have hidden the output of the redirected command.
Instead, you can add #echo off at the start of your bat file.
This will:
From "Echo", Technet
prevent all commands in a batch file (including the echo off
command) from displaying on the screen.

Related

Process.Start() only works when started as release build within VisualStudio

I'm trying to do execute an external .bat by pressing a button.
The intention is to call some XCOPY instructions. Therefore I execute "sync.bat" using Process.Start(startInfo).
The output of that .bat is redirected to my App and shown in a dialog box. My code waits until the external call has finished.
echo "Batch SYNC started."
pause
xcopy "e:\a\*" "e:\b\" /f /i /c /e /y
pause
echo "Batch SYNC finished."
OK:
When I build my program as "release" and start it within VisualStudio2013, everything works fine (I see the Results, have to press ENTER in the black window, files are copied).
FAIL:
When I start my app by double-click (in file-explorer or from the desktop) or a debug build within the VisualStudio, I see the ECHO and the PAUSE output, but the batch did not stop and I see no results from XCOPY. Seems as if the PAUSE and XCOPY are killed immediately.
I got no exception and no entry in Windows-log.
I have tried to make DEBUG and RELEASE configuration identical (with no success).
Does anybody have an idea what I may do to get this simple function work outside the IDE?
Here is the code of the function called when the button is pressed:
private void ProcessSync_bat()
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.FileName = "sync.bat";
startInfo.WindowStyle = ProcessWindowStyle.Normal;
startInfo.Arguments = "";
startInfo.ErrorDialog = true;
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(startInfo))
{
dlgFKSyncMessageBox.AddLine("----------sync.bat started-----------");
dlgSyncMessageBox.AddLine("===============Result================");
while (!exeProcess.StandardOutput.EndOfStream)
{
dlgSyncMessageBox.AddLine(exeProcess.StandardOutput.ReadLine());
}
dlgSyncMessageBox.AddLine("===============ERRORS================");
while (!exeProcess.StandardError.EndOfStream)
{
dlgSyncMessageBox.AddLine(exeProcess.StandardError.ReadLine());
}
exeProcess.WaitForExit();
}
}
catch (Exception exp)
{
dlgSyncMessageBox.AddLine("========EXCEPTION========");
}
}
Solution:
If I additionally set
startInfo.RedirectStandardInput = true;
then it works. I may redirect input from dialog window to the Process. Since I do not need any input for the intended XCOPY, this solution works for me without catching chars from the Dialog window and Forward to the process.
I can't see the logic, why I have to Redirect input too, but I'm glad that my Software now does what I need.

How to wait until my batch file is finished

I'm doing a program where I need to start cmd and there start up a batch file. The problem is that I'm using MyProcess.WaithForexit(); and I think it does not wait until the batch file processing is finished. It just waits until the cmd is closed. My code so far:
System.Diagnostics.ProcessStartInfo ProcStartInfo =
new System.Diagnostics.ProcessStartInfo("cmd");
ProcStartInfo.RedirectStandardOutput = true;
ProcStartInfo.UseShellExecute = false;
ProcStartInfo.CreateNoWindow = false;
ProcStartInfo.RedirectStandardError = true;
System.Diagnostics.Process MyProcess = new System.Diagnostics.Process();
ProcStartInfo.Arguments = "/c start batch.bat ";
MyProcess.StartInfo = ProcStartInfo;
MyProcess.Start();
MyProcess.WaitForExit();
I need to wait until the batch file is finished. How do I do that?
This actually worked just fine for me:
System.Diagnostics.Process.Start("myBatFile.bat").WaitForExit();
As milton said, adding 'exit' at the end of your batch files is most likely a good idea.
Cheers
The start command has arguments that can make it WAIT for the started program to complete. Edit the arguments as show below to pass '/wait':
ProcStartInfo.Arguments = "/c start /wait batch.bat ";
I would also suggest that you want your batch file to exit the cmd envirionment so place an 'exit' at the end of the batch.
#echo off
rem Do processing
exit
This should achieve the desired behavior.

problems with stdout and psexec.exe from sysinternals

i have searched and read about issues with psexec.exe from sysinternals not working properly with c# and stdout. i am now trying to figure out how to just call a batch file that has the following instead of using System.Diagnostics.Process to call psexec:
test.bat contains the following line:
psexec.exe \\hostname -u user -p password ipconfig /all >c:\test.txt
test.txt will be saved on the host where i am running my c sharp app and executing psexec.
when i execute the following:
System.Diagnostics.Process psexec_run = new System.Diagnostics.Process();
psexec_run.StartInfo.FileName = "cmd.exe";
psexec_run.StartInfo.Arguments = #"/c """ + cur_dir + #"\test\test.bat""";
psexec_run.StartInfo.UseShellExecute = false;
psexec_run.Start();
psexec_run.WaitForExit();
i see the cmd window pop up and it runs something but not sure what and goes away.
if i execute the following:
System.Diagnostics.Process psexec_run = new System.Diagnostics.Process();
psexec_run.StartInfo.FileName = cur_dir + "\\test\\psexec.exe";
psexec_run.StartInfo.Arguments = #"\\hostname -u user -p password ipconfig /all";
psexec_run.StartInfo.UseShellExecute = false;
psexec_run.Start();
psexec_run.WaitForExit();
then i see the command window open and it runs psexec which takes quite a few secs and i quickly see my output i need, but i have no way of capturing the output or writing it to a file.
i guess my issue now is since psexec will not work with stdout how can i capture the output from the psexec command to write it to a file???
see the following link for the issues with psexec, the last reply on this url mentioned a way to write the process output to a file without using stdout, i'm newbie to c# i can't figure out how to write process output without use stdout :(
http://forum.sysinternals.com/psexec-fails-to-capture-stdout-when-launched-in-c_topic19333.html
based on response below i tried the following:
ProcessStartInfo psi = new ProcessStartInfo(cur_dir + "\\test\\psexec.exe", #"\\hostname -u user -p password ipconfig /all");
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false;
Process p = Process.Start(psi);
StreamReader myStreamReader = p.StandardOutput;
// Read the standard output of the spawned process.
string sOutput = myStreamReader.ReadToEnd();
i did ReadToEnd so i would make sure it got all the output, it DID NOT!! for some reason it only go the first line of ipconfig output that was it. Also the cmd window it opened up never closed for some reason. even with CreateNoWindow=true the code just hangs. so again something is wrong with psexec and stdout i think?? as i can run this code just fine using ipconfig /all command on the local host and not use psexec...
again i am looking to avoid stdout and somehow find a way to get the output from this command or unless there is something else i'm over looking? also, not to make more work for anyone, but if you d/l psexec.exe from sysinternals and test it with a command on a remote host you will see. i have spent 2 days on this one :( trying to figure out how to use psexec or find some other quick method to execute remote command on a host and get ouptput.
UPDATE:
i gave up on psexec in c# code, i saw many posts about psexec eating the output, having a child window process ,etcc
until my head hurt :) so i am trying to run a batch file and output to a file and it's not making sense...
i have a batch file test.bat with the following
psexec.exe \\\hostname -u name -p password ipconfig /all >c:\test.txt
when i run the following code:
ProcessStartInfo psi = new ProcessStartInfo(cur_dir + #"\test\test.bat");
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false;
Process p = Process.Start(psi);
p.WaitForExit();
the cmd windows comes and goes really quickly and the test.txt file is created but is 0 bytes no info in it.
so if i run the batch file in a windows cmd line with the psexec command it works perfectly fine!!???
so then to verify psexec was the issue i changed the batch file to:
ipconfig /all >c:\test.txt
i execute my code above and it works fine creates the output in the test.txt file..???!!!!
why is not working with psexec am i missing something? if it's psexec, does anyone have
any recommendations for how i can execute a command on a remote windows host and get me the
output???
I have an answer to this problem that has worked for me.
Hopefully someone else will find it useful.
I have literally just spent the last two hours tearing my hair out with this. The psexec tool runs completely fine using a normal command prompt but when attempting to redirect the streams it truncates the output and you only get half output back.
In the end how I fixed my issue was a little bit of a hack. I piped the output of the command to a text file and read it back in to return it from the function.
I also has to set UseShellExecute to true. Without this it still wouldn't work. This had the unfortunate side effect of showing the console window. To get around that I set the window style to be hidden and hey presto it works!!!
Heres my code:
string ExecutePSExec(string command)
{
string result = "";
try
{
string location = AppDomain.CurrentDomain.BaseDirectory;
// append output to file at the end of this string:
string cmdWithFileOutput = string.Format("{0} >{1}temp.log 2>&1", command,location );
// The flag /c tells "cmd" to execute what follows and exit
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + cmdWithFileOutput);
procStartInfo.UseShellExecute = true; // have to set shell execute to true to
procStartInfo.CreateNoWindow = true;
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden; // as a window will be created set the window style to be hiddem
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
proc.WaitForExit();
// now read file back.
string filePath = string.Format("{0}temp.log", location);
result = System.IO.File.ReadAllText(filePath);
}
catch (Exception objException)
{
// Log the exception
}
return result;
}
and its usage:
string command = #"psexec.exe -l -u domain\username -p password /accepteula \\192.168.1.3 netstat -a -n";
ExecutePSExec(command);
I had exactly same problem. i was getting "Windows ip config. " as first line when i run with psexec. i tried with paexec it worked well. I used Marius's code.
Note: if you dont use first cmd / c in arguments command runs only on local computer even if you define target as \\remoteserver
ProcessStartInfo psi = new ProcessStartInfo("cmd.exe");
psi.Arguments = #"cmd/c C:\paexec.exe \\\192.168.2.5 -s -u test.local\administrator -p Password1 cmd /c ipconfig";
psi.CreateNoWindow = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false;
Process p = Process.Start(psi);
System.IO.StreamReader myStreamReader1 = p.StandardOutput;
p.WaitForExit();
string sOutput = myStreamReader1.ReadToEnd();
Are you sure your sourcecode is correct? that link is quite a bit old.. maybe its fixed!
Heres an example how to redirect the standard-output and put whole output in a string via streamreader:
ProcessStartInfo psi = new ProcessStartInfo("tftp.exe");
// preferences for tftp process
psi.CreateNoWindow = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false;
Process p = Process.Start(psi);
StreamReader myStreamReader = p.StandardOutput;
p.WaitForExit();
// Read the standard output of the spawned process.
string sOutput = myStreamReader.ReadToEnd();
i found a solution. apparently psexec is NOT going to work in c sharp. so i came up with some wmi code to connect to a remote host and it's working PERFECTLY!!! :)
i used microsoft's WMICodeCreator.exe to create wmi code for C# for the process method on a remote host, wow that tool is amazing because wmi code is little confusing to me.
psexec's output goes to StandardError and not StandardOutput. I don't know why it is that way. Following code snippet access it.
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.RedirectStandardError = true;
Process process = new Process();
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
errors = process.StandardError.ReadToEnd();
process.Close();

Execute BATCH script in a programs memory

I am currently faced with a problem, I need to execute a batch script within a programs memory (so it does not have to extract the batch file to a temporary location).
I am open to solutions in C# and C++
Any help would be appreciated
cmd.exe won't run a script from the memory of your process. The options which seem most obvious to me are:
Relax the constraint that stops you extracting the script to a temporary file.
Compress your script into a single line and use cmd.exe /C to execute it. You'll need to use the command separator &&.
Write your own batch command interpreter.
Use a different scripting language.
Options 3 and 4 aren't really very attractive! Option 1 looks pretty good to me but I don't know what's leading to your constraint.
Open a pipe to the command shell and write the program code into that pipe. Here is an example: http://support.microsoft.com/default.aspx?scid=kb;en-us;190351
In C# it's an easy way to use System.Diagnostics for the job.
How!?
Basically, every batch command is an .exe file so you can start it in a separate process.
Some code:
using System.Diagnostics;
static void Main()
{
Process batch;
batch = Process.Start("ping.exe", "localhost");
batch.WaitForExit();
batch.Close();
batch = Process.Start("choice.exe", "");
batch.WaitForExit();
batch.Close();
batch = Process.Start("ping.exe", "localhost -n 10");
batch.WaitForExit();
batch.Close();
}
If you don't want to start every command in a separate process the solution is with a simple stream redirection.
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = #"cmd.exe"; // Specify exe name.
startInfo.UseShellExecute = false;
startInfo.ErrorDialog = false;
startInfo.RedirectStandardInput = true;
//
// Start the process.
//
Process process = Process.Start(startInfo);
string[] batchFile = {"ping localhost", "ping google.com -n 10", "exit"};
int cmdIndex = 0;
while (!process.HasExited)
{
if (process.Threads.Count == 1 && cmdIndex < batchFile.Length)
{
process.StandardInput.WriteLine(batchFile[cmdIndex++]);
}
}
What's a good way to write batch scripts in C#?

C# How To Read Console Output With Parameters

Is it possible to run a console application and get its outputted contents back as a string in C#?
I want to be able to use parameters when running the console app:
c:\files\app.exe -a 1 -b 2 -c 3
This isn't the clearest thing I've read today, but I can only assume you're spawning a process (with Process.Start()?) and want to get it's output back into your program.
If so, Process.StandardOutput is probably what you're looking for. For example:
System.Diagnostics.ProcessStartInfo startInfo =
new System.Diagnostics.ProcessStartInfo(#"c:\files\app.exe",#"-a 1 -b 2 -c 3");
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
Process p = Process.Start(startInfo);
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();

Categories