在 C # 中执行批处理文件

我正在尝试用 C # 执行一个批处理文件,但是运气不太好。

我在互联网上发现了很多这样做的例子,但是对我来说并不奏效。

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");
}

命令字符串包含批处理文件的名称(存储在 system32中)和它应该操作的一些文件。(例子: txtmanipulator file1.txt file2.txt file3.txt)。当我手动执行批处理文件时,它可以正常工作。

在执行代码时,它会给我一个 **ExitCode: 1** (Catch all for general errors)

我做错了什么?

448218 次浏览

它工作得很好,我是这样测试的:

String command = @"C:\Doit.bat";


ProcessInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
// ProcessInfo.CreateNoWindow = true;

我把窗户关掉了,这样我就能看到它运行了。

这个应该可以。您可以尝试输出输出和错误流的内容,以便查明正在发生的情况:

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");
}

* 编辑 *

考虑到你在下面的评论中提供的额外信息,我能够重现这个问题。似乎有一些安全设置导致了这种行为(还没有详细调查)。

如果批处理文件不在 C:\Windows\System32中,则此 是的可以工作。尝试将它移动到其他位置,例如可执行文件的位置。请注意,在 Windows 目录中保留自定义批处理文件或可执行文件无论如何都是不好的做法。

* 编辑2 * 原来认为,如果流被同步读取,死锁可能发生,或者通过在 WaitForExit之前同步读取,或者通过一个接一个同步读取 stderrstdout

如果使用异步读取方法,则不应该发生这种情况,如下例所示:

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();
}

在斯坦纳的大力帮助下,这个方法对我很有效:

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();
}
System.Diagnostics.Process.Start("c:\\batchfilename.bat");

这个简单的行将执行批处理文件。

你试过以管理员的身份开始吗?如果使用 VisualStudio,请以管理员身份启动它,因为使用 .bat文件需要这些特权。

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());
}
}

下面的代码对我来说很好用

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

我想要一些更直接可用的东西,不需要特定于组织的硬编码字符串值。我将以下代码作为可直接重用的代码块提供。次要的缺点是在进行调用时需要确定并传递工作文件夹。

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();
}

叫做:

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

在此示例中,在 VisualStudio2017中,作为测试运行的一部分,我希望在执行某些测试之前运行环境重置批处理文件。(SpecFlow + xUnit).我已经厌倦了单独手动运行 bat 文件的额外步骤,希望只运行 bat 文件作为 C # 测试安装代码的一部分。环境重置批处理文件将测试用例文件移回到输入文件夹,清理输出文件夹等,以获得适当的测试开始状态进行测试。QuotesAround 方法只是在命令行周围加上引号,以防文件夹名中有空格(“ Program Files”,有人吗?).它所包含的全部内容是: 私有字符串 QuotesAround (字符串输入){返回“”“ + input +”“”; }

希望有些人会觉得这个很有用,如果你的场景和我的类似,那么就可以节省几分钟的时间。

System.Diagnostics.Process.Start(BatchFileName, Parameters);

我知道这对批处理文件和参数有用,但是不知道如何在 C # 中获得结果。 通常,输出是在批处理文件中定义的。

下面是示例 c # 代码,它向 bat/cmd 文件发送2个参数以回答这个 [问题][1]问题。

评论: 如何传递参数并读取命令执行的结果?

/由 [@Janatbek Sharsheyev ][3]

选项1: 不隐藏控制台窗口、传递参数和不获得输出

  • 这是一个编辑从这个[答案][2]/由[@Brian Rasmussen ][4]

using System;
using System.Diagnostics;




namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
System.Diagnostics.Process.Start(@"c:\batchfilename.bat", "\"1st\" \"2nd\"");
}
}
}

选项2: 隐藏控制台窗口、传递参数和获取输出


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();
}
}
}

编辑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
*/

创建你的球棒并尽可能多地测试它

2. 转换代码到 base64

3. 在代码中用 base64字符串定义一个变量

4. 运行时解码到预定义的适当位置以便执行

在它被解码的路径上调用 bat 的执行

如果必要的话,传递你的论点


_ _ _
[1]: https://stackoverflow.com/questions/60207122/passing-arguments-to-a-cmd-file-in-c-sharp/60211584#60211584
[2]: https://stackoverflow.com/a/3742287/8177207
[3]: https://stackoverflow.com/users/6097430/janatbek-sharsheyev
[4]: https://stackoverflow.com/users/38206/brian-rasmussen

在以前提出的解决方案中,我一直在努力使多个 npm 命令在循环中执行,并在控制台窗口中获得所有输出。

在我合并了前面注释中的所有内容,但是重新安排了代码执行流程之后,它终于开始工作了。

我注意到事件订阅完成得太晚(在流程已经启动之后) ,因此一些输出没有被捕获。

下面的代码现在执行以下操作:

  1. 在进程启动之前订阅事件, 因此,确保没有产出错过。
  2. 一旦流程启动,就开始从输出中读取。

代码已经针对死锁进行了测试,尽管它是同步的(一次执行一个进程) ,所以我不能保证如果并行运行会发生什么。

    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();
}

使用 胶卷:

var result = await Cli.Wrap("foobar.bat").ExecuteBufferedAsync();


var exitCode = result.ExitCode;
var stdOut = result.StandardOutput;