Calling Batch File From C# - 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.

Related

How to running batch that require updating path?

I want to use C# to call a batch script to install and run programs. This requires me to use two instances, one for the installation and one for the actual run, so that the path is updated. Here is my try:
var proc = new Process();
proc.StartInfo.FileName = #"Resources\Install Git.bat";
proc.StartInfo.UseShellExecute = true;
proc.Start();
proc.WaitForExit();
proc.StartInfo.FileName = #"Resources\Clone repo.bat";
proc.Start();
However in the second instance (Clone repo.bat), the path is still not updated, so that it "is not recognized as an internal or external command, operable program or batch file". Why is it so?
At the end of the day, I just set the path variable right in the script.

How to combine multiple gz files into one from Process in C# program when one is missing EOF

I have multiple .gz files in a directory (2 or more), with at least one file missing the end of file marker. Our C# process is unable to read the file with missing end of file, but since they are coming from a third party we do not have control over how they are created.
As such, we've been running the following Linux command manually:
cat file1.gz file2.gz > newFile.gz
In order to automate this, I am looking for a way to leverage the Process functionality in C# to trigger the same command, but this would only be available in Cygwin or some other Linux shell. In my example, I'm using git bash but it could be Powershell or Cygwin or any other available Linux shell that runs on a Windows box.
The following code does not fail, but it does not work as expected. I am wondering if anyone has recommendations about how to do this or any suggestions on a different approach to consider?
Assume that the working directory is set and initialized successfully, so the files exist where the process is run from.
Process bashProcess = new Process();
bashProcess.StartInfo.FileName = #"..\Programs\Git\git-bash.exe";
bashProcess.StartInfo.UseShellExecute = false;
bashProcess.StartInfo.RedirectStandardInput = true;
bashProcess.StartInfo.RedirectStandardOutput = true;
bashProcess.Start();
bashProcess.StandardInput.WriteLine("cat file1.gz file2.gz > newFile.gz");
bashProcess.StandardInput.WriteLine("exit");
bashProcess.StandardInput.Flush();
.
.
.
bashProcess.WaitForExit();
My expectation is that newFile.gz is created
I was able to find a solution to my problem using a DOS command, and spawning a cmd Process from CSharp.
My code now looks like this, avoids having to launch a linux-based shell from Windows, and the copy command in windows does the same thing as cat:
Process proc = new Process();
proc.EnableRaisingEvents = false;
proc.StartInfo.FileName = "cmd";
proc.StartInfo.Arguments = #"/C pushd \\server\folder && copy *.txt.gz /b
combined.gz";
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.UseShellExecute = false;
proc.Start();
proc.WaitForExit();
System.Threading.Thread.Sleep(2000);
string line = proc.StandardOutput.ReadLine();
while (line != null)
{
output.Append(line);
line = proc.StandardOutput.ReadLine();
}

How can i add some bat files to my project so i will not need them anymore on the hard disk?

