web-dev-qa-db-fra.com

Exécuter un fichier batch en C #

J'essaie d'exécuter un fichier batch en C #, mais je n'ai aucune chance de le faire.

J'ai trouvé plusieurs exemples sur Internet, mais cela ne fonctionne pas pour moi.

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

La chaîne de commande contient le nom du fichier de commandes (stocké dans system32) et certains fichiers à manipuler. (Exemple: txtmanipulator file1.txt file2.txt file3.txt). Lorsque j'exécute le fichier de commandes manuellement, cela fonctionne correctement.

Lors de l'exécution du code, cela me donne un **ExitCode: 1** (Catch all for general errors)

Qu'est-ce que je fais mal?

118
Wessel T.

Cela devrait marcher. Vous pouvez essayer d'extraire le contenu des flux de sortie et d'erreur afin de savoir ce qui se passe:

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

* MODIFIER * 

Compte tenu des informations supplémentaires contenues dans votre commentaire ci-dessous, j'ai pu recréer le problème. Il semble exister un paramètre de sécurité à l'origine de ce problème (cela n'a pas été étudié en détail). 

Cette fonctionne si le fichier de commandes ne se trouve pas dans C:\Windows\System32. Essayez de le déplacer vers un autre emplacement, par exemple. l'emplacement de votre exécutable. Notez que conserver les fichiers de commandes personnalisés ou les exécutables dans le répertoire Windows est de toute façon une mauvaise pratique.

* EDIT 2 * Il s'avère si les flux sont lus de manière synchrone, un blocage peut se produire, soit en lisant de manière synchrone avant WaitForExit, soit en lisant à la fois stderr et stdout de manière synchrone une autre. 

Cela ne devrait pas se produire si vous utilisiez plutôt les méthodes de lecture asynchrone, comme dans l'exemple suivant:

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

cette simple ligne exécutera le fichier de commandes.

117
T.S.Sathish

Après une aide précieuse de steinar, voici ce qui a fonctionné pour moi:

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();
}
15
Wessel T.

Ça fonctionne bien. Je l'ai testé comme ça:

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

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

J'ai commenté en éteignant la fenêtre pour pouvoir la voir fonctionner.

13
Steve Wellens

Le code ci-dessous a bien fonctionné pour moi

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

Avez-vous essayé de le démarrer en tant qu'administrateur? Démarrez Visual Studio en tant qu'administrateur si vous l'utilisez, car l'utilisation de fichiers .bat requiert ces privilèges.

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

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

Je sais que cela fonctionnera pour les fichiers de commandes et les paramètres, mais je ne vois pas comment obtenir les résultats en C #. Généralement, les sorties sont définies dans le fichier de commandes.

0
macunix

Je voulais quelque chose qui soit plus directement utilisable sans valeurs de chaîne codées en dur spécifiques à l'organisation. Je propose ce qui suit sous forme de bloc de code directement réutilisable. L'inconvénient mineur est de déterminer et de transmettre le dossier de travail lors de l'appel.

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

Appelé comme ça:

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

Dans cet exemple, à partir de Visual Studio 2017, dans le cadre d'une exécution de test, je souhaite exécuter un fichier de traitement par lots de réinitialisation d'environnement avant d'exécuter certains tests. (SpecFlow + xUnit). J'étais fatigué des étapes supplémentaires pour exécuter manuellement le fichier bat séparément, et je voulais simplement exécuter le fichier bat dans le cadre du code d'installation du test C #. Le fichier de commandes de réinitialisation de l'environnement déplace les fichiers de cas de test dans le dossier d'entrée, nettoie les dossiers de sortie, etc. pour obtenir l'état de démarrage de test approprié. La méthode QuotesAround met simplement des guillemets autour de la ligne de commande au cas où il y aurait des espaces dans les noms de dossiers ("Program Files", quelqu'un?). Voici tout ce qui y figure: chaîne privée QuotesAround (chaîne d'entrée) {return "\" "+ input +"\"";}

J'espère que certains trouveront cela utile et gagneront quelques minutes si votre scénario est similaire au mien.

0
Developer63