Каждый .NET-разработчик, которому когда-либо нужно было запустить процесс и захватить его вывод, написал какую-то вариацию одного и того же опасного шаблонного кода: асинхронное чтение из stdout, асинхронное чтение из stderr, WaitForExitAsync, не забыть очистить оба потока, иначе будет deadlock. Это хорошо известная ловушка, которая существует уже много лет.
.NET 11 наконец-то исправляет это правильно.
RunAndCaptureTextAsync
Главное добавление: единственный статический метод, который запускает процесс, захватывает stdout и stderr, и ожидает завершения без deadlock.
var result = await Process.RunAndCaptureTextAsync("dotnet", "--version");
Console.WriteLine(result.StandardOutput);
Один вызов. Никакого ручного опустошения потоков. Никакого тщательно расположенного WaitForExit. Если вам просто нужно запустить что-то и получить его вывод, это именно тот API, который вы хотите.
Также есть Process.RunAsync для случая, когда нужно ожидать завершения без захвата вывода.
KillOnParentExit
Распространённая проблема с запущенными процессами: если родительский процесс падает или завершается, дочерние процессы продолжают работать как сироты. KillOnParentExit позволяет объявить при запуске процесса, что дочерний процесс должен быть завершён, когда завершается родительский процесс.
Это функциональность, которая существовала в платформо-специфических формах (job objects на Windows, prctl на Linux), но требовала p/invoke или сторонних библиотек для использования из .NET. Теперь это полноценное свойство на ProcessStartInfo.
API на основе SafeProcessHandle
Новая легковесная поверхность API построена вокруг SafeProcessHandle, а не вокруг полного класса Process. Полный класс Process несёт много состояния и его трудно обрезать — путь SafeProcessHandle более дружелюбен к триммеру для приложений, которым нужно минимизировать размер выходных данных (WASM, native AOT).
Полный контроль над наследованием дескрипторов
Обновление также добавляет детальный контроль над тем, какие дескрипторы наследует дочерний процесс и как перенаправляются стандартные дескрипторы. Ранее можно было перенаправлять stdin/stdout/stderr, но нельзя было указать точно, какие дескрипторы наследовать на уровне ОС. Новые API открывают этот контроль.
Почему Это Важно
Класс Process используется в инструментарии, системах сборки, запускателях тестов и любом приложении, которое вызывает другие исполняемые файлы. Старая поверхность API восходила к .NET Framework и показывала свой возраст. Это не ломающее изменение — старые API продолжают работать — но новый код должен предпочитать новую поверхность.
Для обрезанных приложений или сценариев компиляции AOT путь SafeProcessHandle особенно приветствуется. Старый класс Process приносил много кода с тяжёлым использованием рефлексии, что осложняло обрезку.
Оригинальный пост: Process API Improvements in .NET 11
