Ответ 1
Насколько я понимаю, вы хотите сохранить порядок сообщений stdout/stderr. Я не вижу никакого способа DECENT сделать это с помощью управляемого С# процесса (отражение - да, неприятный взлом подкласса - да). Кажется, что он довольно жестко закодирован.
Эта функциональность не зависит от самих потоков. Если вы хотите сохранить порядок, 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, а не С#, но достаточно, чтобы понять алгоритм:
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();