I have a batch file I want to run from a C# windows form. the Batch file is very basic and accepts one parameter
cd C:\Program Files (x86)\Advent\ApxClient
AdvScriptRunner REPRUN -mrgainloss -p%1 -vf -t\\myserver\apx$\pdf\myReport
If i call it in a command prompt, this works fine
C:\Program Files (x86)\Locations\blah>realizedgainloss 123456
that will run just fine, and i get the expected result (it outputs a report run on a third party peice of software). However I cannot for the life of me figure this out with c#. I have the following.
private void button1_Click(object sender, EventArgs e)
{
ExecuteCommand(getCommand());
}
public string getCommand()
{
return "realizedgainloss.bat";
}
static void ExecuteCommand(string command)
{
int exitCode;
ProcessStartInfo processInfo;
Process process;
processInfo = new ProcessStartInfo(command);
//processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
// *** Redirect the output ***
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
processInfo.Arguments = String.Format("{0} {1}", command, "123456");
process = Process.Start(processInfo);
process.WaitForExit();
// *** Read the streams ***
// Warning: This approach can lead to deadlocks, see Edit #2
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
exitCode = process.ExitCode;
Console.WriteLine("output>>" + (String.IsNullOrEmpty(output) ? "(none)" : output));
Console.WriteLine("error>>" + (String.IsNullOrEmpty(error) ? "(none)" : error));
Console.WriteLine("ExitCode: " + exitCode.ToString(), "ExecuteCommand");
process.Close();
}
Its worth noting that if i do not provide a parameter, and change the bat file to be static, with 12345 in place of it's %1, then it runs from C#, so there is something incorrect about how i'm getting the parameters into the bat file...
any thoughts?
You have your batch file name as the command to run and the first parameter of your script. I find it easier and more reliable to use cmd.exe as the command to run and invoke it with the /C argument. Doing it this way you should make sure your working directory is set correctly as well.
processInfo = new ProcessStartInfo("cmd.exe");
processInfo.Arguments = String.Format("/C {0} {1}", command, "123456");
processInfo.WorkingDirectory = yourWorkingDirectory;
Related
I have this code
public static void ExecuteCommand(string command, string workingFolder, int TimeoutMin)
{
int ExitCode;
ProcessStartInfo ProcessInfo;
Process process;
ProcessInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
ProcessInfo.CreateNoWindow = true;
ProcessInfo.UseShellExecute = false;
ProcessInfo.WorkingDirectory = workingFolder;
// *** Redirect the output ***
ProcessInfo.RedirectStandardError = true;
ProcessInfo.RedirectStandardOutput = true;
process = Process.Start(ProcessInfo);
process.WaitForExit(TimeoutMin * 1000 * 60);
// *** Read the streams ***
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
ExitCode = process.ExitCode;
MessageBox.Show("output>>" + (String.IsNullOrEmpty(output) ? "(none)" : output));
MessageBox.Show("error>>" + (String.IsNullOrEmpty(error) ? "(none)" : error));
MessageBox.Show("ExitCode: " + ExitCode.ToString(), "ExecuteCommand");
process.Close();
}
It runs batch file in C#
it is the batch file runs long process which takes few minutes
when I call ExecuteCommand then close the application, the batch file still runs even though app is closed
Is there any way I can control running batch files from within my c# code?
According to C# process hanging due to StandardOutput.ReadToEnd() and StandardError.ReadToEnd()
and Hanging process when run with .NET Process.Start -- what's wrong?
If output is kept filling with info, your thread might be blocked.
Following lines are blocking in your operation
process.WaitForExit(TimeoutMin * 1000 * 60);
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
That is, process.Close is never called before batch is done.
If abortion is required, you need to close process somewhere else while application is exiting.
I reproduce your problem by
Pasting code snippet to a button clicked event on a small winform application.
Press the button.
UI hung <--- That means code snippet is blocking a (UI) thread.
This question already has answers here:
Trying to get StandardOutput after running a console process
(2 answers)
Closed 5 years ago.
I'm running a CMD in c# programmatically and everything works fine, it will run whatever command I input but when I ask to return the output to the console, only some commands output gets returned.
For example if I write "ipconfig" as the command, it will return my ip info but if I write "/ipconfig", it will just return "/ipconfig" as appose to "/ipconfig is not recognized as an interal ..." you get the idea. I also tried it with deleting files, the file deletes just fine but if the file doesnt exist, it should output "could not find file" like it does it normal cmd. It seems it only gives me the output of a command thats runs and not one that doesnt even though I read the cmd till the end of anything it outputs. If this is the case is there anyway to get around this?
Code:
private void Form1_Load(object sender, EventArgs e)
{
string command = "/ipconfig";
string root = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Console.WriteLine(Command(command, root));
}
public string Command(string command, string directory)
{
string commandOutput;
commandOutput = DateTime.Now + Environment.NewLine + command + Environment.NewLine;
ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + command);
procStartInfo.WorkingDirectory = directory;
procStartInfo.RedirectStandardError = true;
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
commandOutput += proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
return commandOutput;
}
I'm pretty sure cmd.exe will write that to standard error, not standard output. If that is the case, you'll need to use proc.StandardError.ReadToEnd() to get the error message.
I have a wrote a C# code that would read several .xml files(Containing executable, path, parameters. The .xml files contains a series of executable that need to be run) and several batch file will be created associate with the .xml file.
After each batch file is created, the program would execute each batch file in the C# code (using cmd of course). The problem is some of the batch file would run for days and it is a waste of memory/instants to keep the C# program running.
Is it possible to keep the .bat file running and close the C# code? Assuming that we do not need any error reporting from the C# code.
Below is the code for executing a batch file.
//Execute BatchFile
public static void ExecuteCommand(string command, string dummyPath)
{
int exitCode;
ProcessStartInfo processInfo;
Process process;
processInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
processInfo.WorkingDirectory = dummyPath;
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
process = Process.Start(processInfo);
process.WaitForExit();
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
exitCode = process.ExitCode;
Console.WriteLine("output>>" + (String.IsNullOrEmpty(output) ? "(none)" : output));
Console.WriteLine("error>>" + (String.IsNullOrEmpty(error) ? "(none)" : error));
Console.WriteLine("ExitCode: " + exitCode.ToString(), "ExecuteCommand");
process.Close();
}
If I understand you correctly, I think it is possible if you don't need any feedback from the .bat file.
I have tested this line of code on my own machine and it worked fine.
static void Main(string[] args)
{
System.Diagnostics.Process.Start(#"d:\simple.bat");
}
and for testing purpose I simply put PING www.google.com inside my .bat file.
The cmd batch process is belong to your program process, main process stop and sub processes will be killed.
I think you can try to create windows task schedule in your program, run batch in the taski, program stop and task schedule will still runnning.
Use case: I am checking certain credentials on a remote system by running commands via PsExec (i.e. for this example, I am trying to retrieve the KB articles currently installed on the remote system).
I have the following to retrieve command output:
public string GetCmDOutput(string cmd)
ProcessStartInfo startInfo = new ProcessStartInfo("control", cmd)
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
};
string output = string.Empty;
Process process = Process.Start(startInfo);
process.OutputDataReceived += (sender, e) => output = string.Concat(output, e.Data);
process.BeginOutputReadLine();
process.Start();
process.WaitForExit();
Delay.Milliseconds(1500) //API-specific delay
return output;
}
Whenever I use GetCmdOutput() to run a command locally it works like a charm, but if I try to run a command with PsExec, my output is empty.
For instance, I ran the following:
string cmd = #"\psexec.exe \\remoteComputerName -u username -p password -c cmd /c wmic qfe";
GetCmdOutput(cmd);
Report.Info(cmd); //API-specific reporting
And an empty string was returned.
After playing around with this for a couple of hours, I feel I may need a second set of eyes. What might be causing this issue?
I have run into this same problem. My solution was to run cmd and have it call psexec. I have psexec's output saved to a temp file for further manipulation. My code is returning a List.
public List<string> ExecutePSExec(string hostname)
{
List<string> recordNames = new List<string>();
string command = #"\\path\to\psexec.exe /accepteula \\" + hostname + ". exe-to-run-remotely";
try
{
string location = AppDomain.CurrentDirectory.BaseDirectory;
string cmdWithFileOutput = string.Format("{0} >{1}temp.log", command, location);
procStartInfo.UseShellExecute = true;
procStartInfo.CreateNoWindow = true;
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
Process proc = new Process();
proc.StartInfo = procStartInfo;
proc.Start();
proc.WaitForExit();
// Read file contents, manipulate data and then delete temp file here
}
catch (Exception e)
{
Console.WriteLine("Failure to run psexec: {0}", e.Message);
}
return recordNames;
}
NOTE: I ran into another problem and found out that running psexec this way requires the remote hostname (not IP Address) in the command to end in a period \\" + hostname + ".
This code assumes you can run psexec on the remote machine as your current user.
I want to execute a batch command and save the output in a string, but I can only execute the file and am not able to save the content in a string.
Batch file:
#echo off
"C:\lmxendutil.exe" -licstatxml -host serv005 -port
6200>C:\Temp\HW_Lic_XML.xml notepad C:\Temp\HW_Lic_XML.xml
C# code:
private void btnShowLicstate_Click(object sender, EventArgs e)
{
string command = "'C:\\lmxendutil.exe' -licstatxml -host lwserv005 -port 6200";
txtOutput.Text = ExecuteCommand(command);
}
static string ExecuteCommand(string command)
{
int exitCode;
ProcessStartInfo processInfo;
Process process;
processInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
// *** Redirect the output ***
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
process = Process.Start(processInfo);
process.WaitForExit();
// *** Read the streams ***
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
exitCode = process.ExitCode;
process.Close();
return output;
}
I want the output in a string and do this directly in C# without a batch file, is this possible?
Don't need to use "CMD.exe" for execute a commandline application or retreive the output, you can use "lmxendutil.exe" directly.
Try this:
processInfo = new ProcessStartInfo();
processInfo.FileName = "C:\\lmxendutil.exe";
processInfo.Arguments = "-licstatxml -host serv005 -port 6200";
//etc...
Do your modifications to use "command" there.
I hope this helps.
It doesn't look to me like your batch file will produce any output. If you run it in the command line, do you see an output? You have the redirection > operator in your bat file line, so it seems like you're sending output to the xml file.
If you have saved the output to an xml file, maybe you should just load that using C# once your process exits.