StandardOutput.ReadToEnd() 挂起
我有一个经常使用外部程序并读取其输出的程序。 使用通常的进程重定向输出它工作得很好,但是当我尝试读取它时,一个特定的参数由于某种原因挂起,没有错误消息 - 没有例外,它只是在到达该行时“停止”。 我当然使用集中函数来调用和读取程序的输出,即:
public string ADBShell(string adbInput)
{
try
{
//Create Empty values
string result = string.Empty;
string error = string.Empty;
string output = string.Empty;
System.Diagnostics.ProcessStartInfo procStartInfo
= new System.Diagnostics.ProcessStartInfo(toolPath + "adb.exe");
procStartInfo.Arguments = adbInput;
procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardError = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
procStartInfo.WorkingDirectory = toolPath;
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
// Get the output into a string
proc.WaitForExit();
result = proc.StandardOutput.ReadToEnd();
error = proc.StandardError.ReadToEnd(); //Some ADB outputs use this
if (result.Length > 1)
{
output += result;
}
if (error.Length > 1)
{
output += error;
}
Return output;
}
catch (Exception objException)
{
throw objException;
}
}
挂起的行是 result = proc.StandardOutput.ReadToEnd();
,但同样,并非每次都是,仅当发送特定参数(“启动服务器”)时。所有其他参数都工作得很好 - 它读取值并返回它。 它悬挂的方式也很奇怪。它不会冻结或给出错误或任何其他内容,它只是停止处理。就像它是一个“返回”命令一样,除了它甚至不返回到调用函数之外,它只是停止一切,而界面仍然启动并运行。 以前有人经历过这个吗?有人知道我应该尝试什么吗?我假设这是流本身中意外的事情,但是有没有办法可以处理/忽略它,以便它无论如何都可以读取它?
I have a program that frequently uses an external program and reads its outputs.
It works pretty well using your usual process redirect output, but one specific argument for some reason hangs when I try to read it, no error message - no exception, it just 'stops' when it reaches that line.
I of course use a centralized function to call and read output from the program, which is this:
public string ADBShell(string adbInput)
{
try
{
//Create Empty values
string result = string.Empty;
string error = string.Empty;
string output = string.Empty;
System.Diagnostics.ProcessStartInfo procStartInfo
= new System.Diagnostics.ProcessStartInfo(toolPath + "adb.exe");
procStartInfo.Arguments = adbInput;
procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardError = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
procStartInfo.WorkingDirectory = toolPath;
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
// Get the output into a string
proc.WaitForExit();
result = proc.StandardOutput.ReadToEnd();
error = proc.StandardError.ReadToEnd(); //Some ADB outputs use this
if (result.Length > 1)
{
output += result;
}
if (error.Length > 1)
{
output += error;
}
Return output;
}
catch (Exception objException)
{
throw objException;
}
}
The line that hangs is result = proc.StandardOutput.ReadToEnd();
, but again, not every time, only when sent a specific argument ("start-server"). All other arguments work just fine - it reads the value and returns it.
It's also strange the way it hangs. It doesn't freeze or give an error or anything, it just stops processing. As if it was a 'return' command, except it doesn't even return to the calling function, it just stops everything with the interface still up and running.
Anyone experienced this before? Anyone have any idea what I should try? I'm assuming it's something unexpected within the stream itself, but is there a way I can handle/ignore this so that it reads it anyway?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
建议的使用
BeginOutputReadLine()
的解决方案是一个好方法,但在这种情况下,它不适用,因为进程(当然使用WaitForExit()
)比异步退出更早输出完全完成。因此,我尝试同步实现它,发现解决方案是使用
StreamReader
类中的Peek()
方法。我添加了对Peek() > 的检查-1
以确保它不是流的结尾,如 MSDN 文章 进行了描述,终于可以工作了,不再挂起!这是代码:
Proposed solutions with
BeginOutputReadLine()
are a good way but in situations such as that, it is not applicable, because process (certainly with usingWaitForExit()
) exits earlier than async output finished completely.So, I tried to implement it synchronously and found that the solution is in using
Peek()
method fromStreamReader
class. I added check forPeek() > -1
to sure that it is not the end of the stream as in MSDN article described and finally it works and stop hanging!Here is the code:
问题是您在
StandardOutput
和StandardError
流上使用同步ReadToEnd
方法。这可能会导致您遇到潜在的僵局。 MSDN 中甚至对此进行了描述。那里描述了解决方案。基本上,它是:使用异步版本BeginOutputReadLine
读取StandardOutput
流的数据:使用BeginOutputReadLine实现异步读取见ProcessStartInfo 挂在“WaitForExit”上?为什么?
The problem is that you are using the synchronous
ReadToEnd
methods on both theStandardOutput
and theStandardError
streams. This can lead to a potential deadlock you are experiencing. This is even described in the MSDN. The solution is described there. Basically, it is: Use the asynchronous versionBeginOutputReadLine
to read the data of theStandardOutput
stream:Implementation of Async reading using BeginOutputReadLine see in ProcessStartInfo hanging on "WaitForExit"? Why?
怎么样:
What about something like:
我有同样的死锁问题。这个代码片段对我有用。
I had the same deadlock problem. This code snippet worked for me.
我遇到了同样的问题,错误只是挂起。
根据您对 Daniel Hilgarth 的回复,我什至没有尝试使用这些代码,尽管我认为它们对我有用。
由于我希望能够做出一些更精美的输出,最终我决定在后台线程中完成两个输出。
这对我有用,让我不必在读取时使用超时。
I had the same kind of problem that error was just hanging.
Based on your response to Daniel Hilgarth I didn't even try using those codes though i think they would have worked for me.
Since I want to be able do some fancier output still eventually i decided that I would do it with both of the outputs being done in a background thread.
This worked for me and allowed me to not have to use a timeout for the read.
对我来说优雅且有用的东西是:
我在此处找到了这个答案诀窍是在标准输入上使用
Flush()
和Close()
。Something that is elegant and worked for me is:
This answer I found here and the trick is using
Flush()
andClose()
on standard input.接受的答案的解决方案对我不起作用。我必须使用任务来避免死锁:
使用
GetStreamOutput
函数,如下所示:The accepted answer's solution didn't work for me. I had to use tasks in order to avoid the deadlock:
With a
GetStreamOutput
function as follows:以防万一有人在想要使用 Windows 窗体和
TextBox
(或RichTextBox
)来显示错误并输出过程实时返回的输出时偶然发现这个问题(因为它们是写入process.StandardOutput
/process.StandardError
)。您需要使用
OutputDataReceived()
/ErrorDataReceived()
才能在没有死锁的情况下读取两个流,否则无法(据我所知)避免死锁,即使是费多尔的答案,现在拥有“答案”标签并且是最新的最受欢迎的答案,对我来说也不起作用。然而,当您使用RichTextBox(或TextBox)输出数据时,您遇到的另一个问题是如何将数据实时(一旦到达)实际写入文本框。您可以在后台线程
OutputDataReceived()
/ErrorDataReceived()
之一内接收对数据的访问权限,并且您只能从主线程。我首先尝试做的是从后台线程调用 process.Start() ,然后调用 BeginInvoke() =>
OutputDataReceived()
/ErrorDataReceived()
线程中的 AppendText(),而主线程是process.WaitForExit()
。然而,这导致我的身体冻结并最终永远悬吊着。经过几天的尝试,我最终得到了下面的解决方案,这似乎工作得很好。
简而言之,您需要将消息添加到
OutputDataReceived()
/ErrorDataReceived()
线程内的并发集合中,而主线程应不断尝试从该集合中提取消息,并且将它们附加到文本框中:这种方法的唯一缺点是,当进程开始在
process.Start()
和process 之间写入消息时,您可能会在极少数情况下丢失消息。 BeginErrorReadLine()
/process.BeginOutputReadLine()
,请记住这一点。避免这种情况的唯一方法是读取完整的流和(或)仅在进程完成时才能访问它们。Just in case someone stumbles upon this question while wiling to use Windows Forms and
TextBox
(orRichTextBox
) to show the errors and outputs the process returns in real time (as they are written toprocess.StandardOutput
/process.StandardError
).You need to use
OutputDataReceived()
/ErrorDataReceived()
in order to read both streams without deadlocks, there is no way (as far as I know) to avoid deadlocks otherwise, even Fedor's answer, which now holds the "Answer" tag and the most likes up to date, does not do the trick for me.However, when you use the RichTextBox (or TextBox) to output the data, another problem you encounter is how to actually write the data into the textbox in real time (once it arrives). You receive the access to the data inside one of the background threads
OutputDataReceived()
/ErrorDataReceived()
and you can onlyAppendText()
from the main thread.What I first tried doing was calling
process.Start()
from a background thread and then callingBeginInvoke() => AppendText()
inOutputDataReceived()
/ErrorDataReceived()
threads while the main thread wasprocess.WaitForExit()
.However, this led to my form freezing and ultimately hanging for eternity. After a few days of trying I ended up with the solution below, that seems to work pretty well.
Shortly speaking, you need to add the messages into a concurrent collection inside
OutputDataReceived()
/ErrorDataReceived()
threads while the main thread should constantly try to extract messages from that collection and append them into the textbox:The only downside of this approach is the fact that you can loose messages in a quite rare case, when process starts writing them between
process.Start()
andprocess.BeginErrorReadLine()
/process.BeginOutputReadLine()
, just keep that in mind. The only way to avoid that is to read the full streams and (or) gain access to them only when the process finishes.第一
秒
这是来自 MSDN
first
second
This is from MSDN