获取 .NET Process 对象以连续刷新输入流?
我正在尝试更改与 Mercurial 命令行客户端对话的类库,1.9 客户端中的新功能是能够启动服务器并通过标准输入/输出管道与其对话。这确实很有希望,因为使用 Mercurial 命令行客户端的主要难题之一是,由于它是用 Python 编写的,因此即使只是询问其版本,也会有一些启动客户端的开销。
然而,有一个问题。到目前为止,我已经通过读取标准输出/错误从命令行客户端检索输出,并且当进程退出时,流刷新,我可以读取流的末尾。
然而,在这种新模式下,进程会将大量文本转储到标准错误/输出,然后等待下一个命令。非常适合减少开销,但很糟糕,因为 .NET 会缓冲这些流。
换句话说,由于进程没有退出,所以我不会得到该输出的最后部分,直到:
- 我要求进程退出
- 我发出另一个命令
有没有办法让我
- 要求缓冲区刷新,这意味着我得到缓冲区中的内容是什么,即使它不足以使缓冲区自行刷新?
- 如果不是,我可以将缓冲区大小设置为 1 个字符吗?
- 我还能做些什么吗?
如果需要的话,我可以处理 P/Invoke。
这是一个 LINQPad 概念验证程序,它可以说明:
它的作用是启动命令提示符并向其提供 DIR命令,但请注意,直到循环内经过 10 秒后,程序才会输出“C:>”生成目录列表后立即输出命令提示符的提示。
换句话说,这就是命令提示符的作用:
- 生成目录列表
- 通过提示“C:>\”请求另一个命令
但是,这是下面的程序所看到的:
- 生成目录列表
- (等待 10 秒)
- 关闭输入流
查看提示
无效主函数() { 字符串 clientExecutablePath = "cmd.exe";
var psi = new ProcessStartInfo(); psi.FileName = clientExecutablePath; psi.RedirectStandardError = true; psi.RedirectStandardInput = true; psi.RedirectStandardOutput = true; psi.CreateNoWindow = true; psi.WorkingDirectory = @"C:\"; psi.WindowStyle = ProcessWindowStyle.Hidden; psi.UseShellExecute = false; psi.ErrorDialog = false; psi.StandardErrorEncoding = Encoding.GetEncoding("Windows-1252"); psi.StandardOutputEncoding = Encoding.GetEncoding("Windows-1252"); var p = Process.Start(psi); var 输入 = p.StandardInput; var 输出 = p.StandardOutput; var 错误 = p.StandardError; 动作
reader = delegate(StreamReader StreamReader, 字符串前缀) { 串线; while ((line = StreamReader.ReadLine()) != null) { Debug.WriteLine(前缀+行); } }; IAsyncResult outputReader = reader.BeginInvoke(output, "o: ", null, null); IAsyncResult errorReader = reader.BeginInvoke(error, "e: ", null, null); 输入.Write("目录\n"); 输入.Flush(); while (!p.HasExited) { 线程.睡眠(10000); 输入.关闭(); } reader.EndInvoke(outputReader); reader.EndInvoke(errorReader); }
I am trying to change my class library that talks to the Mercurial command line client, and new in the 1.9 client is the ability to spin up a server and talk to it over the standard input/output pipes. This is really promising since one of the major headaches of using the Mercurial command line client is that since it is written in Python, there's a bit of overhead spinning up the client, even for just asking it for its version.
However, there is one problem. Up until now I have retrieved the output from the command line client by reading the standard output/error, and when the process exits, the streams flush and I can read the end of the stream.
However, in this new mode, the process will dump a lot of text to the standard error/output, and then sit around waiting for the next command. Great for reducing overhead, but lousy since .NET buffers those streams.
In other words, since the process does not exit, I do not get the last portion of that output until:
- I ask the process to exit
- I issue another command
Is there a way for me to
- Ask the buffer to flush, meaning that I get whatever is in the buffer, even if it is not enough to make the buffer flush by itself?
- If not, can I set the buffer-size to 1 character?
- Anything else I can do?
I can deal with P/Invoke if that is what it takes.
Here's a LINQPad proof-of-concept program that would illustrate:
What it does is spin up a command prompt and feed it the DIR command, however notice that not until those 10 seconds have elapsed inside the loop does the program output the "C:>" prompt that the command prompt outputted right after producing the directory listing.
In other words, this is what the command prompt does:
- Produce directory listing
- Ask for another command by prompting "C:>\"
However, this is what the program below sees:
- Produce directory listing
- (wait 10 seconds)
- Close the input stream
See the prompt
void Main()
{
string clientExecutablePath = "cmd.exe";var psi = new ProcessStartInfo(); psi.FileName = clientExecutablePath; psi.RedirectStandardError = true; psi.RedirectStandardInput = true; psi.RedirectStandardOutput = true; psi.CreateNoWindow = true; psi.WorkingDirectory = @"C:\"; psi.WindowStyle = ProcessWindowStyle.Hidden; psi.UseShellExecute = false; psi.ErrorDialog = false; psi.StandardErrorEncoding = Encoding.GetEncoding("Windows-1252"); psi.StandardOutputEncoding = Encoding.GetEncoding("Windows-1252"); var p = Process.Start(psi); var input = p.StandardInput; var output = p.StandardOutput; var error = p.StandardError; Action<StreamReader, string> reader = delegate(StreamReader streamReader, string prefix) { string line; while ((line = streamReader.ReadLine()) != null) { Debug.WriteLine(prefix + line); } }; IAsyncResult outputReader = reader.BeginInvoke(output, "o: ", null, null); IAsyncResult errorReader = reader.BeginInvoke(error, "e: ", null, null); input.Write("dir\n"); input.Flush(); while (!p.HasExited) { Thread.Sleep(10000); input.Close(); } reader.EndInvoke(outputReader); reader.EndInvoke(errorReader);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我不认为有任何方法可以强制刷新,但是我有几个可能对您有帮助的替代方案:
HTH,
巴特
I don't think there's any way to force a flush, however I have a couple of alternatives which might help you:
HTH,
Bart