What I want to do is run a SSIS Package using this command line:
/SQL "\"\[PACKAGE_NAME]\"" /SERVER [SERVER NAME] /X86 /CHECKPOINTING OFF /REPORTING E
This is my code until now:
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Data.OleDb;
using System.Threading;
using System.Diagnostics;
namespace blablabla.csproj
{
[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
#region VSTA generated code
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
#endregion
public void Main()
{
// Variables
string targetServerName = Dts.Variables["SSIS_SERVER"].Value.ToString();
string packageExecutionName = "package_folder" + "\"" + "package_name";
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.CreateNoWindow = true;
startInfo.FileName = #"dtexec.exe";
string arguments = #"/SQL " + "\"" + "\\" + packageExecutionName + "\"" + " / SERVER " + Dts.Variables["SSIS_SERVER"].Value.ToString() + " / X86 / CHECKPOINTING OFF / REPORTING E /CONSOLELOG M";
Dts.Variables["SSIS_SERVER"].Value.ToString();
// arguments to dtexec
startInfo.Arguments = arguments;
// begin execution
Process proc = Process.Start(startInfo);
}
}
}
The script task runs with success but the package itself doesn't run, so the code must be wrong...
This is my very first time coding in C#, so apologies for any "trivial" error. I've searched on the internet but almost all of the cases it's using the specific folder where the package is saved and then run the package there, but what I need is running with this specific command line.
Thanks for your time.
I would guess that the package is never executing because the syntax for the arguments has extra spaces, like "/ X86" instead of "/X86".
If you check the error code, you can throw an exception if it is not 0. To see the details of that error, you'll need to look at StandardOutput because dtexec does not direct anything to StandardError:
public void Main()
{
//wrap the whole thing in a try..catch to report on errors
try
{
// Variables
string targetServerName = Dts.Variables["SSIS_SERVER"].Value.ToString();
string packageExecutionName = "package_folder" + "\"" + "package_name";
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.CreateNoWindow = true;
startInfo.FileName = #"dtexec.exe";
//tell the process to capture standard error
startInfo.RedirectStandardOutput = true;
string arguments = #"/SQL " + "\"" + "\\" + packageExecutionName + "\"" + " / SERVER " + Dts.Variables["SSIS_SERVER"].Value.ToString() + " / X86 / CHECKPOINTING OFF / REPORTING E /CONSOLELOG M";
Dts.Variables["SSIS_SERVER"].Value.ToString();
// arguments to dtexec
startInfo.Arguments = arguments;
// begin execution
Process proc = Process.Start(startInfo);
//capture standard output and wait for exit
string error = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
//Check the exit code and throw an error
if(proc.ExitCode != 0)
{
throw new Exception(error.ToString());
}
Dts.TaskResult = (int)ScriptResults.Success;
}
catch(Exception e)
{
//write to the ssis log
Dts.Events.FireError(1, "", e.Message, "", 0);
}
}
Related
I have a WCF rest service, and inside it I hava a execution of a Process:
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/C " + Properties.Resources.NAME_APP_IAL + " " + ...);
startInfo.WorkingDirectory = HttpContext.Current.Server.MapPath(#"" + ...);
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
Process oProcess = null;
try
{
oProcess = Process.Start(startInfo);
bool bStep = true;
while (bStep)
{
Process[] oPro = Process.GetProcessesByName(Properties.Resources.NAME_APP_IAL);
if (oPro == null || oPro.Count() == 0 ) bStep = false;
}
}
catch (Win32Exception ex)
{
throw ..
}
The process works well,
but after it finished I get a file as result ,
my problem that the process is terminated quickly so that I get empty file !
So how can I fix it : to be sure that the processus is finished or terminated at first ?
Also with this verify if I have a exception or the process is blocked or anything else, how can I recognize it ?
Thanks for your help and for your suggestion and advice,
You need to use WaitForExit. See dotnetperls.
using (Process exeProcess = Process.Start(startInfo))
{
exeProcess.WaitForExit();
}
I'm trying to execute a batch file in C#, but I'm not getting any luck doing it.
I've found multiple examples on the Internet doing it, but it is not working for me.
public void ExecuteCommand(string command)
{
int ExitCode;
ProcessStartInfo ProcessInfo;
Process Process;
ProcessInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
ProcessInfo.CreateNoWindow = true;
ProcessInfo.UseShellExecute = false;
Process = Process.Start(ProcessInfo);
Process.WaitForExit();
ExitCode = Process.ExitCode;
Process.Close();
MessageBox.Show("ExitCode: " + ExitCode.ToString(), "ExecuteCommand");
}
The command string contains the name of the batch file (stored in system32) and some files it should manipulate. (Example: txtmanipulator file1.txt file2.txt file3.txt). When I execute the batch file manually, it works correctly.
When executing the code, it gives me an **ExitCode: 1** (Catch all for general errors)
What am I doing wrong?
This should work. You could try to dump out the contents of the output and error streams in order to find out what's happening:
static void 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 ***
// 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();
}
static void Main()
{
ExecuteCommand("echo testing");
}
* EDIT *
Given the extra information in your comment below, I was able to recreate the problem. There seems to be some security setting that results in this behaviour (haven't investigated that in detail).
This does work if the batch file is not located in C:\Windows\System32. Try moving it to some other location, e.g. the location of your executable. Note that keeping custom batch files or executables in the Windows directory is bad practice anyway.
* EDIT 2 *
It turns out that if the streams are read synchronously, a deadlock can occur, either by reading synchronously before WaitForExit or by reading both stderr and stdout synchronously one after the other.
This should not happen if using the asynchronous read methods instead, as in the following example:
static void ExecuteCommand(string command)
{
var processInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
var process = Process.Start(processInfo);
process.OutputDataReceived += (object sender, DataReceivedEventArgs e) =>
Console.WriteLine("output>>" + e.Data);
process.BeginOutputReadLine();
process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) =>
Console.WriteLine("error>>" + e.Data);
process.BeginErrorReadLine();
process.WaitForExit();
Console.WriteLine("ExitCode: {0}", process.ExitCode);
process.Close();
}
System.Diagnostics.Process.Start("c:\\batchfilename.bat");
this simple line will execute the batch file.
After some great help from steinar this is what worked for me:
public void ExecuteCommand(string command)
{
int ExitCode;
ProcessStartInfo ProcessInfo;
Process process;
ProcessInfo = new ProcessStartInfo(Application.StartupPath + "\\txtmanipulator\\txtmanipulator.bat", command);
ProcessInfo.CreateNoWindow = true;
ProcessInfo.UseShellExecute = false;
ProcessInfo.WorkingDirectory = Application.StartupPath + "\\txtmanipulator";
// *** 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;
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 works fine. I tested it like this:
String command = #"C:\Doit.bat";
ProcessInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
// ProcessInfo.CreateNoWindow = true;
I commented out turning off the window so I could SEE it run.
Here is sample c# code that are sending 2 parameters to a bat/cmd file for answer this question.
Comment: how can I pass parameters and read a result of command execution?
/by #Janatbek Sharsheyev
Option 1 : Without hiding the console window, passing arguments and without getting the outputs
This is an edit from this answer /by #Brian Rasmussen
using System;
using System.Diagnostics;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
System.Diagnostics.Process.Start(#"c:\batchfilename.bat", "\"1st\" \"2nd\"");
}
}
}
Option 2 : Hiding the console window, passing arguments and taking outputs
using System;
using System.Diagnostics;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var process = new Process();
var startinfo = new ProcessStartInfo(#"c:\batchfilename.bat", "\"1st_arg\" \"2nd_arg\" \"3rd_arg\"");
startinfo.RedirectStandardOutput = true;
startinfo.UseShellExecute = false;
process.StartInfo = startinfo;
process.OutputDataReceived += (sender, argsx) => Console.WriteLine(argsx.Data); // do whatever processing you need to do in this handler
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
}
}
}
// C# decode bat file and run passing arguments: // edit 01/2022
using System;
using System.IO;
using System.Text;
using System.Diagnostics;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
String encodedString = #"QGVjaG8gb2ZmIAoKc2V0ICJ4PUZvbGRlciIKeGNvcHkgL3kgL3YgL2UgLlw
iJXglIlwqIFxcMTAuMC4wLjIwMFxkXAoKZm9yICUleSBpbiAoMjAyLDIwMy
wyMDQsMjA1KWRvICgKICAgICBuZXQgdXNlIFxcMTAuMC4wLiUlfnlcZSAiJ
X4xIiAvdXNlcjoiJX4yIgogICAgIGVjaG9cQ29weWluZyBmaWxlcyB0byBc
XDEwLjAuMC4lJX55XGVcCiAgICAgeGNvcHkgL3kgL3YgL2UgLlwiJXglIlw
qIFxcMTAuMC4wLiUlfnlcZVwKICAgICk=";
File.WriteAllBytes(#"z:\batchfilename.bat", Convert.FromBase64String(encodedString));
System.Diagnostics.Process.Start(#"z:\batchfilename.bat", "\"PassWord1\" \"User1\"");
}
}
}
/* bat file decoded:
#echo off
set "x=Folder"
xcopy /y /v /e .\"%x%"\* \\10.0.0.200\d\
for %%y in (202,203,204,205)do (
net use \\10.0.0.%%~y\e "%~1" /user:"%~2"
echo\Copying files to \\10.0.0.%%~y\e\
xcopy /y /v /e .\"%x%"\* \\10.0.0.%%~y\e\
)
Execute bat:
#"z:\batchfilename.bat", "\"PassWord1\" \"User1\""
Bat argument:
Argument %1 == PassWord1 Argument %2 == User1
*/
1. Create your bat and test it as much as possible
2. Convert the code to base64
3. Defines a variable in your code with the base64 strings
4. Decode at runtime to a pre-defined and proper location for execution
5. Call the bat execution on the path where it was decodes
6. If necessary, pass your arguments
Below code worked fine for me
using System.Diagnostics;
public void ExecuteBatFile()
{
Process proc = null;
string _batDir = string.Format(#"C:\");
proc = new Process();
proc.StartInfo.WorkingDirectory = _batDir;
proc.StartInfo.FileName = "myfile.bat";
proc.StartInfo.CreateNoWindow = false;
proc.Start();
proc.WaitForExit();
ExitCode = proc.ExitCode;
proc.Close();
MessageBox.Show("Bat file executed...");
}
using System.Diagnostics;
private void ExecuteBatFile()
{
Process proc = null;
try
{
string targetDir = string.Format(#"D:\mydir"); //this is where mybatch.bat lies
proc = new Process();
proc.StartInfo.WorkingDirectory = targetDir;
proc.StartInfo.FileName = "lorenzo.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();
proc.WaitForExit();
}
catch (Exception ex)
{
Console.WriteLine("Exception Occurred :{0},{1}", ex.Message, ex.StackTrace.ToString());
}
}
Have you tried starting it as an administrator? Start Visual Studio as an administrator if you use it, because working with .bat files requires those privileges.
With previously proposed solutions, I have struggled to get multiple npm commands executed in a loop and get all outputs on the console window.
It finally started to work after I have combined everything from the previous comments, but rearranged the code execution flow.
What I have noticed is that event subscribing was done too late (after the process has already started) and therefore some outputs were not captured.
The code below now does the following:
Subscribes to the events, before the process has started,
therefore ensuring that no output is missed.
Begins reading from outputs as soon as the process is started.
The code has been tested against the deadlocks, although it is synchronous (one process execution at the time) so I cannot guarantee what would happen if this was run in parallel.
static void RunCommand(string command, string workingDirectory)
{
Process process = new Process
{
StartInfo = new ProcessStartInfo("cmd.exe", $"/c {command}")
{
WorkingDirectory = workingDirectory,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true
}
};
process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => Console.WriteLine("output :: " + e.Data);
process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => Console.WriteLine("error :: " + e.Data);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
Console.WriteLine("ExitCode: {0}", process.ExitCode);
process.Close();
}
I wanted something that was more directly usable without organization-specific hard-coded string values in it. I offer the following as a directly reusable chunk of code. The minor downside is needing to determine and pass the working folder when making the call.
public static void ExecuteCommand(string command, string workingFolder)
{
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();
// *** 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();
}
Called like this:
// This will get the current WORKING directory (i.e. \bin\Debug)
string workingDirectory = Environment.CurrentDirectory;
// This will get the current PROJECT directory
string projectDirectory = Directory.GetParent(workingDirectory).Parent.FullName;
string commandToExecute = Path.Combine(projectDirectory, "TestSetup", "WreckersTestSetupQA.bat");
string workingFolder = Path.GetDirectoryName(commandToExecute);
commandToExecute = QuotesAround(commandToExecute);
ExecuteCommand(commandToExecute, workingFolder);
In this example, from within Visual Studio 2017, as part of a test run, I want to run an environment reset batch file before executing some tests. (SpecFlow+xUnit). I got tired of extra steps for manually running the bat file separately, and wanted to just run the bat file as part of the C# test setup code. The environment reset batch file moves test case files back into the input folder, cleans up output folders, etc. to get to the proper test starting state for testing. The QuotesAround method simply puts quotes around the command line in case there are spaces in folder names ("Program Files", anyone?). All that's in it is this: private string QuotesAround(string input) {return "\"" + input + "\"";}
Hope some find this useful and save a few minutes if your scenario is similar to mine.
System.Diagnostics.Process.Start(BatchFileName, Parameters);
I know this will work for batch file and parameters, but no ideas how to get the results in C#.
Usually, the outputs are defined in the batch file.
Using CliWrap:
var result = await Cli.Wrap("foobar.bat").ExecuteBufferedAsync();
var exitCode = result.ExitCode;
var stdOut = result.StandardOutput;
I'm running an app that uses a console app in the background
When I do this in cmd read-info.exe Myfile.file >fileinfo.txt it will create the file fileinfo.txt in the route folder.
But when I do it in code nothing happens, why?
private void button1_Click(object sender, EventArgs e)
{
FolderBrowserDialog theDialog = new FolderBrowserDialog();
theDialog.RootFolder = System.Environment.SpecialFolder.MyComputer;
if (theDialog.ShowDialog() == DialogResult.OK)
{
textBox1.Text = theDialog.SelectedPath.ToString();
string command = "read-info.exe " + textBox1.Text +"> File.txt";
string retur = CMD(command);
}
}
static string CMD(string args)
{
string cmdbat = "cd " + Application.StartupPath.Replace("\\", "/") + "\r\n";
cmdbat += args + " >> out.txt\r\n";
cmdbat += "exit\r\n";
File.WriteAllText("cmd.bat", cmdbat);
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.Arguments = "";
startInfo.UseShellExecute = true;
startInfo.WorkingDirectory = Application.StartupPath;
startInfo.CreateNoWindow = true;
startInfo.FileName = Application.StartupPath + "\\cmd.bat";
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
System.Threading.Thread.Sleep(5000);
string cmdOut = File.ReadAllText("out.txt");
File.Delete("cmd.bat");
File.Delete("out.txt");
return cmdOut;
}
See this - I tnink it is what necessary: http://www.dotnetperls.com/redirectstandardoutput
Copied from the link above:
using System;
using System.Diagnostics;
using System.IO;
class Program
{
static void Main()
{
//
// Setup the process with the ProcessStartInfo class.
//
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = #"C:\7za.exe"; // Specify exe name.
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
//
// Start the process.
//
using (Process process = Process.Start(start))
{
//
// Read in all the text from the process with the StreamReader.
//
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
}
}
use cmd /C for running console commands.
string command = "cmd /C read-info.exe " + textBox1.Text +"> File.txt";
Got It Working Thanks to #crashmstr
it outputs file as out.txt so just had to comment out File.Delete("out.txt");
I have used the following code to copy the folder from one path to another path. If the copying file is already exists it does not replace the existing file. Is there any command available with this like xcopy/replace?
private static void ProcessXcopy(string SolutionDirectory, string TargetDirectory)
{
// Use ProcessStartInfo class
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
//Give the name as Xcopy
startInfo.FileName = "xcopy";
//make the window Hidden
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
//Send the Source and destination as Arguments to the process
startInfo.Arguments = "\"" + SolutionDirectory + "\"" + " " + "\"" + TargetDirectory + "\"" + #" /e /y /I";
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(startInfo))
{
exeProcess.WaitForExit();
}
}
catch (Exception exp)
{
throw exp;
}
}
Referrence: http://www.c-sharpcorner.com/UploadFile/jawedmd/xcopy-using-C-Sharp-to-copy-filesfolders/
xcopy should overwrite files by default (and the /Y flag suppresses prompts for confirmation). Could it be that the destination file is read-only? In that case, you also need to specify the /R flag.
The note at http://support.microsoft.com/kb/240268 describes /R as an additional parameter to overwrite read-only files. Have you tried adding that?
I'm trying to execute a batch file in C#, but I'm not getting any luck doing it.
I've found multiple examples on the Internet doing it, but it is not working for me.
public void ExecuteCommand(string command)
{
int ExitCode;
ProcessStartInfo ProcessInfo;
Process Process;
ProcessInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
ProcessInfo.CreateNoWindow = true;
ProcessInfo.UseShellExecute = false;
Process = Process.Start(ProcessInfo);
Process.WaitForExit();
ExitCode = Process.ExitCode;
Process.Close();
MessageBox.Show("ExitCode: " + ExitCode.ToString(), "ExecuteCommand");
}
The command string contains the name of the batch file (stored in system32) and some files it should manipulate. (Example: txtmanipulator file1.txt file2.txt file3.txt). When I execute the batch file manually, it works correctly.
When executing the code, it gives me an **ExitCode: 1** (Catch all for general errors)
What am I doing wrong?
This should work. You could try to dump out the contents of the output and error streams in order to find out what's happening:
static void 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 ***
// 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();
}
static void Main()
{
ExecuteCommand("echo testing");
}
* EDIT *
Given the extra information in your comment below, I was able to recreate the problem. There seems to be some security setting that results in this behaviour (haven't investigated that in detail).
This does work if the batch file is not located in C:\Windows\System32. Try moving it to some other location, e.g. the location of your executable. Note that keeping custom batch files or executables in the Windows directory is bad practice anyway.
* EDIT 2 *
It turns out that if the streams are read synchronously, a deadlock can occur, either by reading synchronously before WaitForExit or by reading both stderr and stdout synchronously one after the other.
This should not happen if using the asynchronous read methods instead, as in the following example:
static void ExecuteCommand(string command)
{
var processInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
var process = Process.Start(processInfo);
process.OutputDataReceived += (object sender, DataReceivedEventArgs e) =>
Console.WriteLine("output>>" + e.Data);
process.BeginOutputReadLine();
process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) =>
Console.WriteLine("error>>" + e.Data);
process.BeginErrorReadLine();
process.WaitForExit();
Console.WriteLine("ExitCode: {0}", process.ExitCode);
process.Close();
}
System.Diagnostics.Process.Start("c:\\batchfilename.bat");
this simple line will execute the batch file.
After some great help from steinar this is what worked for me:
public void ExecuteCommand(string command)
{
int ExitCode;
ProcessStartInfo ProcessInfo;
Process process;
ProcessInfo = new ProcessStartInfo(Application.StartupPath + "\\txtmanipulator\\txtmanipulator.bat", command);
ProcessInfo.CreateNoWindow = true;
ProcessInfo.UseShellExecute = false;
ProcessInfo.WorkingDirectory = Application.StartupPath + "\\txtmanipulator";
// *** 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;
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 works fine. I tested it like this:
String command = #"C:\Doit.bat";
ProcessInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
// ProcessInfo.CreateNoWindow = true;
I commented out turning off the window so I could SEE it run.
Here is sample c# code that are sending 2 parameters to a bat/cmd file for answer this question.
Comment: how can I pass parameters and read a result of command execution?
/by #Janatbek Sharsheyev
Option 1 : Without hiding the console window, passing arguments and without getting the outputs
This is an edit from this answer /by #Brian Rasmussen
using System;
using System.Diagnostics;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
System.Diagnostics.Process.Start(#"c:\batchfilename.bat", "\"1st\" \"2nd\"");
}
}
}
Option 2 : Hiding the console window, passing arguments and taking outputs
using System;
using System.Diagnostics;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var process = new Process();
var startinfo = new ProcessStartInfo(#"c:\batchfilename.bat", "\"1st_arg\" \"2nd_arg\" \"3rd_arg\"");
startinfo.RedirectStandardOutput = true;
startinfo.UseShellExecute = false;
process.StartInfo = startinfo;
process.OutputDataReceived += (sender, argsx) => Console.WriteLine(argsx.Data); // do whatever processing you need to do in this handler
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
}
}
}
// C# decode bat file and run passing arguments: // edit 01/2022
using System;
using System.IO;
using System.Text;
using System.Diagnostics;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
String encodedString = #"QGVjaG8gb2ZmIAoKc2V0ICJ4PUZvbGRlciIKeGNvcHkgL3kgL3YgL2UgLlw
iJXglIlwqIFxcMTAuMC4wLjIwMFxkXAoKZm9yICUleSBpbiAoMjAyLDIwMy
wyMDQsMjA1KWRvICgKICAgICBuZXQgdXNlIFxcMTAuMC4wLiUlfnlcZSAiJ
X4xIiAvdXNlcjoiJX4yIgogICAgIGVjaG9cQ29weWluZyBmaWxlcyB0byBc
XDEwLjAuMC4lJX55XGVcCiAgICAgeGNvcHkgL3kgL3YgL2UgLlwiJXglIlw
qIFxcMTAuMC4wLiUlfnlcZVwKICAgICk=";
File.WriteAllBytes(#"z:\batchfilename.bat", Convert.FromBase64String(encodedString));
System.Diagnostics.Process.Start(#"z:\batchfilename.bat", "\"PassWord1\" \"User1\"");
}
}
}
/* bat file decoded:
#echo off
set "x=Folder"
xcopy /y /v /e .\"%x%"\* \\10.0.0.200\d\
for %%y in (202,203,204,205)do (
net use \\10.0.0.%%~y\e "%~1" /user:"%~2"
echo\Copying files to \\10.0.0.%%~y\e\
xcopy /y /v /e .\"%x%"\* \\10.0.0.%%~y\e\
)
Execute bat:
#"z:\batchfilename.bat", "\"PassWord1\" \"User1\""
Bat argument:
Argument %1 == PassWord1 Argument %2 == User1
*/
1. Create your bat and test it as much as possible
2. Convert the code to base64
3. Defines a variable in your code with the base64 strings
4. Decode at runtime to a pre-defined and proper location for execution
5. Call the bat execution on the path where it was decodes
6. If necessary, pass your arguments
Below code worked fine for me
using System.Diagnostics;
public void ExecuteBatFile()
{
Process proc = null;
string _batDir = string.Format(#"C:\");
proc = new Process();
proc.StartInfo.WorkingDirectory = _batDir;
proc.StartInfo.FileName = "myfile.bat";
proc.StartInfo.CreateNoWindow = false;
proc.Start();
proc.WaitForExit();
ExitCode = proc.ExitCode;
proc.Close();
MessageBox.Show("Bat file executed...");
}
using System.Diagnostics;
private void ExecuteBatFile()
{
Process proc = null;
try
{
string targetDir = string.Format(#"D:\mydir"); //this is where mybatch.bat lies
proc = new Process();
proc.StartInfo.WorkingDirectory = targetDir;
proc.StartInfo.FileName = "lorenzo.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();
proc.WaitForExit();
}
catch (Exception ex)
{
Console.WriteLine("Exception Occurred :{0},{1}", ex.Message, ex.StackTrace.ToString());
}
}
Have you tried starting it as an administrator? Start Visual Studio as an administrator if you use it, because working with .bat files requires those privileges.
With previously proposed solutions, I have struggled to get multiple npm commands executed in a loop and get all outputs on the console window.
It finally started to work after I have combined everything from the previous comments, but rearranged the code execution flow.
What I have noticed is that event subscribing was done too late (after the process has already started) and therefore some outputs were not captured.
The code below now does the following:
Subscribes to the events, before the process has started,
therefore ensuring that no output is missed.
Begins reading from outputs as soon as the process is started.
The code has been tested against the deadlocks, although it is synchronous (one process execution at the time) so I cannot guarantee what would happen if this was run in parallel.
static void RunCommand(string command, string workingDirectory)
{
Process process = new Process
{
StartInfo = new ProcessStartInfo("cmd.exe", $"/c {command}")
{
WorkingDirectory = workingDirectory,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true
}
};
process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => Console.WriteLine("output :: " + e.Data);
process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => Console.WriteLine("error :: " + e.Data);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
Console.WriteLine("ExitCode: {0}", process.ExitCode);
process.Close();
}
I wanted something that was more directly usable without organization-specific hard-coded string values in it. I offer the following as a directly reusable chunk of code. The minor downside is needing to determine and pass the working folder when making the call.
public static void ExecuteCommand(string command, string workingFolder)
{
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();
// *** 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();
}
Called like this:
// This will get the current WORKING directory (i.e. \bin\Debug)
string workingDirectory = Environment.CurrentDirectory;
// This will get the current PROJECT directory
string projectDirectory = Directory.GetParent(workingDirectory).Parent.FullName;
string commandToExecute = Path.Combine(projectDirectory, "TestSetup", "WreckersTestSetupQA.bat");
string workingFolder = Path.GetDirectoryName(commandToExecute);
commandToExecute = QuotesAround(commandToExecute);
ExecuteCommand(commandToExecute, workingFolder);
In this example, from within Visual Studio 2017, as part of a test run, I want to run an environment reset batch file before executing some tests. (SpecFlow+xUnit). I got tired of extra steps for manually running the bat file separately, and wanted to just run the bat file as part of the C# test setup code. The environment reset batch file moves test case files back into the input folder, cleans up output folders, etc. to get to the proper test starting state for testing. The QuotesAround method simply puts quotes around the command line in case there are spaces in folder names ("Program Files", anyone?). All that's in it is this: private string QuotesAround(string input) {return "\"" + input + "\"";}
Hope some find this useful and save a few minutes if your scenario is similar to mine.
System.Diagnostics.Process.Start(BatchFileName, Parameters);
I know this will work for batch file and parameters, but no ideas how to get the results in C#.
Usually, the outputs are defined in the batch file.