我在svn.exe周围写了一个快速而又脏的包装来检索一些内容并用它做一些事情,但对于某些输入它偶尔会重复挂起并且无法完成。例如,一个调用是svn list:
svn list "http://myserver:84/svn/Documents/Instruments/" --xml --no-auth-cache --username myuser --password mypassword
当我从命令shell执行此操作时,此命令行运行正常,但它在我的应用程序中挂起。运行它的我的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 + "\"";
我可以在这里搞砸什么,如何调试这个外部流程?
编辑:
我现在正在解决这个问题。 Mucho感谢Jon Skeet的建议,这确实很有效。我有另外一个关于我的处理方法的问题,因为我是一个多线程的新手。我想提出改善任何明显缺陷的建议或其他任何愚蠢的建议。我最终创建了一个包含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;
}
}
这似乎有效,但我怀疑这是最好的方法。这合理吗?我该怎么做才能改善它?
答案 0 :(得分:37)
一个标准问题:进程可能正在等待您读取其输出。在等待它退出时,创建一个单独的线程来读取其标准输出。这有点痛苦,但这可能就是问题所在。
答案 1 :(得分:14)
Jon Skeet是对的钱!
如果您在启动svn命令后不介意轮询,请尝试:
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());
}
答案 2 :(得分:2)
我必须将exe拖放到客户端计算机上,然后使用Process.Start启动它。
正在调用的应用程序将挂起-问题最终是假设该exe危险并阻止其他应用程序启动的他们的机器。
右键单击exe并转到属性。在安全警告旁边的底部点击“取消阻止”。
答案 3 :(得分:1)
我知道这是一个老帖子,但也许这会帮助某人。我使用它来使用.Net TPL任务执行一些AWS (Amazon Web Services) CLI命令。
我在我的命令执行中执行了类似的操作,该命令在.Net TPL任务中执行,该任务是在我的WinForm后台工作程序bgwRun_DoWork
方法中创建的,该方法持有while(!bgwRun.CancellationPending)
的循环。这包含使用.Net ThreadPool类通过新线程读取Process的标准输出。
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
}
答案 4 :(得分:0)
我知道我的SVN回购有时可能会运行缓慢,所以可能5秒还不够长?你是否从断点复制了你传递给过程的字符串,所以你肯定它没有提示你做任何事情?