2017-08-03 3 views
4

Je dois écrire un wrapper pour un programme de ligne de commande interactif. Cela signifie que je dois pouvoir envoyer des commandes à l'autre programme via son entrée standard et recevoir la réponse via sa sortie standard.Comment utiliser un programme de ligne de commande interactif à partir d'un autre programme .NET

Le problème est que le flux de sortie standard semble être bloqué alors que le flux d'entrée est toujours ouvert. Dès que je ferme le flux d'entrée, j'obtiens la réponse. Mais alors je ne peux pas envoyer plus de commandes.

C'est ce que je me sers au moment (la plupart du temps de here):

void Main() { 
    Process process; 
    process = new Process(); 
    process.StartInfo.FileName = "atprogram.exe"; 
    process.StartInfo.Arguments = "interactive"; 

    // Set UseShellExecute to false for redirection. 
    process.StartInfo.UseShellExecute = false; 
    process.StartInfo.CreateNoWindow = true; 

    // Redirect the standard output of the command. 
    // This stream is read asynchronously using an event handler. 
    process.StartInfo.RedirectStandardOutput = true; 
    // Set our event handler to asynchronously read the output. 
    process.OutputDataReceived += (s, e) => Console.WriteLine(e.Data); 

    // Redirect standard input as well. This stream is used synchronously. 
    process.StartInfo.RedirectStandardInput = true; 
    process.Start(); 

    // Start the asynchronous read of the output stream. 
    process.BeginOutputReadLine(); 

    String inputText; 
    do 
    { 
     inputText = Console.ReadLine(); 
     if (inputText == "q") 
     { 
      process.StandardInput.Close(); // After this line the output stream unblocks 
      Console.ReadLine(); 
      return; 
     } 
     else if (!String.IsNullOrEmpty(inputText)) 
     { 
      process.StandardInput.WriteLine(inputText); 
     } 
    } 
} 

J'ai aussi essayé de lire le flux de sortie standard synchrone, mais avec le même résultat. Toute méthode appelle indéfiniment le bloc de flux de sortie jusqu'à ce que le flux d'entrée soit fermé - même Peek() et EndOfStream.

Existe-t-il un moyen de communiquer avec l'autre processus en mode duplex intégral?

+0

Pouvez-vous rendre asynchrone –

+0

@BenderBending Oui je peux, mais est-ce que cela aidera? – Karsten

+0

Je crois que ce serait. t le flux d'entrée sur un thread et le flux de sortie sur l'autre thread. –

Répondre

1

J'ai essayé de reproduire votre problème avec une petite suite de tests. Au lieu d'utiliser des gestionnaires d'événements, je le fais de la manière la plus triviale que je puisse concevoir: Synchrone. De cette façon, aucune complexité supplémentaire n'est ajoutée au problème.

Voici mon petit « echoApp » J'ai écrit dans la rouille, juste pour le fou rire et aussi d'avoir une chance de courir dans l'éternel problème des guerres de terminaison de ligne (\n vs vs \r\r\n). Selon la façon dont votre application de ligne de commande est écrite, cela pourrait en effet être l'un de vos problèmes.

use std::io; 

fn main() { 
    let mut counter = 0; 
    loop { 
     let mut input = String::new(); 
     let _ = io::stdin().read_line(&mut input); 
     match &input.trim() as &str { 
      "quit" => break, 
      _ => { 
       println!("{}: {}", counter, input); 
       counter += 1; 
      } 
     } 
    } 
} 

Et - être un os paresseux qui n'aime pas la création d'une solution pour un petit test, je F # au lieu de C# pour le côté contrôle - il est assez facile à lire, je pense:

open System.Diagnostics; 

let echoPath = @"E:\R\rustic\echo\echoApp\target\debug\echoApp.exe" 

let createControlledProcess path = 
    let p = new Process() 
    p.StartInfo.UseShellExecute <- false 
    p.StartInfo.RedirectStandardInput <- true 
    p.StartInfo.RedirectStandardOutput <- true 
    p.StartInfo.Arguments <- "" 
    p.StartInfo.FileName <- path 
    p.StartInfo.CreateNoWindow <- true 
    p 

let startupControlledProcess (p : Process) = 
    if p.Start() 
    then 
     p.StandardInput.NewLine <- "\r\n" 
    else() 

let shutdownControlledProcess (p : Process) = 
    p.StandardInput.WriteLine("quit"); 
    p.WaitForExit() 
    p.Close() 

let interact (p : Process) (arg : string) : string = 
    p.StandardInput.WriteLine(arg); 
    let o = p.StandardOutput.ReadLine() 
    // we get funny empty lines every other time... 
    // probably some line termination problem (unix \n vs \r\n etc - 
    // who can tell what rust std::io does...?) 
    if o = "" then p.StandardOutput.ReadLine() 
    else o 

let p = createControlledProcess echoPath 
startupControlledProcess p 
let results = 
    [ 
     interact p "Hello" 
     interact p "World" 
     interact p "Whatever" 
     interact p "floats" 
     interact p "your" 
     interact p "boat" 
    ] 
shutdownControlledProcess p 

L'exécution de ce à f # interactive des rendements (CTRL-A Alt-Entrée dans Visual studio):

val echoPath: string = « E: \ R \ rustique \ echo \ echoApp \ target \ debug \ echoApp. exe "

val createControlledProcess: Piste: string -> Process

val startupControlledProcess: p: Process -> unit

val shutdownControlledProcess: p: Process -> unit

interagissent val: p: Process -> arg : chaîne -> chaîne

val p: Process = System.Diagnostics.Processus

résultats val: liste de chaînes =

[ "0: Bonjour"; "1: Monde"; "2: Peu importe"; "3: flotteurs"; "4: votre"; « 5: bateau »]

val il: unit =()

Je ne pouvais pas reproduire tout blocage ou de blocage, etc. Ainsi, dans votre cas, je tenterait d'enquêter si peut-être vos NewLine besoins immobiliers quelques ajustements (voir la fonction startupControlledProcess) Si l'application contrôlée ne reconnaît pas une entrée comme une ligne, elle peut ne pas répondre, attendant toujours le reste de la ligne d'entrée et vous pourriez obtenir l'effet que vous avez

+0

Merci pour votre réponse complète! J'ai essayé différentes valeurs pour'NewLine 'sans succès. J'ai également essayé de lancer le processus à partir de python avec des résultats similaires. Je pense que cela a à voir avec la façon dont l'autre application est écrite. J'ai eu un problème similaire avec WinSCP jusqu'à ce que je découvre qu'il a un fichier WinSCP.com séparé qui fonctionne beaucoup mieux depuis la ligne de commande. Je ne sais toujours pas quelle est la différence entre les deux et il n'y a pas d'alternative à l'atprogram pour autant que je sache ... – Karsten

+0

Peut-être que vous devez essayer si le programme cible repose sur un répertoire de travail spécifique. Certains programmes (mal écrits) le font. Cela pourrait empêcher le programme de fonctionner. Ou peut-être que cela dépend de certains paramètres d'environnement comme tant de programmes venant du monde unix. Vous pouvez spécifier le répertoire de travail et les paramètres d'environnement (peut-être PATH?) Lorsque vous démarrez l'application. – BitTickler