2010-08-10 5 views
2

J'essaie d'obtenir les résultats de n'importe quelle application DOS, en laissant effectivement C# fonctionner comme s'il s'agissait d'un utilisateur.Application de style cmd.exe écrite en C#

Je peux l'obtenir pour exécuter une commande, puis afficher la sortie. Le problème est de savoir quand la sortie est terminée! Par exemple, si je vais démarrer/lancer "cmd.exe", tapez "D:", puis "cd D: \", puis "arbre", il affiche ma structure de dossiers sur le lecteur D, puis me permet de tapez ma commande suivante (seulement après il est fini d'imprimer la liste).

Cependant, je n'arrive pas à trouver un moyen de le faire réaliser que c'est fini, et devrait permettre la commande suivante (essentiellement lorsque cmd.exe commence à clignoter votre curseur).

 public Process p = null; 

    private void Form1_Load(object sender, System.EventArgs e) 
    { 
     ProcessStartInfo procStarter = new ProcessStartInfo("cmd.exe"); 
     procStarter.RedirectStandardOutput = true; 
     procStarter.RedirectStandardInput = true; 
     procStarter.UseShellExecute = false; 
     procStarter.CreateNoWindow = true; 
     p = Process.Start(procStarter); 
    } 

    private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e) 
    { 
     p.Close(); 
    } 

    private void btnSend_Click(object sender, System.EventArgs e) 
    { 
     p.StandardInput.WriteLine("D:"); 
     p.StandardInput.WriteLine(@"cd D:\"); 
     txtOutput.Text = SendCommand(txtInput.Text); 
    } 

    private string SendCommand(string cmd) 
    { 
     p.StandardInput.WriteLine(cmd); 
     return p.StandardOutput.ReadToEnd(); 
    } 

Dans sendCommand (string cmd), si je lance p.StandardOutput.ReadToEnd(), selon le code ci-dessus, il se bloque toujours, en attendant sans doute pour l'application de fermer?

Si je boucle à travers p.StandardOutput.ReadLine(), il montre tout le texte (y compris le "D: \>" juste avant où le curseur clignotant serait alors, mais il ne réalise pas que c'est la fin, appelle ReadLine à nouveau, et se bloque à ReadToEnd.Une solution de contournement sale serait de traiter comme la fin de la réponse si la ligne en cours se termine par ">", mais qui se décompose si une ligne se termine comme ça n'importe où dans le réponse.

J'ai essayé une boucle à travers le caractère de flux par caractère, et il n'y a pas de caractère spécial envoyé à la fin.

des idées?

Note: Mon but ultime est de fournir une bibliothèque de lumière que je peux utiliser pour exécuter n'importe quel exécutable DOS (qui peut nécessiter plusieurs commandes tapées, pas seulement les commandes passées en ligne de commande à l'ouverture), analyser les résultats retournés avec un modèle regex et renvoyant le résultat extrait. J'ai pensé que si je peux effectivement ré-implémenter le cmd.exe dans une application Windows, alors une telle bibliothèque sera possible.

Merci, Lee

+0

Ecrire un fichier de commandes, exécutez. –

Répondre

0

Si vous cherchez « comment attendre jusqu'à ce que le processus se termine donné naissance », Process.WaitForExit est ce que devrait faire l'affaire. Vous pouvez générer un nouveau shell pour chaque "commande".

+0

Je cherche à obtenir les résultats avant que l'application ne se termine (ce qui est possible, j'ai juste besoin de savoir quand c'est fini de me donner les résultats). Par exemple (c'est ce qui m'a donné l'idée), vous voulez que la source soit sûre pour exporter chaque version, chaque fichier. Vous pouvez utiliser leurs API non gérées ou le faire via une ligne de commande. Cependant vous auriez besoin d'ouvrir un projet, de boucler tous les fichiers, de boucler toutes les versions, puis d'effectuer un GET sur chacun des fichiers. Cela impliquerait l'exécution de plusieurs commandes dans le même processus, en analysant la sortie après chacune d'elles. – Lee

+0

@Lee - Pourquoi ne pas rediriger la sortie vers un fichier que vous pouvez lire en une fois, une fois la commande terminée. Une autre idée consiste à générer un fichier de commandes contenant toutes les commandes que vous souhaitez appeler, puis générer un fichier cmd.exe avec le nom du fichier de commandes en tant qu'argument. [Comme MSDN le dit, ReadToEnd() bloque indéfiniment les serveurs interactifs, ce que vous voyez. ] – Gishu

+0

Je suppose que je rencontrerais le même problème de ne pas savoir quand la commande est réellement terminée en redirigeant la sortie vers le fichier (je ne l'ai pas encore testé, juste en travaillant sur l'intuition). L'écriture d'un fichier de commandes ne fonctionnerait pas tout à fait, car le contenu du fichier de commandes serait effectivement basé sur les commandes précédentes du même lot. Merci pour vos suggestions. – Lee

2

Je suppose que votre approche ne fonctionne pas. cmd.exe ne va pas vous communiquer via StandardOutput quand ou si la commande que vous avez exécutée est terminée ou non. (Je tiens à préciser que cela ne vous empêche pas d'exécuter plusieurs commandes. Vous pouvez probablement envoyer les lignes de commande et ne pas en fait besoin pour attendre qu'il se termine.)

