I wrote a quick and dirty wrapper around svn.exe to retrieve some content and do something with it, but for certain inputs it occasionally and reproducibly hangs and won't finish. For example, one call is to svn list:
svn list "http://myserver:84/svn/Documents/Instruments/" --xml --no-auth-cache --username myuser --password mypassword
This command line runs fine when I just do it from a command shell, but it hangs in my app. My c# code to run this is:
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(); }
This takes the full 5000 ms and never finishes. Extending the time doesn't help. In a separate command prompt, it runs instantly, so I'm pretty sure it's unrelated to an insufficient waiting time. For other inputs, however, this seems to work fine.
I also tried running a separate cmd.exe here (where exe is svn.exe and args is the original arg string), but the hang still occurred:
string cmd = "cmd"; string arguments = "/S /C \"" + exe + " " + args + "\"";
What could I be screwing up here, and how can I debug this external process stuff?
EDIT:
I'm just now getting around to addressing this. Mucho thanks to Jon Skeet for his suggestion, which indeed works great. I have another question about my method of handling this, though, since I'm a multi-threaded novice. I'd like suggestions on improving any glaring deficiencies or anything otherwise dumb. I ended up creating a small class that contains the stdout stream, a StringBuilder to hold the output, and a flag to tell when it's finished. Then I used ThreadPool.QueueUserWorkItem and passed in an instance of my class:
ProcessBufferHandler bufferHandler = new ProcessBufferHandler(proc.StandardOutput.BaseStream, Encoding.UTF8); ThreadPool.QueueUserWorkItem(ProcessStream, bufferHandler); proc.WaitForExit(ms); if (proc.HasExited) { bufferHandler.Stop(); return bufferHandler.ReadToEnd(); }
... and ...
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; } }
This seems to work, but I'm doubtful that this is the best way. Is this reasonable? And what can I do to improve it?
Start(ProcessStartInfo)Starts the process resource that is specified by the parameter containing process start information (for example, the file name of the process to start) and associates the resource with a new Process component.
Start another application using your . NET code As a . NET method, Start has a series of overloads, which are different sets of parameters that determine exactly what the method does. The overloads let you specify just about any set of parameters that you might want to pass to another process when it starts.
One standard issue: the process could be waiting for you to read its output. Create a separate thread to read from its standard output while you're waiting for it to exit. It's a bit of a pain, but that may well be the problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With