简体   繁体   English

在 C# 中执行批处理文件

[英]Executing Batch File in C#

I'm trying to execute a batch file in C#, but I'm not getting any luck doing it.我正在尝试在 C# 中执行一个批处理文件,但我没有得到任何运气。

I've found multiple examples on the Internet doing it, but it is not working for me.我在 Internet 上找到了多个这样做的示例,但它对我不起作用。

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.命令字符串包含批处理文件的名称(存储在system32中)和它应该操作的一些文件。 (Example: txtmanipulator file1.txt file2.txt file3.txt ). (示例: 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)执行代码时,它给了我一个**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 .如果批处理文件不在C:\\Windows\\System32确实有效。 Try moving it to some other location, eg the location of your executable.尝试将其移动到其他位置,例如可执行文件的位置。 Note that keeping custom batch files or executables in the Windows directory is bad practice anyway.请注意,将自定义批处理文件或可执行文件保存在 Windows 目录中无论如何都是不好的做法。

* 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. * 编辑 2 * 事实证明,如果同步读取流,可能会发生死锁,要么在WaitForExit之前同步读取,要么一个接一个同步读取stderrstdout

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

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

After some great help from steinar this is what worked for me:在 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();
}

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 .这是示例 c# 代码,它们将 2 个参数发送到 bat/cmd 文件以回答这个问题

Comment: how can I pass parameters and read a result of command execution?评论:如何传递参数并读取命令执行结果?

/by @Janatbek Sharsheyev /by @Janatbek Sharsheyev

Option 1 : Without hiding the console window, passing arguments and without getting the outputs选项 1:不隐藏控制台窗口,传递参数且不获取输出

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

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.如果使用 Visual Studio,请以管理员身份启动它,因为使用.bat文件需要这些权限。

With previously proposed solutions, I have struggled to get multiple npm commands executed in a loop and get all outputs on the console window.使用以前提出的解决方案,我一直在努力让多个 npm 命令在循环中执行并在控制台窗口上获得所有输出。

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:下面的代码现在执行以下操作:

  1. Subscribes to the events, before the process has started, therefore ensuring that no output is missed.在流程开始之前订阅事件,从而确保不会丢失任何输出。
  2. 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.在此示例中,在 Visual Studio 2017 中,作为测试运行的一部分,我想在执行一些测试之前运行环境重置批处理文件。 (SpecFlow+xUnit). (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.我厌倦了单独手动运行 bat 文件的额外步骤,只想将 bat 文件作为 C# 测试设置代码的一部分运行。 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?). QuotesAround 方法只是在命令行周围加上引号,以防文件夹名称中有空格(“程序文件”,有人吗?)。 All that's in it is this: private string QuotesAround(string input) {return "\\"" + input + "\\"";}它的全部内容是: private string QuotesAround(string input) {return "\\"" + input + "\\"";}

Hope some find this useful and save a few minutes if your scenario is similar to mine.如果您的场景与我的相似,希望有些人觉得这很有用并节省几分钟。

Using CliWrap :使用CliWrap

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

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

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#.我知道这适用于批处理文件和参数,但不知道如何在 C# 中获得结果。 Usually, the outputs are defined in the batch file.通常,输出在批处理文件中定义。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM