Я написал быструю и грязную оболочку вокруг svn.exe, чтобы извлечь некоторый контент и что-то с ним сделать, но для определенных входов он иногда и воспроизводимо зависает и не завершится. Например, один вызов в список svn:
svn list "http://myserver:84/svn/Documents/Instruments/" --xml --no-auth-cache --username myuser --password mypassword
Эта командная строка работает нормально, когда я просто делаю это из командной оболочки, но она зависает в моем приложении. Мой код C # для запуска это:
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();
}
Это занимает все 5000 мс и никогда не заканчивается. Продление времени не помогает. В отдельной командной строке он запускается мгновенно, поэтому я уверен, что он не связан с недостаточным временем ожидания. Однако для других входов это работает нормально.
Я также попытался запустить отдельный cmd.exe здесь (где exe - svn.exe, а args - исходная строка arg), но зависание по-прежнему происходило:
string cmd = "cmd";
string arguments = "/S /C \"" + exe + " " + args + "\"";
Что я мог напортачить здесь, и как я могу отлаживать этот внешний процесс?
EDIT:
Я только сейчас приступаю к решению этой проблемы. Большое спасибо Джону Скиту за его предложение, которое действительно прекрасно работает. У меня есть еще один вопрос о моем методе обработки этого, так как я многопоточный новичок. Я хотел бы получить предложения по улучшению любых явных недостатков или чего-то еще глупого. Я закончил тем, что создал небольшой класс, который содержит поток stdout, StringBuilder для хранения вывода и флаг, чтобы сообщить, когда он закончится. Затем я использовал ThreadPool.QueueUserWorkItem и передал экземпляр моего класса:
ProcessBufferHandler bufferHandler = new ProcessBufferHandler(proc.StandardOutput.BaseStream,
Encoding.UTF8);
ThreadPool.QueueUserWorkItem(ProcessStream, bufferHandler);
proc.WaitForExit(ms);
if (proc.HasExited)
{
bufferHandler.Stop();
return bufferHandler.ReadToEnd();
}
... и ...
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;
}
}
Кажется, это работает, но я сомневаюсь, что это лучший способ. Это разумно? И что я могу сделать, чтобы улучшить это?