157

我正在尝试在 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)

我究竟做错了什么?

4

12 回答 12

208

这应该有效。您可以尝试转储输出和错误流的内容,以了解发生了什么:

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 目录中无论如何都是不好的做法。

* EDIT 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();
}
于 2011-04-01T22:25:59.257 回答
139
System.Diagnostics.Process.Start("c:\\batchfilename.bat");

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

于 2012-04-12T12:26:37.837 回答
20

在 steinar 的大力帮助下,这对我有用:

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();
}
于 2011-04-02T00:33:49.640 回答
15

它工作正常。我是这样测试的:

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

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

我注释掉关闭窗口,以便我可以看到它运行。

于 2011-04-01T22:13:44.123 回答
10

这是将 2 个参数发送到 bat/cmd 文件以回答此[question][1]的示例 c# 代码。

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

/by [@Janatbek Sharsheyev][3]

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

  • 这是对此 [answer][2] /by [@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();
        }
    }
}

// C# 解码 bat 文件并运行传递参数: // 编辑 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.创建你的球棒并尽可能多地测试它

2.将代码转换为base64

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

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

5.在解码路径上调用bat执行

6.如有必要,传递你的论点


_ _ _ 
[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
于 2020-02-13T16:57:50.653 回答
4

下面的代码对我来说很好

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...");
}
于 2016-07-29T10:26:09.590 回答
3
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());
    }
}
于 2016-07-28T06:10:00.090 回答
1

您是否尝试过以管理员身份启动它?如果使用 Visual Studio,请以管理员身份启动它,因为处理.bat文件需要这些权限。

于 2014-04-17T16:53:33.973 回答
1

使用之前提出的解决方案,我一直在努力让多个 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();
    }
于 2020-04-14T07:54:50.820 回答
0

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

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

在此示例中,在 Visual Studio 2017 中,作为测试运行的一部分,我想在执行某些测试之前运行环境重置批处理文件。(SpecFlow+xUnit)。我厌倦了单独手动运行 bat 文件的额外步骤,只想将 bat 文件作为 C# 测试设置代码的一部分运行。环境重置批处理文件将测试用例文件移回输入文件夹,清理输出文件夹等,以达到正确的测试开始状态以进行测试。QuotesAround 方法只是在命令行周围加上引号,以防文件夹名称中有空格(“程序文件”,有人吗?)。里面的内容是这样的:私有字符串 QuotesAround(string input) {return "\"" + input + "\"";}

如果您的情况与我的情况相似,希望有些人觉得这很有用并节省几分钟。

于 2019-01-31T19:12:51.753 回答
0

使用CliWrap

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

var exitCode = result.ExitCode;
var stdOut = result.StandardOutput;
于 2020-04-15T17:01:28.833 回答
-1

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

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

于 2019-08-07T15:19:03.643 回答