据我了解,您希望保留 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;
资料来源:如何从 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();