Peut-être un plus approprié approche pourrait être de ne pas utiliser cmd.exe du tout. Au lieu de cela, utilisez Process.Start() pour exécuter chaque commande individuelle. Ensuite, vous pouvez utiliser StandardOutput.ReadToEnd() et il finira lorsque le processus est terminé, et vous pouvez exécuter le suivant.

+0

Qu'en est-il de faire fonctionner quelque chose comme telnet, ou l'application SQLplus d'Oracle? - Je ne pense pas que tu puisses faire ça en exécutant des commandes indvidiaul séparément? En résumé, lorsque plusieurs étapes sont impliquées, telles que la connexion, la définition de répertoires courants, etc., je pense que l'exécution de commandes séparées ne fonctionnerait pas? Bien que vous puissiez évidemment utiliser les API .net disponibles pour ce faire, il serait bon d'avoir la possibilité de pouvoir utiliser n'importe quelle application DOS. – Lee

1

Je suis d'accord avec Timwi, mais voir si quelque chose comme ci-dessous aide

 ProcessStartInfo procStarter = new ProcessStartInfo("cmd.exe"); 
     procStarter.RedirectStandardOutput = true; 
     procStarter.RedirectStandardInput = true; 
     procStarter.UseShellExecute = false; 
     procStarter.CreateNoWindow = true; 
     procStarter.WorkingDirectory = @"D:\"; 
     procStarter.Arguments = "/C dir"; 
     Process p = Process.Start(procStarter); 
     string output = p.StandardOutput.ReadToEnd(); 
ligne de commande

/C à cmd.exe mettra fin à cmd.exe une fois que le travail est fait. Vous pouvez également utiliser p.Exited (événement exité) pour savoir quand cela se produit.

Cependant, le cmd.exe ne sera pas toujours exécuté. Mais avez-vous vraiment besoin de le garder en marche?

+0

J'ai remarqué votre utilisation de "WorkingDirectory". Je pourrais potentiellement intercepter les commandes de type "C: \" et "cd * dir *", et garder une trace du répertoire de travail moi-même, en exécutant chaque commande comme un processus séparé. Seul le scénario dans lequel cela ne fonctionnerait pas serait pour des applications telles que telnet ou sqlplus, où il y a un processus de connexion d'une certaine description. – Lee

+0

(• Fous rires •) Vous êtes d'accord avec qui? ☺ – Timwi

+0

@Lee oui vous avez raison, pour des choses comme telnet de sqlplus vous devez utiliser StandardInput, et pour prendre une sortie, vous devez utiliser StandardOutput. Mais pour savoir quand l'application a quitté, vous pouvez utiliser l'événement "Exited". @Timwi par erreur J'ai accepté Lee d'abord puis je l'ai édité :) – Vishalgiri

0

Il ya environ un an, j'ai écrit un serveur telnet pour Windows qui permettait à l'utilisateur distant d'émettre des commandes contre cmd.exe. Peut-être que vous pouvez l'utiliser comme point de départ pour votre propre projet.

0

En lisant la sortie asynchrone J'ai eu ce travail (aleast presque) comme vous décrit:

public Process p = null; 

    private void Send_Click(object sender, System.EventArgs e) 
    { 

     p.StandardInput.WriteLine("D:"); 
     p.StandardInput.WriteLine(@"cd D:\"); 
     p.StandardInput.WriteLine(txtInput.Text); 
    } 

    private void Form1_Load_1(object sender, EventArgs e) 
    { 
     ProcessStartInfo procStarter = new ProcessStartInfo("cmd.exe"); 
     procStarter.RedirectStandardOutput = true; 
     procStarter.RedirectStandardInput = true; 
     procStarter.UseShellExecute = false; 
     procStarter.CreateNoWindow = true; 

     p = Process.Start(procStarter); 

     p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived); 
     p.BeginOutputReadLine(); 

    } 

    void p_OutputDataReceived(object sender, DataReceivedEventArgs e) 
    { 
     addTextToOutput(e.Data); 
    } 

    private void addTextToOutput(string text) 
    { 
     if (txtOutput.InvokeRequired) 
     { 
      addTextCallback cb = new addTextCallback(addTextToOutput); 
      this.Invoke(cb, new Object[] { text }); 
     } 
     else 
     { 
      txtOutput.Text += text+ System.Environment.NewLine; 
     } 
    } 

    delegate void addTextCallback(String text); 

    private void Form1_FormClosing(object sender, FormClosingEventArgs e) 
    { 
     p.Close(); 
    } 
+0

Cela ressemble à une technique similaire à celle utilisée par Matthew Whited. Bien qu'il offre la fonctionnalité la plus semblable à cmd.exe, il ne vous permet pas de lier une réponse à une commande, les deux se produisent indépendamment. Mais il semble que ce que je cherche ne peut malheureusement pas être fait. Merci à tous pour votre aide, c'est ma première fois sur stackoverflow et j'ai été très satisfait de la rapidité et de la qualité de la réponse. – Lee

+0

Pourriez-vous expliquer comment vous voulez que les commandes soient liées à la sortie? Je ne suis pas sûr que ce soit exactement ce que vous essayez d'atteindre. – Falle1234

Questions connexes