For example i have this code:
Process proc = new Process();
proc.EnableRaisingEvents = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.FileName = "dxdiag.bat";
proc.StartInfo.CreateNoWindow = true;
proc.Start();
proc.WaitForExit();
proc.Close();
I did a test if i delete the file dxdiag.bat from my project \debug directory im getting an exception on the line:
proc.Start();
That the file is not found.
Once the file is in the \debug directory its working.
I want to add the dxdiag.bat file to my project so if i send to someone else my program he will be able run my program and the process will run the dxdiag.bat from the program it self so the one i sent the program wont need the bat file on his own hard disk.
As long as you have no variables or nonsingleline-commands you can run this function with each line of the bat-file
internal static void executeCommand(string command, bool waitForExit,
bool hideWindow, bool runAsAdministrator)
{
System.Diagnostics.ProcessStartInfo psi =
new System.Diagnostics.ProcessStartInfo("cmd", "/C " + command);
if (hideWindow)
{
psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
}
if (runAsAdministrator)
{
psi.Verb = "runas";
}
if (waitForExit)
{
System.Diagnostics.Process.Start(psi).WaitForExit();
}
else
{
System.Diagnostics.Process.Start(psi);
}
}
so you can also save the text in the code
string bat_file = #"#echo off\ncopy c:\users\Desktop\filea.txt c:\users\Desktop\fileb.txt\n...";
otherwise i would create a temporary bat file, run it and delete it.
The easiest solution is to add the bat file to the project in Visual Studio. To do this do the next steps:
Copy the bat file to the directory that holds the project.
Add the file to the project.
Set the property "Build Action" to "Content".
Set the property "Copy to output directory" to "Copy Always".
This will copy the bat fill to the output directory of the build. If you then distribute your program you can send the output directory. This will not prevent the user form deleting the bat file of course.
The other option would be to embed the batch file as a resource in your project and use it from there.
Add dxdiag.bat to your project by right-clicking on the project and select add existing item. Then, once it is in your project, right-click the dxdiag.bat file and select properties. Set the "copy to output directory" property to "always".
What you want to do is embed the .bat file as an embedded resource as coshmos suggested. To do this you need to right click on the .bat file in the solution explorer, select properties, and then under "Build Action" section select "Embedded Resource". After that, assuming your bat file is in the program's root directory, the following code should work:
string batFileName = "dxdiag.bat";
string programName = Assembly.GetExecutingAssembly().GetName().Name;
using (Stream input = Assembly.GetExecutingAssembly().GetManifestResourceStream(programName + "." + batFileName))
{
using (TextReader tr = new StreamReader(input))
{
File.WriteAllText(batFileName, tr.ReadToEnd());
}
}
Process proc = new Process();
proc.EnableRaisingEvents = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.FileName = batFileName;
proc.StartInfo.CreateNoWindow = true;
proc.Start();
proc.WaitForExit();
proc.Close();
The first half of this code pulls the bat file out of the embedded resources and saves it as an actual, but temporary, bat file in the programs root directory. After that it is basically the same as your code.
Additionally if you add File.Delete(batFileName); after this code, it will automatically delete the temporary bat file it created.
// create the dxdiag.bat file
using (StreamWriter sw = new StreamWriter("dxdiag.bat"))
{
sw.WriteLine(".........");
// ......
}
Process proc = new Process();
proc.EnableRaisingEvents = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.FileName = "dxdiag.bat";
proc.StartInfo.CreateNoWindow = true;
proc.Start();
proc.WaitForExit();
proc.Close();
//delete the file
File.Delete("dxdiag.bat");
You can embed your .bat files. When your need them, you check that these files are existed. If no, you copy them from embedded resources to %appdata%\YourAppName and then use.
How to read embedded resources: How to read embedded resource text file
Description of embedded resources from the Microsoft: http://support.microsoft.com/kb/319292

How to run VBS script from C# code

All what I am trying to do is to be able to run VBS script from code behind but I am getting this error: “The system cannot find the file specified”. I know the path name and I only need to execute that .vbs script but it is giving me hard time and I am not able to figure out. Please help. Thanks
here is my code
System.Diagnostics.Process.Start(#"cscript //B //Nologo \\loc1\test\myfolder\test1.vbs");
i have updated the code as shown below but i am getting a security warning asking me if i want to open it. Is there a way to not get those kind of warning and just run script without any warnings?
here is the updated code:
Process proc = null;
try
{
string targetDir = string.Format(#"\\loc1\test\myfolder");//this is where mybatch.bat lies
proc = new Process();
proc.StartInfo.WorkingDirectory = targetDir;
proc.StartInfo.FileName = "test1.vbs";
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());
}
Your code working fine for me, I think the error was in your File Path,
Better Confirm the File Path you given is valid or Not..
You can run that file like below also..
Process scriptProc = new Process();
scriptProc.StartInfo.FileName = #"cscript";
scriptProc.StartInfo.Arguments ="//B //Nologo \\loc1\test\myfolder\test1.vbs";
scriptProc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
scriptProc.Start();
scriptProc.WaitForExit();
scriptProc.Close();
But check your File Path you given..
The parameters have to be included separately. There is an arguments field you use to pass arguments to the process.
You can use this as a guide for executing programs with command line from an application.

Service hangs up at WaitForExit after calling batch file

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. ;)

Categories

Resources