我正在开发一个 C# 应用程序,我需要启动一个外部控制台 程序来执行一些任务(提取文件)。我需要做的是重定向控制台程序的输出。类似 this one 的代码不起作用,因为它仅在控制台程序中写入新行时引发事件,但我使用的是“更新”控制台窗口中显示的内容,而不写入任何新行。每次更新控制台中的文本时如何引发事件?或者只是每隔 X 秒获取一次控制台程序的输出?提前致谢!
最佳答案
我遇到了一个与您描述的非常相似(可能完全相同)的问题:
我最后做的是这样的:
StandardOutput.BaseStream.BeginRead 的“无限”循环。BeginRead的回调中,检查EndRead的返回值是否为0;这意味着控制台进程已关闭其输出流(即永远不会再向标准输出写入任何内容)。BeginRead 强制您使用恒定长度的缓冲区,因此请检查 EndRead 的返回值是否等于缓冲区大小。这意味着可能有更多的输出等待读取,并且可能希望(甚至有必要)将这些输出全部处理成一个片段。我所做的是保留一个 StringBuilder 并附加到目前为止读取的输出。每当读取输出但其长度小于缓冲区长度时,通知自己(我用一个事件来做)有输出,将 StringBuilder 的内容发送给订阅者,然后清除它。 但是,在我的例子中,我只是将更多内容写入控制台的标准输出。我不确定在您的情况下“更新”输出意味着什么。
更新:我刚刚意识到(不是在解释你在做什么,这是一次很好的学习经历吗?)上面概述的逻辑有一个差一错误:如果输出的长度BeginRead 读取的内容正好 等于缓冲区的长度,然后此逻辑会将输出存储在 StringBuilder 中并在尝试查看时阻塞如果有更多输出要追加。只有当/如果有更多输出可用时,“当前”输出才会作为更大字符串的一部分发送回给您。
显然,要 100% 正确地做到这一点,需要一些方法来防止这种情况(或者更大的缓冲区加上对运气的信心)。
更新 2(代码):
免责声明: 此代码不是生产就绪的。这是我快速拼凑出一个概念验证解决方案以完成需要完成的工作的结果。请不要在您的生产应用程序中使用它。如果这段代码导致可怕的事情发生在你身上,我会假装是别人写的。
public class ConsoleInputReadEventArgs : EventArgs
{
public ConsoleInputReadEventArgs(string input)
{
this.Input = input;
}
public string Input { get; private set; }
}
public interface IConsoleAutomator
{
StreamWriter StandardInput { get; }
event EventHandler<ConsoleInputReadEventArgs> StandardInputRead;
}
public abstract class ConsoleAutomatorBase : IConsoleAutomator
{
protected readonly StringBuilder inputAccumulator = new StringBuilder();
protected readonly byte[] buffer = new byte[256];
protected volatile bool stopAutomation;
public StreamWriter StandardInput { get; protected set; }
protected StreamReader StandardOutput { get; set; }
protected StreamReader StandardError { get; set; }
public event EventHandler<ConsoleInputReadEventArgs> StandardInputRead;
protected void BeginReadAsync()
{
if (!this.stopAutomation) {
this.StandardOutput.BaseStream.BeginRead(this.buffer, 0, this.buffer.Length, this.ReadHappened, null);
}
}
protected virtual void OnAutomationStopped()
{
this.stopAutomation = true;
this.StandardOutput.DiscardBufferedData();
}
private void ReadHappened(IAsyncResult asyncResult)
{
var bytesRead = this.StandardOutput.BaseStream.EndRead(asyncResult);
if (bytesRead == 0) {
this.OnAutomationStopped();
return;
}
var input = this.StandardOutput.CurrentEncoding.GetString(this.buffer, 0, bytesRead);
this.inputAccumulator.Append(input);
if (bytesRead < this.buffer.Length) {
this.OnInputRead(this.inputAccumulator.ToString());
}
this.BeginReadAsync();
}
private void OnInputRead(string input)
{
var handler = this.StandardInputRead;
if (handler == null) {
return;
}
handler(this, new ConsoleInputReadEventArgs(input));
this.inputAccumulator.Clear();
}
}
public class ConsoleAutomator : ConsoleAutomatorBase, IConsoleAutomator
{
public ConsoleAutomator(StreamWriter standardInput, StreamReader standardOutput)
{
this.StandardInput = standardInput;
this.StandardOutput = standardOutput;
}
public void StartAutomate()
{
this.stopAutomation = false;
this.BeginReadAsync();
}
public void StopAutomation()
{
this.OnAutomationStopped();
}
}
这样使用:
var processStartInfo = new ProcessStartInfo
{
FileName = "myprocess.exe",
RedirectStandardInput = true,
RedirectStandardOutput = true,
UseShellExecute = false,
};
var process = Process.Start(processStartInfo);
var automator = new ConsoleAutomator(process.StandardInput, process.StandardOutput);
// AutomatorStandardInputRead is your event handler
automator.StandardInputRead += AutomatorStandardInputRead;
automator.StartAutomate();
// do whatever you want while that process is running
process.WaitForExit();
automator.StandardInputRead -= AutomatorStandardInputRead;
process.Close();
关于C# - 实时控制台输出重定向,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4501511/
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我正在使用puppet为ruby程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这
当我在Rails控制台中按向上或向左箭头时,出现此错误:irb(main):001:0>/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/rb-readline-0.4.2/lib/rbreadline.rb:4269:in`blockin_rl_dispatch_subseq':invalidbytesequenceinUTF-8(ArgumentError)我使用rvm来管理我的ruby安装。我正在使用=>ruby-2.0.0-p247[x86_64]我使用bundle来管理我的gem,并且我有rb-readline(0.4.2)(人们推荐的最少
我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.
这是一道面试题,我没有答对,但还是很好奇怎么解。你有N个人的大家庭,分别是1,2,3,...,N岁。你想给你的大家庭拍张照片。所有的家庭成员都排成一排。“我是家里的friend,建议家庭成员安排如下:”1岁的家庭成员坐在这一排的最左边。每两个坐在一起的家庭成员的年龄相差不得超过2岁。输入:整数N,1≤N≤55。输出:摄影师可以拍摄的照片数量。示例->输入:4,输出:4符合条件的数组:[1,2,3,4][1,2,4,3][1,3,2,4][1,3,4,2]另一个例子:输入:5输出:6符合条件的数组:[1,2,3,4,5][1,2,3,5,4][1,2,4,3,5][1,2,4,5,3][
我将我的Rails应用程序部署到OpenShift,它运行良好,但我无法在生产服务器上运行“Rails控制台”。它给了我这个错误。我该如何解决这个问题?我尝试更新rubygems,但它也给出了权限被拒绝的错误,我也无法做到。railsc错误:Warning:You'reusingRubygems1.8.24withSpring.UpgradetoatleastRubygems2.1.0andrun`gempristine--all`forbetterstartupperformance./opt/rh/ruby193/root/usr/share/rubygems/rubygems
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
我想使用spawn(针对多个并发子进程)在Ruby中执行一个外部进程,并将标准输出或标准错误收集到一个字符串中,其方式类似于使用Python的子进程Popen.communicate()可以完成的操作。我尝试将:out/:err重定向到一个新的StringIO对象,但这会生成一个ArgumentError,并且临时重新定义$stdxxx会混淆子进程的输出。 最佳答案 如果你不喜欢popen,这是我的方法:r,w=IO.pipepid=Process.spawn(command,:out=>w,:err=>[:child,:out])
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.