Files
windows-imaging-tools-builder/scripts/Build.Process.cs
T

65 lines
4.1 KiB
C#

using System;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace PveImageBuilder {
public sealed class BuildProcess : IDisposable {
[StructLayout(LayoutKind.Sequential)] struct BasicLimits {
public long PerProcessTime, PerJobTime;
public uint LimitFlags;
public UIntPtr MinWorkingSet, MaxWorkingSet;
public uint ActiveProcessLimit;
public UIntPtr Affinity;
public uint PriorityClass, SchedulingClass;
}
[StructLayout(LayoutKind.Sequential)] struct IoCounters { public ulong ReadOps, WriteOps, OtherOps, ReadBytes, WriteBytes, OtherBytes; }
[StructLayout(LayoutKind.Sequential)] struct ExtendedLimits {
public BasicLimits Basic;
public IoCounters Io;
public UIntPtr ProcessMemory, JobMemory, PeakProcessMemory, PeakJobMemory;
}
[DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern IntPtr CreateJobObject(IntPtr attributes, string name);
[DllImport("kernel32.dll", SetLastError=true)] static extern bool SetInformationJobObject(IntPtr job, int infoClass, ref ExtendedLimits info, uint length);
[DllImport("kernel32.dll", SetLastError=true)] static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
[DllImport("kernel32.dll", SetLastError=true)] static extern bool TerminateJobObject(IntPtr job, uint exitCode);
[DllImport("kernel32.dll")] static extern bool CloseHandle(IntPtr handle);
readonly ConcurrentQueue<string> output = new ConcurrentQueue<string>();
readonly Process process = new Process();
IntPtr job;
public BuildProcess(string executable, string arguments, string directory) {
job=CreateJobObject(IntPtr.Zero,null);
if (job==IntPtr.Zero) throw new Win32Exception(Marshal.GetLastWin32Error());
try {
var limits=new ExtendedLimits();
limits.Basic.LimitFlags=0x2000; // Kill only this job's processes on close.
if (!SetInformationJobObject(job,9,ref limits,(uint)Marshal.SizeOf(typeof(ExtendedLimits)))) throw new Win32Exception(Marshal.GetLastWin32Error());
process.StartInfo=new ProcessStartInfo(executable,arguments) { UseShellExecute=false, CreateNoWindow=true, RedirectStandardOutput=true, RedirectStandardError=true, WorkingDirectory=directory, StandardOutputEncoding=new System.Text.UTF8Encoding(false), StandardErrorEncoding=new System.Text.UTF8Encoding(false) };
process.OutputDataReceived += (sender,e) => { if(e.Data!=null) output.Enqueue(e.Data); };
process.ErrorDataReceived += (sender,e) => { if(e.Data!=null) output.Enqueue(e.Data); };
process.Start();
if (!AssignProcessToJobObject(job,process.Handle)) throw new Win32Exception(Marshal.GetLastWin32Error());
process.BeginOutputReadLine();
process.BeginErrorReadLine();
} catch {
try { if(!process.HasExited) process.Kill(); } catch {}
CloseHandle(job); job=IntPtr.Zero;
process.Dispose();
throw;
}
}
public string[] ReadOutput() { var lines=new System.Collections.Generic.List<string>(); string line; while(output.TryDequeue(out line)) lines.Add(line); return lines.ToArray(); }
public bool HasExited { get { return process.HasExited; } }
public int ExitCode { get { return process.ExitCode; } }
public int Id { get { return process.Id; } }
public void Wait() { process.WaitForExit(); }
public void Cancel() {
if (!TerminateJobObject(job,130)) throw new Win32Exception(Marshal.GetLastWin32Error());
if (!process.WaitForExit(10000)) throw new TimeoutException("Build worker did not stop.");
process.WaitForExit();
}
public void Dispose() { if(job!=IntPtr.Zero) { CloseHandle(job); job=IntPtr.Zero; } process.Dispose(); }
}
}