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ディレクトリに保持することは、とにかく悪い習慣であることに注意してください。

*編集2*ストリーム同期的 に読み取られる場合、前に同期的に読み取るかWaitForExit、両方stderrを読み取り、stdout次々に同期的に読み取ることによって、デッドロックが発生する可能性があることがわかりました。

次の例のように、代わりに非同期読み取りメソッドを使用する場合、これは発生しないはずです。

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 に答える
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 に答える
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)。バット ファイルを個別に手動で実行するための余分な手順にうんざりしていたので、バット ファイルを C# テスト セットアップ コードの一部として実行したいと考えていました。環境リセット バッチ ファイルは、テスト ケース ファイルを入力フォルダーに戻したり、出力フォルダーをクリーンアップしたりして、テストに適したテスト開始状態にします。フォルダー名にスペースが含まれている場合に備えて、QuotesAround メソッドはコマンド ラインを引用符で囲みます ("Program Files" ですよね?)。含まれているのはこれだけです: private string quotesaround(string input) {return "\"" + input + "\"";}

あなたのシナリオが私のものと似ている場合は、これが便利で数分節約できることを願っています.

于 2019-01-31T19:12:51.753 に答える