// Launch a child process and put it in the job immediately, redirecting its // stdout/stderr to log files (matches the pod_out/err.txt + bridge_out/err.txt // the old launch_pod.ps1 produced). // // The assign happens right after Start(), before DOSBox/pythonw has a chance to // spawn anything, so the whole tree is captured. (A CREATE_SUSPENDED launch // would close even that microscopic gap; neither of these children forks at // startup, so Start()+assign is sufficient here.) using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; namespace VwePod { internal static class ChildProcess { public static Process StartInJob( JobObject job, string exe, string args, IDictionary extraEnv, string outPath, string errPath, string workingDir) { var psi = new ProcessStartInfo { FileName = exe, Arguments = args, UseShellExecute = false, // inherit our env; allow job assign CreateNoWindow = false, // children make their own windows RedirectStandardOutput = true, RedirectStandardError = true, }; if (!string.IsNullOrEmpty(workingDir)) psi.WorkingDirectory = workingDir; if (extraEnv != null) foreach (var kv in extraEnv) psi.Environment[kv.Key] = kv.Value; var proc = new Process { StartInfo = psi }; var outWriter = new StreamWriter(outPath, false) { AutoFlush = true }; var errWriter = new StreamWriter(errPath, false) { AutoFlush = true }; proc.OutputDataReceived += (s, e) => { if (e.Data != null) lock (outWriter) outWriter.WriteLine(e.Data); }; proc.ErrorDataReceived += (s, e) => { if (e.Data != null) lock (errWriter) errWriter.WriteLine(e.Data); }; proc.Exited += (s, e) => { try { outWriter.Dispose(); errWriter.Dispose(); } catch { } }; proc.EnableRaisingEvents = true; proc.Start(); job.Add(proc.Handle); // capture before it can spawn a child proc.BeginOutputReadLine(); proc.BeginErrorReadLine(); return proc; } } }