i'm trying to execute a .bat file through a c# console application using code from here:
Service hangs up at WaitForExit after calling batch file
Kevin's solution kinda works, but some commands in my .bat file get ignored for some reason, but when i manually execute the .bat file all commands work just fine.
e.g. xcopy command doesn't work while executing .bat from console app, but start command works fine.
Any idea why this happens?
p.s. recently i found that if the program is being launched from command prompt, it works well. How come? Still i need to put it on autorun, so this doesn't solve the problem.
Also, if launched by clicking on exe file, output shows
xcopy folder1 folder2
but if launched from command prompt, output shows
xcopy folder1 folder2
smth/smth.smth copied
....
5 files copied.
And it actually is being copied.
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();
Batch file commands do not get ignored when you execute the command processor from a service. They however easily fail and can do so unobserved since you cannot see their output. Typical reasons for failure are having the ProcessStartInfo.WorkingDirectory not set correctly so that relative paths no longer work or trouble caused by the service running with a different user account that doesn't have the same rights as the one you use from the desktop.
Diagnose problems by redirecting output to a file, run cmd.exe /c and use the > operator. Append 2>&1 so that the file contains both regular and error output. And by using the %errorlevel% variable and EXIT command judiciously so you can discover that execution failed by using the Process.ExitCode property.
Related
I am trying to silently read and write to a .cmd file from a C# console application. The .cmd file looks like this :
ECHO OFF
set Input=""
set /p IsCustom="Do you want to create a custom deploy package ? (Y/N)"
set /p Input="Enter product name (press enter for none):"
ECHO ON
cd .\DeployScript
IF /I "%IsCustom%" == "Y" (
nant -buildfile:Deploy.build -D:environment=Disk Deploy.LocalRelease -D:productname=%Input%
cd ..
GOTO END
)
nant -buildfile:Deploy.build -D:environment=Disk Deploy.NewLocalRelease -D:productname=%Input%
cd ..
:END
This is where I want to insert the values :
set /p IsCustom="Do you want to create a custom deploy package ? (Y/N)"
set /p Input="Enter product name (press enter for none):"
Here is what I tried in C# console application, but I am not able to read the above two questions and write to them:
private static void ExecuteCmdFile()
{
Process process = new Process();
process.EnableRaisingEvents = true;
process.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_OutputDataReceived);
process.ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_ErrorDataReceived);
process.Exited += new System.EventHandler(process_Exited);
process.StartInfo.FileName = Path + #"\createPackage.cmd";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
}
static void process_Exited(object sender, EventArgs e)
{
Console.WriteLine(string.Format("process exited with code {0}\n", ""));
}
static void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
//Console.WriteLine(e.Data + "\n");
}
I am unable to read the questions at process_OutputDataReceived() and have no clues on inserting values. Just wanted to know if I am reading/writing to the .cmd file from C# application correctly ? Am I missing something here or is there any other approach ?
The Windows Command Processor cmd.exe processing a batch file is not designed for communication with other processes. It is designed for executing commands and executables one after the other and supports simple IF conditions and GOTO to control the sequence of command/program executions and FOR for doing something repeated in a loop. That´s it.
I suggest modifying the batch file to this code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "IsCustom=%~1"
if defined IsCustom goto CheckInput
%SystemRoot%\System32\choice.exe /C NY /N /M "Do you want to create a custom deploy package (Y/N)?"
if errorlevel 2 (set "IsCustom=Y") else set "IsCustom=N"
:CheckInput
set "Input=%~2"
if not defined Input goto InputPrompt
if /I "%Input%" == "/N" set "Input="
goto ProcessData
:InputPrompt
set /P "Input=Enter product name (press enter for none): "
if not defined Input goto ProcessData
set "Input=%Input:"=%"
:ProcessData
cd /D "%~dp0DeployScript"
if /I "%IsCustom%" == "Y" (
nant -buildfile:Deploy.build -D:environment=Disk Deploy.LocalRelease -D:productname="%Input%"
goto END
)
nant -buildfile:Deploy.build -D:environment=Disk Deploy.NewLocalRelease -D:productname="%Input%"
:END
endlocal
Now the batch file can be executed without any argument in which case the user is prompted twice with evaluation of the input in a safe and secure manner.
But it is also possible to run the batch file with one or two arguments from another executable like a program coded in C# or from within a command prompt window or called by another batch file.
The first argument is assigned to the environment variable IsCustom which is explicitly undefined on batch file executed without any argument or with "" as first argument. The IF condition used later with referencing the string value of the environment variable IsCustom just checks if the string value is Y or y to do the custom action. Any other argument string passed as first argument to the batch file results in running the standard action.
The second argument is assigned to the environment variable Input which is also explicitly undefined on batch file executed without any argument or with "" as second argument. If the second argument is case-insensitive equal /N, then the batch file interprets this as explicit request to use no product name.
The Yes/No prompt is done using command CHOICE which is highly recommended for such choice prompts if the batch file is called without any argument or with "" as first argument.
The second prompt is done using set /P if the batch file is called without a second argument or with just "" as second argument. Double quotes are removed from input string if the user inputs a string at all for safe and secure processing of the input string by the remaining lines.
The directory DeployScript is most likely a subdirectory of the directory containing the batch file and for that reason the command CD is used with option /D to change if needed also the current drive and makes explicitly the subdirectory DeployScript the current directory independent on which directory is the current directory on starting the execution of the batch file.
nant should be referenced with file extension and if possible with full path if the full path is well known because of being relative to batch file path or is fixed making it possible to use the fully qualified file name in the batch file as it is done for external command CHOICE.
The command ENDLOCAL results in making the initial current directory again the current directory. SETLOCAL pushes the current directory path on stack. ENDLOCAL pops that path from stack and makes this directory again the current directory (if not deleted in the meantime which is not possible here). For that reason the execution of cd .. is not needed at all.
Now the C# coded application can run cmd.exe with the options /D and /C and the batch file name with its full path with the two arguments Y or N and product name or /N. There is no need anymore to communicate with cmd.exe processing the batch file or passing the arguments via standard input stream to the internal command SET of cmd.exe.
Note:
I do not really understand why the C# Process class is used being a C# wrapper class for the Windows kernel function CreateProcess called with using the STARTUPINFO structure to run cmd.exe to process a batch file. It would be also possible to use the Process class to run nant directly by the C# coded program and of course also any other executable which the batch file perhaps runs additionally. A C# coded application has direct access to all Windows library functions used by cmd.exe on processing a batch file. So there is really no need in my opinion to use a batch file at all for the task according to the provided information about the task.
For understanding the used commands in the batch file and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /? ... explains batch file argument referencing
choice /?
cmd /?
echo /?
endlocal /?
goto /?
if /?
set /?
setlocal /?
I am trying to get the output of a process. I've achieved this by using a .bat, which contains the following text:
program.exe > output.txt < input.txt
where program.exe is my executable, output.txt is the file I want to output the data to and input.txt is just an empty file since the program wants key inputs from time to time. The .bat file works perfectly when I run it myself, but when I attempt to run it using C# code, it just doesn't finish properly.
I am trying to run it using the following code:
Process.Start(path);
I've tried a lot of different stuff but this is the code that I last tried but none of my attempts worked. Also, the .bat file doesn't run properly when you run it as an administrator and the C# program I am using is requiring administrator permissions. Could this be the issue and not the actual process running?
You need to run the process cmd.exe /c [path] (aka the process is "cmd.exe", and your arguments are $"/c {path}".
When you're on the command prompt you get "program execution" and ".bat/.cmd interactive scripting". Since you want to execute a batch file you need to tell CreateProcess that cmd.exe is what's actually running it.
In ProcessStartInfo you should set UseShellExecute to false, then set WorkingDirectory in the path where you found that the bat is working correctly.
Below is an example to make you understand better:
using (Process MyProcess = new Process())
{
var MyProcessInfo = new ProcessStartInfo()
{
UseShellExecute = false,
WorkingDirectory = "path",
FileName = "file.exe",
Arguments = "args"
};
MyProcess.StartInfo = MyProcessInfo;
MyProcess.Start();
}
The issue was just what I thought. The .bat file doesn't work properly when launched from an elevated app. To fix it I used this:
Process.Start("explorer.exe", path);
Worked perfectly. Fixed it really quickly but thought I'd leave this here if anybody finds himself stuck with the same thing.
So here's my end goal. Steam allows you to add other installed games to your library but only if it's a .exe file it's pointed at to start running.
I just installed Arena and Daggerfall and they both run via DOSBox which is launched from a .bat file. So I decided to turn it into a .exe by writing my own. So far I've got the code written. When I just run the .bat file, it opens everything fine, however, when I try running it from my code, the .bat file executes but with errors. Here is my code below:
if (File.Exists("D:\\Bethesda Softworks\\Arena\\Arena (Full Screen).bat"))
{
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo("cmd.exe", "/c" + "\"D:\\Bethesda Softworks\\Arena\\Arena (Full Screen).bat\"");
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
System.Diagnostics.Process proc;
proc = System.Diagnostics.Process.Start(psi);
}
And the error I'm getting is this:
"The system cannot find the path specified. 'dosbox.exe' is not recognized as an internal or external command, operable program or batch file.
I'm not sure if this is an issue because of how dosbox is called from the Batch file or how the .exe ends up running the batch file. Either way, I'd rather fix this in the code rather than by making alterations to the .bat file itself.
Any help is greatly appreciated!!
Try setting ProcessStartInfo.WorkingDirectory
psi.WorkingDirectory = "D:\\Bethesda Softworks\\Arena";
I have a scenario in which i have to create a windows service which will check that if the batch file is not running then it should execute that batch file.
Moreover, my batch file is used for automation using Selenium for Application checkout.
When i create that service, somehow the batch file is not executing and is not able to launch the selenium. When i see same in task manager, i can see a cmd instance is running but that cannot run my selenium.
Code of my Project: Timer is set to 1 minute
private void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (!File.Exists(ConfigurationManager.AppSettings["testFilePath"])) //To check whether batch file is running or not
{
System.Diagnostics.Process proc = new System.Diagnostics.Process(); // Declare New Process
proc.StartInfo.FileName = ConfigurationManager.AppSettings["BatchFilePath"];
proc.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
proc.StartInfo.CreateNoWindow = true;
proc.Start();
proc.WaitForExit();
}
}
Batch File is like This:
copy C:\AutomatedTests\AHD\testingWS.txt C:\AutomatedTests\AHD\AHDExecutionService\testingWS.txt
start cmd /k java -jar "C:\AHS_Automation\Release\UnifiedBillingSystem\Selenium RC\selenium-server-1.0.3\selenium-server.jar"
This code will check periodically that if batch file is not under execution then my service will start its execution otherwise will do nothing.
Not sure with so little context... but the first thing I would check is the path that executes the bat file with respect to the files that the bat file uses; if you're calling the bat file from another program, it normally inherits the environment folder from that program - which is something that most people don't expect. (for a windows service this is windows\system32) The easiest way to check if this is the case is by setting the path explicitly in the first line of the bat file (e.g. cd [path of bat file] )
public void runBatchfile(String batchfilename)
{
try
{
ProcessStartInfo processInfo = new ProcessStartInfo(batchfilename);
processInfo.UseShellExecute = false;
Process batchProcess = new Process();
batchProcess.StartInfo = processInfo;
batchProcess.StartInfo.CreateNoWindow = true;
batchProcess.Start();
batchProcess.WaitForExit();
}
catch (Exception r) { }
}
runBatchfile(#"c:\lol.bat");
lol.bat contains these 2 lines
dir c:\ /s /b > c:\filelist.txt
exit
and when I run my code all it does is creating a filelist.txt file, but doesn't actually perform the rest of the command, which does work if I manually insert it into CMD.
Btw I've tried making the extension .cmd and I also tried without the exit command, without any other results.
please help me :)
On my Windows 7 64-bit machine with Visual Studio 2010, running this code straight from the IDE doesn't do anything whatsoever.
On a hunch that it might have something to do with permission to write to the root directory of drive C:, I tried running the .exe directly from an Explorer window with admin rights (right-click, Run as Administrator) and that worked.
I'd say it's a permission problem.
Maybe you could redirect the output to a file located elsewhere?
update:
I changed the batch file so that the dir command gets redirected to my desktop and it runs just fine.
I've had issues with this as well when calling from C#. The workaround that I usually use is to physically allow it to show the window when running the command. Not sure why this makes a difference but it has worked for me in the past.