2010-05-04 19 views
4

Je démarre un processus dans mon application C# qui exécute une application console. J'ai redirigé l'entrée et la sortie standard, et suis capable de lire quelques lignes via StandardOutput.ReadLine(). Je suis convaincu que j'ai le ProcessStartInfo configuré correctement.StandardOutput.EndOfStream se bloque

L'application de la console, au démarrage, les sorties quelques lignes (se terminant par une ligne « marqueur ») et attend l'entrée. Après réception de l'entrée, il sort à nouveau quelques lignes (se terminant à nouveau par une ligne "marker"), et ainsi de suite. Mon intention est d'en lire les lignes jusqu'à ce que je reçoive la ligne "marker", à quel point je sais envoyer la chaîne d'entrée appropriée.

Mon problème est que, après plusieurs itérations, le programme se bloque. La suspension du débogueur a tendance à placer le blocage dans un appel à StandardOutput.EndOfStream. Tel est le cas dans le code de test suivant:

while (!mProcess.StandardOutput.EndOfStream) // Program hangs here. 
{ 
    Console.WriteLine(mProcess.StandardOutput.ReadLine()); 
} 

Quand je teste pour la ligne « marqueur », je reçois le même genre de se bloquer si je tente d'accéder StandardOutput.EndOfStream après avoir lu la ligne:

Qu'est-ce que je pourrais faire pour que cette propriété fonctionne si horriblement?
string line = ""; 
while (!isMarker(line)) 
{ 
    line = mProcess.StandardOutput.ReadLine(); 
} 
bool eos = mProcess.StandardOutput.EndOfStream; // Program hangs here. 

Répondre

9

Vous ne pouvez pas utiliser EndOfStream fiable ici. La propriété StreamReader.EndOfStream appellera StandardOutput.Read() si aucun caractère n'est tamponné. Cet appel Read() bloquera si le processus n'envoie rien à son tube de sortie et ne le ferme pas. Ce qui est pratiquement garanti, car il faudra attendre l'entrée. EndOfStream ne retournera pas vrai tant que le processus n'aura pas fermé la fin du canal de sortie et que StreamReader aura consommé tous ses caractères mis en mémoire tampon. À la fin du programme.

En utilisant BeginOutputReadLine() est peut-être une meilleure façon de détecter la ligne « marqueur ». Attention, le rappel se passe sur un autre thread. Notez également qu'il ne devrait pas être nécessaire d'attendre que le processus envoie le marqueur, tout ce que vous écrivez sera tamponné jusqu'à ce que le processus soit prêt à le lire. Attention les tampons sont trop petits, l'impasse est possible.

0

Avez-vous d'attendre le processus se termine avant la lecture de sa sortie standard:

mProcess.WaitForExit(); 
+1

Non; l'intention est d'interagir avec l'application console. – TreDubZedd

1

Il existe de nombreux chemins pour lesquels vous pouvez créer une impasse lors de l'interaction avec une classe de processus. Microsoft les décrit sur le MSDN site here. Voici comment je l'appelle. Notez la gestion de ErrorDataReceived et OutputDataReceived et les appels à BeginErrorReadLine et BeginOutputReadLine. Cela élimine les scénarios de blocage en demandant au processus parent de lire les flux de manière asynchrone. Remarque: RunProcessResponse est mon propre petit objet de transfert de données wrapper.

Public Function RunProcess(ByVal executableFileName As String, ByVal arguments As String, ByVal workingDirectory As System.String) As RunProcessResponse 
    Dim process As System.Diagnostics.Process = Nothing 
    Dim response As RunProcessResponse 

    Try 
     process = New System.Diagnostics.Process() 
     Dim psInfo As New System.Diagnostics.ProcessStartInfo() 
     Dim errorString As System.String = String.Empty 
     Dim outputString As System.String = String.Empty 


     If Not System.String.IsNullOrEmpty(workingDirectory) Then 
      psInfo.WorkingDirectory = workingDirectory 
     End If 

     psInfo.FileName = executableFileName 
     psInfo.Arguments = arguments 
     psInfo.WindowStyle = ProcessWindowStyle.Hidden 
     psInfo.CreateNoWindow = True 
     psInfo.RedirectStandardError = True 
     psInfo.RedirectStandardOutput = True 
     psInfo.UseShellExecute = False 

     AddHandler process.ErrorDataReceived, Sub(sender As Object, args As DataReceivedEventArgs) 
                If args.Data IsNot Nothing Then 
                 errorString &= args.Data & vbCrLf 
                End If 
               End Sub 
     AddHandler process.OutputDataReceived, Sub(sender As Object, args As DataReceivedEventArgs) 
                If args.Data IsNot Nothing Then 
                 outputString &= args.Data & vbCrLf 
                End If 
               End Sub 

     process.StartInfo = psInfo 
     process.Start() 

     process.BeginErrorReadLine() 
     process.BeginOutputReadLine() 
     process.WaitForExit() 

     response = New RunProcessResponse(errorString, outputString, process.ExitCode) 

     Return response 
    Finally 
     If process IsNot Nothing Then 
      process.Dispose() 
     End If 
    End Try 
End Function 
0

Si vous savez qu'il n'y a pas plus d'entrée standard après votre:

while (!mProcess.StandardOutput.EndOfStream) 
boucle

, vous pouvez fermer l'entrée standard avec:

mProcess.StandardInput.Close(); 

signaler qu'il n'y est plus contribution. Tant que l'entrée standard est ouverte, il y a le potentiel pour plus d'entrée, donc plus de sortie; par conséquent, la sortie standard n'atteindra jamais EndOfStream.