public static void Main(String[] args) { uint maxmem = 0; List <String> procargs = null; bool showhelp = false; int pid = 0; var p = new OptionSet() { { "m|maxmem=", "Max committed memory usage in bytes (accepted suffixes: K, M or G).", v => { if (v == null) { return; } if (v.EndsWith("K", StringComparison.OrdinalIgnoreCase)) { maxmem = UInt32.Parse(v.Substring(0, v.Length - 1)) << 10; return; } if (v.EndsWith("M", StringComparison.OrdinalIgnoreCase)) { maxmem = UInt32.Parse(v.Substring(0, v.Length - 1)) << 20; return; } if (v.EndsWith("G", StringComparison.OrdinalIgnoreCase)) { maxmem = UInt32.Parse(v.Substring(0, v.Length - 1)) << 30; return; } maxmem = UInt32.Parse(v); } }, { "p|pid=", "Attach to an already running process", (int v) => pid = v }, { "h|help", "Show this message and exit", v => showhelp = v != null }, { "?", "Show this message and exit", v => showhelp = v != null } }; try { procargs = p.Parse(args); } catch (OptionException ex) { Console.Write("ERROR: invalid argument "); Console.WriteLine(ex.Message); Console.WriteLine(); showhelp = true; } catch (FormatException) { Console.WriteLine("ERROR: invalid memory constraint"); Console.WriteLine(); showhelp = true; } if (!showhelp && (procargs.Count == 0 && pid == 0) || (pid > 0 && procargs.Count > 0)) { Console.WriteLine("ERROR: please provide either process name to start or PID of the already running process"); Console.WriteLine(); showhelp = true; } if (showhelp) { ShowHelp(p); return; } IntPtr hJob, hIOCP, hProcess; hJob = hIOCP = hProcess = IntPtr.Zero; PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); Thread listener = null; try { if (pid > 0) { // open existing process hProcess = CheckResult(ApiMethods.OpenProcess(ProcessAccessFlags.AllAccess, false, pid)); } else { // start suspended process pi = StartSuspendedProcess(procargs); hProcess = pi.hProcess; } var securityAttributes = new SECURITY_ATTRIBUTES(); securityAttributes.nLength = Marshal.SizeOf(securityAttributes); hJob = CheckResult(ApiMethods.CreateJobObject(ref securityAttributes, "procgov-" + Guid.NewGuid())); // create completion port hIOCP = CheckResult(ApiMethods.CreateIoCompletionPort(ApiMethods.INVALID_HANDLE_VALUE, IntPtr.Zero, IntPtr.Zero, 1)); var assocInfo = new JOBOBJECT_ASSOCIATE_COMPLETION_PORT { CompletionKey = IntPtr.Zero, CompletionPort = hIOCP }; uint size = (uint)Marshal.SizeOf(assocInfo); CheckResult(ApiMethods.SetInformationJobObject(hJob, JOBOBJECTINFOCLASS.AssociateCompletionPortInformation, ref assocInfo, size)); // start listening thread listener = new Thread(CompletionPortListener); listener.Start(hIOCP); if (maxmem > 0.0f) { // configure constraints var limitInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION { BasicLimitInformation = new JOBOBJECT_BASIC_LIMIT_INFORMATION { LimitFlags = JobInformationLimitFlags.JOB_OBJECT_LIMIT_PROCESS_MEMORY | JobInformationLimitFlags.JOB_OBJECT_LIMIT_BREAKAWAY_OK | JobInformationLimitFlags.JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK }, ProcessMemoryLimit = (UIntPtr)maxmem }; size = (uint)Marshal.SizeOf(limitInfo); CheckResult(ApiMethods.SetInformationJobObject(hJob, JOBOBJECTINFOCLASS.ExtendedLimitInformation, ref limitInfo, size)); } // assign a process to a job to apply constraints CheckResult(ApiMethods.AssignProcessToJobObject(hJob, hProcess)); // resume process main thread (if it was started by us) if (pid == 0) { CheckResult(ApiMethods.ResumeThread(pi.hThread)); // and we can close the thread handle CloseHandle(pi.hThread); } if (ApiMethods.WaitForSingleObject(hProcess, ApiMethods.INFINITE) == 0xFFFFFFFF) { throw new Win32Exception(); } } finally { CloseHandle(hIOCP); // wait for the listener thread to finish if (listener != null && listener.IsAlive) { listener.Join(); } CloseHandle(hProcess); CloseHandle(hJob); } }
internal static extern bool SetInformationJobObject(SafeJobObjectHandle hJob, JobObjectInformationClass JobObjectInfoClass, [In] ref JOBOBJECT_ASSOCIATE_COMPLETION_PORT lpJobObjectInfo, int cbJobObjectInfoLength);