我从 C# 启动一个进程,如下所示:
public bool Execute()
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.Arguments = "the command";
startInfo.FileName = "C:\\MyApp.exe";
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
Log.LogMessage("{0} {1}", startInfo.FileName, startInfo.Arguments);
using (Process myProcess = Process.Start(startInfo))
{
StringBuilder output = new StringBuilder();
myProcess.OutputDataReceived += delegate(object sender, DataReceivedEventArgs e)
{
Log.LogMessage(Thread.CurrentThread.ManagedThreadId.ToString() + e.Data);
};
myProcess.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs e)
{
Log.LogError(Thread.CurrentThread.ManagedThreadId.ToString() + " " + e.Data);
};
myProcess.BeginErrorReadLine();
myProcess.BeginOutputReadLine();
myProcess.WaitForExit();
}
return false;
}
但这有一个问题...如果有问题的应用程序按以下顺序写入 std out 和 std err:
std out: msg 1
std err: msg 2
std out: msg 3
然后我从日志中看到的输出是:
msg 2
msg 1
msg 3
这似乎是因为事件处理程序是在另一个线程中执行的。所以我的问题是如何维护写入 std err 和 std out 的进程顺序?
我想过使用时间戳,但由于线程的抢占性质,我认为这行不通..
更新:确认在数据上使用时间戳是没有用的。
最终更新:接受的答案解决了这个问题——但是它确实有一个缺点,当流被合并时,没有办法知道写入了哪个流。因此,如果您需要写入 stderr == failure 的逻辑而不是应用程序退出代码,您可能仍然会被搞砸。
最佳答案
据我所知,您想保留 stdout/stderr 消息的顺序。我没有看到用 C# 托管进程执行此操作的任何体面的方法(反射 - 是的,令人讨厌的子类化黑客 - 是的)。看起来它几乎是硬编码的。
此功能不依赖于线程本身。如果要保持顺序,STDOUT 和STDERROR 必须使用相同的句柄(缓冲区)。如果他们使用相同的缓冲区,它将被同步。
这是 Process.cs 的一个片段:
if (startInfo.RedirectStandardOutput) {
CreatePipe(out standardOutputReadPipeHandle,
out startupInfo.hStdOutput,
false);
} else {
startupInfo.hStdOutput = new SafeFileHandle(
NativeMethods.GetStdHandle(
NativeMethods.STD_OUTPUT_HANDLE),
false);
}
if (startInfo.RedirectStandardError) {
CreatePipe(out standardErrorReadPipeHandle,
out startupInfo.hStdError,
false);
} else {
startupInfo.hStdError = new SafeFileHandle(
NativeMethods.GetStdHandle(
NativeMethods.STD_ERROR_HANDLE),
false);
}
如您所见,将有两个缓冲区,如果我们有两个缓冲区,我们已经丢失了订单信息。
基本上,您需要创建自己的 Process() 类来处理这种情况。伤心?是的。 好消息是这并不难,看起来很简单。这是从 StackOverflow 获取的代码,不是 C# 但足以理解算法:
function StartProcessWithRedirectedOutput(const ACommandLine: string; const AOutputFile: string;
AShowWindow: boolean = True; AWaitForFinish: boolean = False): Integer;
var
CommandLine: string;
StartupInfo: TStartupInfo;
ProcessInformation: TProcessInformation;
StdOutFileHandle: THandle;
begin
Result := 0;
StdOutFileHandle := CreateFile(PChar(AOutputFile), GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, 0);
Win32Check(StdOutFileHandle <> INVALID_HANDLE_VALUE);
try
Win32Check(SetHandleInformation(StdOutFileHandle, HANDLE_FLAG_INHERIT, 1));
FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
FillChar(ProcessInformation, SizeOf(TProcessInformation), 0);
StartupInfo.cb := SizeOf(TStartupInfo);
StartupInfo.dwFlags := StartupInfo.dwFlags or STARTF_USESTDHANDLES;
StartupInfo.hStdInput := GetStdHandle(STD_INPUT_HANDLE);
StartupInfo.hStdOutput := StdOutFileHandle;
StartupInfo.hStdError := StdOutFileHandle;
if not(AShowWindow) then
begin
StartupInfo.dwFlags := StartupInfo.dwFlags or STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow := SW_HIDE;
end;
CommandLine := ACommandLine;
UniqueString(CommandLine);
Win32Check(CreateProcess(nil, PChar(CommandLine), nil, nil, True,
CREATE_NEW_PROCESS_GROUP + NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInformation));
try
Result := ProcessInformation.dwProcessId;
if AWaitForFinish then
WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
finally
CloseHandle(ProcessInformation.hProcess);
CloseHandle(ProcessInformation.hThread);
end;
finally
CloseHandle(StdOutFileHandle);
end;
end;
来源:How to redirect large amount of output from command executed by CreateProcess?
您想要使用 CreatePipe 而不是文件。从管道中,您可以像这样异步读取:
standardOutput = new StreamReader(new FileStream(
standardOutputReadPipeHandle,
FileAccess.Read,
4096,
false),
enc,
true,
4096);
和 BeginReadOutput()
if (output == null) {
Stream s = standardOutput.BaseStream;
output = new AsyncStreamReader(this, s,
new UserCallBack(this.OutputReadNotifyUser),
standardOutput.CurrentEncoding);
}
output.BeginReadLine();
关于c# - 以正确的顺序捕获进程 stdout 和 stderr,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18529662/
在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',
我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击
我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是
我遵循了教程http://gettingstartedwithchef.com/,第1章。我的运行list是"run_list":["recipe[apt]","recipe[phpap]"]我的phpapRecipe默认Recipeinclude_recipe"apache2"include_recipe"build-essential"include_recipe"openssl"include_recipe"mysql::client"include_recipe"mysql::server"include_recipe"php"include_recipe"php::modul
question的一些答案关于redirect_to让我想到了其他一些问题。基本上,我正在使用Rails2.1编写博客应用程序。我一直在尝试自己完成大部分工作(因为我对Rails有所了解),但在需要时会引用Internet上的教程和引用资料。我设法让一个简单的博客正常运行,然后我尝试添加评论。靠我自己,我设法让它进入了可以从script/console添加评论的阶段,但我无法让表单正常工作。我遵循的其中一个教程建议在帖子Controller中创建一个“评论”操作,以添加评论。我的问题是:这是“标准”方式吗?我的另一个问题的答案之一似乎暗示应该有一个CommentsController参
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:
我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
我需要一些关于TDD概念的帮助。假设我有以下代码defexecute(command)casecommandwhen"c"create_new_characterwhen"i"display_inventoryendenddefcreate_new_character#dostufftocreatenewcharacterenddefdisplay_inventory#dostufftodisplayinventoryend现在我不确定要为什么编写单元测试。如果我为execute方法编写单元测试,那不是几乎涵盖了我对create_new_character和display_invent