Running batch file with arguments from C# - c#

I have a batch file like this
#echo off
xcopy /e %1 %2
I have my C# code as follows:
string MyBatchFile = #"C:\Program Files (x86)\MybatchFile.bat";
string _sourcePath = #"C:\FolderToCopy";
string _tempTargetPath = #"C:\TargetFolder\";
var process = new Process {
StartInfo = {
Arguments = string.Format("{0} {1}",
_sourcePath,
_tempTargetPath)
}
};
process.StartInfo.FileName = MyBatchFile;
bool b = process.Start();
I expect this to copy the source files to target location. But nothing happens. My console window also does not stay for enough time so that I can see the error. Can anyone guide to achieve this. I am new in batch files processing.
Edit
By adding a pause in the end of batch file. Able to reproduce error. Getting error as
Files not found - Program
Running batch file directly does work fine. Just now noticed......when source path has any spaces....I am getting error

What about quoting argument?
Arguments = String.Format("\"{0}\" \"{1}\"", _sourcePath, _tempTargetPath) …

.bat file is a text file, in order to execute it, you should start cmd process.
Start it like this:
System.Diagnostics.Process.Start("cmd.exe", "/c yourbatch.bat");
Additional arguments may follow. Try this without c#, in a cmd window, or Run dialog.

try
string MyBatchFile = #"C:\MybatchFile.bat";
string _sourcePath = #"C:\FolderToCopy\*.*";
string _tempTargetPath = #"C:\TargetFolder\";
i.e. add *.* to the source path
and add a 3rd line pause to the batch file
#echo off
copy /e %1 %2
pause

Related

Calling cmd process doesn't appear to accept path in argument

