Processus suspendu lorsqu'il est exécuté avec .NET Process.Start - qu'est-ce qui ne va pas?

StackOverflow https://stackoverflow.com/questions/439617

  •  22-07-2019
  •  | 
  •  

Question

J'ai écrit un wrapper rapide et sale autour de svn.exe pour récupérer du contenu et en faire quelque chose, mais pour certaines entrées, il se bloque parfois de manière reproductible et ne se termine pas. Par exemple, un appel consiste à svn list:

svn list "http://myserver:84/svn/Documents/Instruments/" --xml  --no-auth-cache --username myuser --password mypassword

Cette ligne de commande fonctionne correctement lorsque je le fais simplement à partir d'un interpréteur de commandes, mais elle se bloque dans mon application. Mon code c # à exécuter est le suivant:

string cmd = "svn.exe";
string arguments = "list \"http://myserver:84/svn/Documents/Instruments/\" --xml  --no-auth-cache --username myuser --password mypassword";
int ms = 5000;
ProcessStartInfo psi = new ProcessStartInfo(cmd);
psi.Arguments = arguments;
psi.RedirectStandardOutput = true;
psi.WindowStyle = ProcessWindowStyle.Normal;
psi.UseShellExecute = false;
Process proc = Process.Start(psi);
StreamReader output = new StreamReader(proc.StandardOutput.BaseStream, Encoding.UTF8);

proc.WaitForExit(ms);
if (proc.HasExited)
{
    return output.ReadToEnd();
}

Cela prend 5000 ms et ne se termine jamais. Prolonger le temps ne aide pas. Dans une invite de commande séparée, il s'exécute instantanément. Je suis donc presque sûr que cela n'a rien à voir avec un temps d'attente insuffisant. Cependant, cela semble bien fonctionner pour les autres intrants.

J'ai aussi essayé d'exécuter un cmd.exe séparé ici (où exe est svn.exe et args est la chaîne arg d'origine), mais le blocage a quand même eu lieu:

string cmd = "cmd";
string arguments = "/S /C \"" + exe + " " + args + "\"";

Qu'est-ce que je peux faire foirer ici et comment puis-je déboguer ce processus externe?

EDIT:

Je commence à peine à régler ce problème. Merci beaucoup à Jon Skeet pour sa suggestion, qui fonctionne vraiment bien. J'ai une autre question à propos de ma méthode de traitement, cependant, puisque je suis un novice multi-thread. Je voudrais des suggestions sur l’amélioration des carences criantes ou de toute autre bêtise. J'ai fini par créer une petite classe contenant le flux stdout, un StringBuilder pour contenir la sortie et un indicateur pour indiquer son achèvement. Ensuite, j'ai utilisé ThreadPool.QueueUserWorkItem et passé dans une instance de ma classe:

ProcessBufferHandler bufferHandler = new ProcessBufferHandler(proc.StandardOutput.BaseStream,
                                                                          Encoding.UTF8);
ThreadPool.QueueUserWorkItem(ProcessStream, bufferHandler);

proc.WaitForExit(ms);
if (proc.HasExited)
{
    bufferHandler.Stop();
    return bufferHandler.ReadToEnd();
}

... et ...

private class ProcessBufferHandler
{
    public Stream stream;
    public StringBuilder sb;
    public Encoding encoding;
    public State state;

    public enum State
    {
        Running,
        Stopped
    }

    public ProcessBufferHandler(Stream stream, Encoding encoding)
    {
        this.stream = stream;
        this.sb = new StringBuilder();
        this.encoding = encoding;
        state = State.Running;
    }
    public void ProcessBuffer()
    {
        sb.Append(new StreamReader(stream, encoding).ReadToEnd());
    }

    public string ReadToEnd()
    {
        return sb.ToString();
    }

    public void Stop()
    {
        state = State.Stopped;
    }
}

Cela semble fonctionner, mais je doute que ce soit le meilleur moyen. Est-ce raisonnable? Et que puis-je faire pour l'améliorer?

Était-ce utile?

La solution

Un problème standard: le processus peut attendre que vous lisiez sa sortie. Créez un thread distinct à lire à partir de sa sortie standard en attendant sa sortie. C'est un peu pénible, mais c'est peut-être le problème.

Autres conseils

Jon Skeet a raison sur l’argent!

Si cela ne vous dérange pas d’interroger après avoir lancé votre commande svn, essayez ceci:

Process command = new Process();
command.EnableRaisingEvents = false;
command.StartInfo.FileName = "svn.exe";
command.StartInfo.Arguments = "your svn arguments here";
command.StartInfo.UseShellExecute = false;
command.StartInfo.RedirectStandardOutput = true;
command.Start();

while (!command.StandardOutput.EndOfStream)
{
    Console.WriteLine(command.StandardOutput.ReadLine());
}

J'ai dû déposer un fichier exe sur la machine d'un client et utiliser Process.Start pour le lancer.

L’appel appelant se bloquait - le problème finissait par être leur machine en supposant que le fichier exe était dangereux et en empêchant les autres applications de le démarrer.

Faites un clic droit sur le fichier exe et allez dans les propriétés. Hit " Débloquer " vers le bas, à côté de l'avertissement de sécurité.

 entrer la description de l'image ici

Je sais que c'est un ancien post, mais peut-être que cela aidera quelqu'un. Je l'ai utilisé pour exécuter certaines commandes CLI AWS (Amazon Web Services) à l'aide de tâches .Net TPL.

J'ai fait quelque chose comme ceci dans l'exécution de ma commande, qui est exécutée dans une tâche .Net TPL créée dans ma méthode de travail d'arrière-plan WinForm bgwRun_DoWork qui tient une boucle avec tant que (! bgwRun .AnnulationPending) . Ceci contient la lecture de la sortie standard du processus via un nouveau thread utilisant la classe .Net ThreadPool.

private void bgwRun_DoWork(object sender, DoWorkEventArgs e)
{
  while (!bgwRun.CancellationPending)
  {
   //build TPL Tasks
   var tasks = new List<Task>();

   //work to add tasks here

   tasks.Add(new Task(()=>{

     //build .Net ProcessInfo, Process and start Process here

     ThreadPool.QueueUserWorkItem(state =>
       {
           while (!process.StandardOutput.EndOfStream)
           {
               var output = process.StandardOutput.ReadLine();
               if (!string.IsNullOrEmpty(output))
               {
                   bgwRun_ProgressChanged(this, new ProgressChangedEventArgs(0, new ExecutionInfo
                   {
                       Type = "ExecutionInfo",
                       Text = output,
                       Configuration = s3SyncConfiguration
                   }));
               }

               if (cancellationToken.GetValueOrDefault().IsCancellationRequested)
               {
                     break;
               }
           }
       });
   });//work Task

   //loop through and start tasks here and handle completed tasks

  } //end while
}

Je sais que mon référentiel SVN peut être lent parfois, alors peut-être que 5 secondes ne sont pas assez longues? Avez-vous copié la chaîne que vous transmettez au processus à partir d’un point de rupture afin d’être certain que cela ne vous demandera rien?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top