I am writing a program that is running a batch file, and will need the output further in the program. this is my C# code:
public void ExecuteBatFile()
{
Process proc = null;
try
{
string targetDir = string.Format("C:\\Users"); //this is where mybatch.bat lies
proc = new Process();
proc.StartInfo.WorkingDirectory = targetDir;
proc.StartInfo.FileName = "batch.bat";
proc.StartInfo.Arguments = string.Format("10"); //this is argument
proc.StartInfo.CreateNoWindow = false;
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; //this is for hiding the cmd window...so execution will happen in back ground.
proc.Start();
string output = proc.StandardOutput.ReadToEnd();
Console.WriteLine(output);
proc.WaitForExit();
}
catch (Exception ex)
{
Console.WriteLine("Exception Occurred :{0},{1}", ex.Message, ex.StackTrace.ToString());
}
}
}
Once I run I get this error though:
Exception thrown: 'System.InvalidOperationException' in System.dll
Exception Occurred :StandardOut has not been redirected or the process
hasn't started yet., at
System.Diagnostics.Process.get_StandardOutput() at
CefSharp.MinimalExample.Wpf.CallBatchFile.ExecuteBatFile() in
C:\Users\CefSharp.MinimalExamplemaster\CefSharp.MinimalExample.Wpf\CallBatchFile.cs:line
27
The batch file runs successfully, but then I'm not able to store the result into a variable. I can't get it to work in anyway. Has anyone got any suggestion?
This is my moch batch:
#echo off
cd C:\users\934874
echo.>silvio.txt
title This is your first batch script!
echo Welcome to batch scripting!
if "%errorlevel%"=="0" cls &Echo Success.
if "%errorlevel%"=="1" cls &Echo Fail
pause
The output that I'm expecting is either Success / Fail at console, and store that as a string into a variable.
Thanks in advance for your help
Need to redirect the output
proc.StartInfo.RedirectStandardOutput = true;
https://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardoutput(v=vs.110).aspx
Quoting from
https://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.useshellexecute(v=vs.110).aspx
When UseShellExecute is false, the WorkingDirectory property is not used to find the executable. Instead, it is used only by the process that is started and has meaning only within the context of the new process. When UseShellExecute is false, the FileName property can be either a fully qualified path to the executable, or a simple executable name that the system will attempt to find within folders specified by the PATH environment variable.
One of the simplest way is to make your batch file write its output to a text file. Then your main program can read the text file after the batch has finished.
If you really want to read from the standard output stream, please take a look at this other SO post:
Capturing console output from a .NET application (C#)
I am hoping that this is an easy question, but i have the following code in my C# application and for some reason it will not execute the batch file I am pointing to.
private void filesystemwatcher_Renamed(object sender, System.IO.RenamedEventArgs e)
{
if (File.Exists("C:\\Watcher\\File.txt"))
{
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.EnableRaisingEvents = false;
proc.StartInfo.FileName = "C:\\Watcher\\Cleanup.bat";
proc.Start();
MessageBox.Show("Cleaned up files, your welcome.");
}
else
{
label4.Text = "Error: No file found";
}
}
It will display the messagebox correctly so I know that it is reaching that area of code, but I do not see a cmd box pop up or anything that would show that it just ran the batch file. I can also tell because cleanup.bat just renames a file and that's it. After I get the messagebox the file name hasn't changed.
If I double click the batch file manually it works just fine. I have also adjusted the permissions of the batch file to Full Control for everyone (just for testing purposes)
This should work
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName = "C:\\Watcher\\Cleanup.bat";
proc.StartInfo.WorkingDirectory = "C:\\Watcher";
proc.Start();
You need to set the WorkingDirectory otherwise the command will be executed in what is the current directory of the calling application
Try setting the proc.StartInfo.UseShellExecute to true; this tells the OS to perform a lookup of the file extension to find the correct handler in the registry.
When I execute a process through and try to redirect the output/error, I get the following error:
System.ComponentModel.Win32Exception (0x80004005): Access is denied
at System.Diagnostics.Process.CreatePipe(SafeFileHandle& parentHandle, SafeFileHandle& childHandle, Boolean parentInputs)
at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
...
What could be wrong? Here is a repro:
string path = "C:\\batch.cmd";
using (Process proc = new Process())
{
bool pathExists = File.Exists(path);
if(!pathExists) throw new ArgumentException("Path doesnt exist");
proc.StartInfo.FileName = path;
proc.StartInfo.WorkingDirectory = workingDir.FullName;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.Start(); //Exception thrown here
proc.WaitForExit();
}
No decent reason for this to fail, the code has not yet gotten to a point where it would do anything security-sensitive. This is environmental, something on your machine is interfering. Reboot first, disable anti-malware next. If that doesn't help then use TaskMgr.exe, Processes tab and arbitrarily start killing processes, with some luck you'll hit the evil-doer. Ask questions about getting this machine stable again at superuser.com
You have to make sure that the account that execute your program have the rights to execute the program your trying to launch with the process.start, and that the account have the rights to create a pipe on the system .
HAve you tried to remove the redirectOutput ? If without redirecting the output you dont get the exception means that your user can't create a pipe, so you have to give this right to the user .
This should have the full file path and file name, trying to start a folder will result in this error.
string path = "C:\\test.exe";
proc.StartInfo.FileName = path;
Also does the application have administrative privileges?
Edit: if it is a batch file, it needs to have the extension .bat such as "batch.bat" to be run properly. Also if it is a batch file, it cannot be empty or else it will throw an exception.
I'm trying to run a Jscript task from a C# console application.
The Jscipt file is not mine so I can't change it. The script moves some files and this is what is causing the issues.
When I run the script manually, i.e. form the shell it executes correctly. When I try and run the script from my console application the bulk of the process runs but I get a ":Error = Permission denied" error when it tries to move the files.
I've tried every permutation of the Diagnostics.Process class that I can think of but I've had no luck.
My current code:
Process process = new Process();
process.StartInfo.WorkingDirectory = Path.GetDirectoryName((string)path);
process.StartInfo.FileName = #"cmd.exe";
process.StartInfo.Arguments = "/C " + (string)path;
process.StartInfo.UseShellExecute = false;
process.StartInfo.Verb = "runas";
process.StartInfo.LoadUserProfile = true;
process.StartInfo.Domain = "admin";
process.StartInfo.UserName = #"cardax_sync_test";
process.StartInfo.Password = GetSecureString("abc123");
process.Start();
process.WaitForExit();
Any ideas?
Thanx
Rookie Mistake!
I forgot to close the text reader that creates one of the input files for the jscript.
I'll submit this question for deletion when it get's old enough. Don't want more useless info clogging up the net!
I have a service that sometimes calls a batch file. The batch file takes 5-10 seconds to execute:
System.Diagnostics.Process proc = new System.Diagnostics.Process(); // Declare New Process
proc.StartInfo.FileName = fileName;
proc.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
proc.StartInfo.CreateNoWindow = true;
proc.Start();
proc.WaitForExit();
The file does exist and the code works when I run the same code in-console. However when it runs inside the service, it hangs up at WaitForExit(). I have to kill the batch file from the Process in order to continue. (I am certain the file exists, as I can see it in the processes list.)
How can I fix this hang-up?
Update #1:
Kevin's code allows me to get output. One of my batch files is still hanging.
"C:\EnterpriseDB\Postgres\8.3\bin\pg_dump.exe" -i -h localhost -p 5432 -U postgres -F p -a -D -v -f "c:\backupcasecocher\backupdateevent2008.sql" -t "\"public\".\"dateevent\"" "DbTest"
The other batch file is:
"C:\EnterpriseDB\Postgres\8.3\bin\vacuumdb.exe" -U postgres -d DbTest
I have checked the path and the postgresql path is fine. The output directory does exist and still works outside the service. Any ideas?
Update #2:
Instead of the path of the batch file, I wrote the "C:\EnterpriseDB\Postgres\8.3\bin\pg_dump.exe" for the proc.StartInfo.FileName and added all parameters to proc.StartInfo.Arguments. The results are unchanged, but I see the pg_dump.exe in the process window. Again this only happens inside the service.
Update #3:
I have run the service with a user in the administrator group, to no avail. I restored null for the service's username and password
Update #4:
I created a simple service to write a trace in the event log and execute a batch file that contains "dir" in it. It will now hang at proc.Start(); - I tried changing the Account from LocalSystem to User and I set the admnistrator user and password, still nothing.
Here is what i use to execute batch files:
proc.StartInfo.FileName = target;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.UseShellExecute = false;
proc.Start();
proc.WaitForExit
(
(timeout <= 0)
? int.MaxValue : timeout * NO_MILLISECONDS_IN_A_SECOND *
NO_SECONDS_IN_A_MINUTE
);
errorMessage = proc.StandardError.ReadToEnd();
proc.WaitForExit();
outputMessage = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
I don't know if that will do the trick for you, but I don't have the problem of it hanging.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace VG
{
class VGe
{
[STAThread]
static void Main(string[] args)
{
Process proc = null;
try
{
string targetDir = string.Format(#"D:\adapters\setup");//this is where mybatch.bat lies
proc = new Process();
proc.StartInfo.WorkingDirectory = targetDir;
proc.StartInfo.FileName = "mybatch.bat";
proc.StartInfo.Arguments = string.Format("10");//this is argument
proc.StartInfo.CreateNoWindow = false;
proc.Start();
proc.WaitForExit();
}
catch (Exception ex)
{
Console.WriteLine("Exception Occurred :{0},{1}", ex.Message,ex.StackTrace.ToString());
}
}
}
}
string targetDir = string.Format(#"D:\");//PATH
proc = new Process();
proc.StartInfo.WorkingDirectory = targetDir;
proc.StartInfo.FileName = "GetFiles.bat";
proc.StartInfo.Arguments = string.Format("10");//argument
proc.StartInfo.CreateNoWindow = false;
proc.Start();
proc.WaitForExit();
Tested,works clear.
What does the batch file do? Are you certain the process is getting launched with enough privs to execute the batch file? Services can be limited in what they are allowed to do.
Also make sure if you are doing something like usin the copy command to overwrite a file that you do something like:
echo Y | copy foo.log c:\backup\
Also, make sure you are using full paths for the batch commands, etc. If the batch file is launching a GUI app in some sort of "Console" mode, that may be an issue too. Remember, services don't have a "Desktop" (unless you enable the "interact with desktop") to draw any kind of windows or message boxes to. In your program, you might want to open the stdout and stderr pipes and read from them during execution in case you are getting any error messages or anything.
WebServices are probably executing as the IUSR account, or the anonymous account, which ever, so that might be an issue for you. If it works when you run it in console, that's just the first step. :)
I don't recall if System.Diagnostics. are available only in debug or not. Probably not, but some of them might be. I'll have to check up on that for ya.
Hope this gives you some ideas.
Larry
pg_dump.exe is probably prompting for user input. Does this database require authentication? Are you relying on any ENVIRONMENT variables that won't be present for the service? I don't know pg_dump but what are the other possible reasons it would prompt for input?
The next step I would take is to fire up the debugger, and see if you can tell what the program is waiting on. If you are expierenced at debugging in assembly, you may be able to get an IDEA of what's happening using tools like ProcExp, FileMon, etc.
Being a windows SERVICE, and not a web service, makes quite a bit of difference. Anyways, have you tried my suggestion of setting the "Allow Service to interact with desktop"?
If you are desperate, you might try launching cmd.exe instead of your batch file. Then, using the cmd.exe's cmd line parameters, you can have IT start the batch file. This would probably give you a cmd prompt window to view the actual output, if you turn on the interact with desktop.
For complete help on cmd.exe, just type cmd /? at any command prompt.
Larry
Here is the solution. The solution is not clear because I have changed so many time the code and now it's working!
I have tried to use a Account of User, and it's not what worked. Use LocalSystem. Here is the code that execute, mostly what Kevin gave me.
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName = fileName;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.UseShellExecute = false;
proc.Start();
proc.WaitForExit();
output1 = proc.StandardError.ReadToEnd();
proc.WaitForExit();
output2 = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
Thank you all, I'll up-vote everybody and accept Kevin since he helps me since the beginning. Very weird because it works now...
Daok, it looks as if the only thing you changed was the timeout period on the initial WaitForExit(). You need to be VERY careful of that. If something DOES hang your service, it will NEVER return (and well, pretty much work like it has been for you thus far.. heh), but it won't be good for the end users...
Now, perhaps that you know what's causing this to hang, you can debug it further and find the full solution...
That, or spin this off in some thread that you can monitor, and kill if it hangs too long.
Just my 2 cents worth, which usually isn't a whole lot. ;)