I have a an application that runs daily and creates a log file; after a period of X days, the log files should be removed which I'm running at the end of all of the other code elements. Originally, this was being completed via PowerShell script however the client cannot run PowerShell code due to ExecutionPolicy on the machine that this application will be running. So, I'm attempting to convert to cmd process. Current code for log removal:
string arg2 = ConfigFile.OutputPaths.Log; //configuration value for log path
int arg2 = ConfigFile.DeleteLogsAfter; //configuration value for days
var processStartInfo = new ProcessStartInfo()
{
FileName = #"cmd.exe",
UseShellExecute = false,
Arguments = $"FORFILES /P \"{arg1}\" /S /M *.* /D -{arg2} /C \"cmd /c del #path\""
};
Process process = Process.Start(processStartInfo);
process.WaitForExit(1000);
This produces the following error:
Could Not Find C:\code\exe_name\debug\#path
In debug, when I view the argument, which is:
FORFILES /P "C:\Work\Clients\Test Files\Logs" /S /M *.* /D -13 /C "cmd /c del #path"
that is being created, if I copy and paste into a command prompt window, it will execute and perform the deletion as expected.
Any ideas how why it isn't accepting the path argument in the process as it does in cmd?
From forfiles help:
There are some disadvantages to using CMD.exe with FORFILES, a new process will be created and destroyed for every file that FORFILES processes, so if you loop through 1000 files, then 1000 copies of CMD.exe will be opened and closed, this will affect performance. Also any variable created with SET will be lost as soon as FORFILES moves on to the next file.
In most cases using a simple FOR command along with Parameter extensions will provide a better/less buggy solution.
Could you do someting like that:
using System. IO;
​
string[] files = Directory. GetFiles(dirName);
​
foreach (string file in files)
{
FileInfo fi = new FileInfo(file);
if (fi. LastAccessTime < DateTime. Now. AddDays(-10)) ...

How to capture "Root Path" into a variable

I am trying to run logman.exe for a elevated CMD, for this below code I tried,
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = #"C:\Windows\System32\cmd.exe",
Arguments = "cmd /k logman.exe PerfCounterCustom | findstr \"Root\"",
Verb = "runas",
UseShellExecute = true,
}
};
try
{
proc.Start();
while (!proc.StandardOutput.EndOfStream)
{
string line = proc.StandardOutput.ReadLine();
}
Console.WriteLine("Successfully elevated!");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
And it's giving error output like,
System.InvalidOperationException: StandardOut has not been redirected or the process hasn't started yet.
at System.Diagnostics.Process.get_StandardOutput()
2 Questions,
when I am running application exe, it's showing 2 CMD window, the 1st one showing error and 2nd one showing result for argument "cmd /k logman.exe PerfCounterCustom | findstr \"Root\"" [Root Path]
how to disable showing both window?
Why I am getting this error?
To your 1st Question: In the ProcessStartInfo set WindowStyle to ProcessWindowStyle.Hidden
An alternative solution to read the output of the command is to write the output to a text file. Therefore you have to add >> "[Name or Path of file].txt" to the end of your command. Then just read the file from C# e.g. with File.ReadAllLines.
Two things to consider here:
If you do that often at Runtime and the command delivers huge amounts of text don't write it to an SSD.
Please check that the file is empty / not existing before, because Windows just appends the output to the end of the file. If you run that in multiple threads use a thread identifier in the file name.
You have to set RedirectStandardOutput of the ProcessStartInfo to true and you have to run proc.WaitForExit() before reading the output.
Please note that this solution causes incompatibilities with running the process as administrator via runas.

MVC executing a LibreOffice conversion

Hi I'm trying to convert either a doc or docx to a pdf in a c# MVC application. I know I can do this using libreOffice. So I created a simple batch file to take 2 variables and then run them into the libreoffice 'soffice' headless to convert to pdf.
So that gave me this code.
echo on
SET var1=%2
IF "%var1:~-1%"=="\" SET var1=%var1:~0,-1%
cd "C:\Program Files\LibreOffice 5\program\"
soffice --headless --convert-to pdf %1 --outdir %var1%
Originally I thought the problem was within my MVC application and the way I called this batch script. But I commented (REM) the soffice and outputted out the command in the bash using the standard output.
var psi = new ProcessStartInfo("cmd.exe", "/k " + command);
//psi.CreateNoWindow = true;
psi.FileName = command;
psi.UseShellExecute = false;
psi.RedirectStandardError = true;
psi.RedirectStandardOutput = true;
psi.Arguments = string.Format("{0} {1}", fullPath2, tempPath);
var process = Process.Start(psi);
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
Trace.WriteLine(output);
Trace.WriteLine(error);
process.WaitForExit();
When I commented the soffice line - it hit the WaitForExit and worked no problems (ok with no pdf conversions, but the script exited).
If I don't do that it seems to execute the cmd and even the soffice commands because I can see them in the task manager - but obvisouly nothing happens.
Additionally the code above works when I did a c# command line program (I've hard coded the file/command lines in both instances). The executable also works when I run as the user that is running the app pool in my MVC application.
The bash file also works file 'standalone' no matter if me or my appPool user run it.
So what gives - why won't this run.
This is the code that comes out of that trace - so what the bash script does.
c:\windows\system32\inetsrv>echo on
c:\windows\system32\inetsrv>SET var1=C:\inetpub\xxxxxxxxx\Temp\
c:\windows\system32\inetsrv>IF "\" == "\" SET var1=C:\inetpub\xxxxxxxxx\Temp
c:\windows\system32\inetsrv>cd "C:\Program Files\LibreOffice 5\program\"
C:\Program Files\LibreOffice 5\program>soffice --headless --convert-to pdf C:\inetpub\xxxxxxxxx\Temp\636295920370843147.doc --outdir C:\inetpub\xxxxxxxxx\Temp
I've got a feeling that this has something to do with the amount of characters or something because the soffice does fireup (can see it in the task manager).
FYI there are no spaces or special characters anywhere.
Any ideas?
Update
This looks to be an issue with the wait command. So any help with that helpful, I'm starting to think perhaps this is an issue with c# and libreoffice 5 - I've seen examples that supposedly work with libreoffice 4.
I guess my challenge continues....

Deleting directory that contains items

I am trying to delete a directory using C#. The first method I tried was
Directory.Delete(#"C:\Program Files (x86)\Qmuzki32");
I get an exception stating that the directory is not empty. I then found a cmd command which I can use to delete the directory quietly regardless of the fact that the directory is empty or not. I ran the following command in cmd:
rmdir /s /q "C:/Program Files (x86)/Qmuzik32"
This worked and did exactly what I wanted it to do. With my first attempt I tried building this command into a C# process like so:
if (Directory.Exists(#"C:\Program Files (x86)\Qmuzik32"))
{
string sQM32Folder = #"C:\Program Files (x86)\Qmuzik32";
Process del = new Process();
del.StartInfo.FileName = "cmd.exe";
del.StartInfo.Arguments = string.Format("rmdir /s /q \"{0}\"", sQM32Folder);
del.WaitForExit();
}
This did not work and then I tried it like this:
if (Directory.Exists(#"C:\Program Files (x86)\Qmuzik32"))
{
string sQM32Folder = #"C:\Program Files (x86)\Qmuzik32";
Process del = new Process();
del.StartInfo.FileName = "rmdir.exe";
del.StartInfo.Arguments = string.Format("/s /q \"{0}\"", sQM32Folder);
del.WaitForExit();
}
Same problem. I get the exception:
No process is associated with this object.
I do think I am on the right track; maybe the code above just requires some tweaking.
Just use Directory.Delete(string, bool).
While the low-level filesystem APIs of course require you to make sure the directory is empty first, any half-decent framework abstracting them allows you do do a recursive delete. In fact, existence of such a method would be the first thing I'd check before even trying to resort to external programs.
If you want to use the cmd way you can use this:
ProcessStartInfo Info = new ProcessStartInfo();
Info.Arguments = "/C rd /s /q \"C:\\Program Files (x86)\\Qmuzik32\"";
Info.WindowStyle = ProcessWindowStyle.Hidden;
Info.CreateNoWindow = true;
Info.FileName = "cmd.exe";
Process.Start(Info);
del.Start();
del.WaitForExit();
you didn't start the procces so it doesn't have a PID so it dies

How to send series of commands to a command window process?

We have a few commands(batch files/executables) on our network path which we have to call to initialize our 'development environment' for that command window. It sets some environmental variables, adds stuff to the Path etc. (Then only whatever working commands we type will be recognized & I don't know what goes inside those initializing commands)
Now my problem is, I want to call a series of those 'working commands' using a C# program, and certainly, they will work only if the initial setup is done. How can I do that? Currently, I'm creating a batch file by scratch from the program like this for example:
file.Writeline("InitializationStep1.bat")
file.Writeline("InitializeStep2.exe")
file.Writeline("InitializeStep3.exe")
Then the actual commands
file.Writeline("Dowork -arguments -flags -blah -blah")
file.Writeline("DoMoreWork -arguments -flags -blah -blah")
Then finally close the file writer, and run this batch file.
Now if I directly execute this using Process.<strike>Run</strike>Start("cmd.exe","Dowork -arguments"); it won't run.
How can I achieve this in a cleaner way, so that I have to run the initialization commands only once? (I could run cmd.exe each time with all three initializers, but they take a lot of time so I want to do it only once)
As #Hakeem has pointed out, System.Diagnostic.Process does not have a static Run method. I think you are referring to the method Start.
Once you have completed building the batch file, then simply execute it using the following code,
Process p = new Process();
p.StartInfo.FileName = batchFilePath;
p.StartInfo.Arguments = #"-a arg1 -b arg2";
p.Start();
Note that the # symbol is required to be prefixed to the argument string so that escape sequence characters like \ are treated as literals.
Alternative code
Process.Start(batchFilePath, #"-a arg1 -b arg2");
or
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = batchFilePath;
processStartInfo.Arguments = #"-a arg1 -b arg2";
Process.Start(processStartInfo);
More information
Process.Start method
Example of multi command batch file
dir /O
pause
dir
pause
Save this file as .bat and then execute using the Start method. In this case you can specify the argument with the command in the batch file itself (in the above example, the /O option is specified for the dir command.
I suppose you already have done the batch file creation part, now just append the arguments to the commands in the batch file.
Redirecting Input to a process
Since you want to send multiple commands to the same cmd process, you can redirect the standard input of the process to the take the input from your program rather than the keyboard.
Code is inspired from a similar question at: Execute multiple command lines with the same process using C#
private string ProcessRunner()
{
ProcessStartInfo processStartInfo = new ProcessStartInfo("cmd.exe");
processStartInfo.RedirectStandardInput = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.UseShellExecute = false;
Process process = Process.Start(processStartInfo);
if (process != null)
{
process.StandardInput.WriteLine("dir");
process.StandardInput.WriteLine("mkdir testDir");
process.StandardInput.WriteLine("echo hello");
//process.StandardInput.WriteLine("yourCommand.exe arg1 arg2");
process.StandardInput.Close(); // line added to stop process from hanging on ReadToEnd()
string outputString = process.StandardOutput.ReadToEnd();
return outputString;
}
return string.Empty;
}
The method returns the output of the command execution. In a similar fashion, you could also redirect and read the StandardOuput stream of the process.
The Process.Run method that you mentioned, is that from the Process class in System.Diagnostics namespace? AFAIK, the Process type doesn't have either a static or instance method named Run. If you haven't already I'd try with the Start method on Process, either instance or static

Categories