using System.Text; using System.Threading; using System.Runtime.InteropServices; using System.ComponentModel; using System.ComponentModel.Design; using System.Runtime.CompilerServices; using System.Diagnostics; using System; using System.Collections; using System.Collections.Generic; using System.IO; using Microsoft.Win32.SafeHandles; using System.Collections.Specialized; using System.Globalization; using System.Security; using System.Security.Permissions; using System.Runtime.Versioning; using System.Runtime.ConstrainedExecution; using Microsoft.Win32; namespace TrustedUninstaller.Shared { [DefaultEvent("Exited"), DefaultProperty("StartInfo"), HostProtection(SharedState = true, Synchronization = true, ExternalProcessMgmt = true, SelfAffectingProcessMgmt = true)] public static class AugmentedProcess { public class Process : Component { public enum CreateType { UserToken, RawToken } // // FIELDS // bool haveProcessId; int processId; bool haveProcessHandle; SafeProcessHandle m_processHandle; bool isRemoteMachine; string machineName; ProcessInfo processInfo; Int32 m_processAccess; ProcessThreadCollection threads; ProcessModuleCollection modules; bool haveMainWindow; IntPtr mainWindowHandle; // no need to use SafeHandle for window string mainWindowTitle; bool haveWorkingSetLimits; bool haveProcessorAffinity; IntPtr processorAffinity; bool havePriorityClass; ProcessPriorityClass priorityClass; ProcessStartInfo startInfo; bool watchForExit; bool watchingForExit; EventHandler onExited; bool exited; int exitCode; bool signaled; DateTime exitTime; bool haveExitTime; bool responding; bool haveResponding; bool priorityBoostEnabled; bool havePriorityBoostEnabled; bool raisedOnExited; bool expandEnvironmentVariables; RegisteredWaitHandle registeredWaitHandle; WaitHandle waitHandle; ISynchronizeInvoke synchronizingObject; StreamReader standardOutput; StreamWriter standardInput; StreamReader standardError; OperatingSystem operatingSystem; bool disposed; static object s_CreateProcessLock = new object(); // This enum defines the operation mode for redirected process stream. // We don't support switching between synchronous mode and asynchronous mode. private enum StreamReadMode { undefined, syncMode, asyncMode } StreamReadMode outputStreamReadMode; StreamReadMode errorStreamReadMode; public event DataReceivedEventHandler OutputDataReceived; public event DataReceivedEventHandler ErrorDataReceived; // Abstract the stream details internal AsyncStreamReader output; internal AsyncStreamReader error; internal bool pendingOutputRead; internal bool pendingErrorRead; internal static TraceSwitch processTracing = null; public Process() { this.machineName = "."; this.outputStreamReadMode = StreamReadMode.undefined; this.errorStreamReadMode = StreamReadMode.undefined; this.m_processAccess = NativeMethods.PROCESS_ALL_ACCESS; } [ResourceExposure(ResourceScope.Machine)] Process(string machineName, bool isRemoteMachine, int processId, ProcessInfo processInfo) : base() { this.processInfo = processInfo; this.machineName = machineName; this.isRemoteMachine = isRemoteMachine; this.processId = processId; this.haveProcessId = true; this.outputStreamReadMode = StreamReadMode.undefined; this.errorStreamReadMode = StreamReadMode.undefined; this.m_processAccess = NativeMethods.PROCESS_ALL_ACCESS; } // // PROPERTIES // bool Associated { get { return haveProcessId || haveProcessHandle; } } public string ProcessName { get { this.EnsureState(Process.State.HaveProcessInfo); return this.processInfo.processName; } } public int ExitCode { get { EnsureState(State.Exited); return exitCode; } } public bool HasExited { get { if (!exited) { EnsureState(State.Associated); SafeProcessHandle handle = null; try { handle = GetProcessHandle(NativeMethods.PROCESS_QUERY_INFORMATION | NativeMethods.SYNCHRONIZE, false); if (handle.IsInvalid) { exited = true; } else { int exitCode; // Although this is the wrong way to check whether the process has exited, // it was historically the way we checked for it, and a lot of code then took a dependency on // the fact that this would always be set before the pipes were closed, so they would read // the exit code out after calling ReadToEnd() or standard output or standard error. In order // to allow 259 to function as a valid exit code and to break as few people as possible that // took the ReadToEnd dependency, we check for an exit code before doing the more correct // check to see if we have been signalled. if (NativeMethods.GetExitCodeProcess(handle, out exitCode) && exitCode != NativeMethods.STILL_ACTIVE) { this.exited = true; this.exitCode = exitCode; } else { // The best check for exit is that the kernel process object handle is invalid, // or that it is valid and signaled. Checking if the exit code != STILL_ACTIVE // does not guarantee the process is closed, // since some process could return an actual STILL_ACTIVE exit code (259). if (!signaled) // if we just came from WaitForExit, don't repeat { ProcessWaitHandle wh = null; try { wh = new ProcessWaitHandle(handle); this.signaled = wh.WaitOne(0, false); } finally { if (wh != null) wh.Close(); } } if (signaled) { if (!NativeMethods.GetExitCodeProcess(handle, out exitCode)) throw new Win32Exception(); this.exited = true; this.exitCode = exitCode; } } } } finally { ReleaseProcessHandle(handle); } if (exited) { RaiseOnExited(); } } return exited; } } public IntPtr Handle { [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] get { EnsureState(State.Associated); return OpenProcessHandle(this.m_processAccess).DangerousGetHandle(); } } [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public SafeProcessHandle SafeHandle { get { EnsureState(State.Associated); return OpenProcessHandle(this.m_processAccess); } } public int Id { get { EnsureState(State.HaveId); return processId; } } public string MachineName { get { EnsureState(State.Associated); return machineName; } } [System.Runtime.InteropServices.ComVisible(false)] public long NonpagedSystemMemorySize64 { get { EnsureState(State.HaveNtProcessInfo); return processInfo.poolNonpagedBytes; } } [System.Runtime.InteropServices.ComVisible(false)] public long PagedMemorySize64 { get { EnsureState(State.HaveNtProcessInfo); return processInfo.pageFileBytes; } } [System.Runtime.InteropServices.ComVisible(false)] public long PagedSystemMemorySize64 { get { EnsureState(State.HaveNtProcessInfo); return processInfo.poolPagedBytes; } } [System.Runtime.InteropServices.ComVisible(false)] public long PeakPagedMemorySize64 { get { EnsureState(State.HaveNtProcessInfo); return processInfo.pageFileBytesPeak; } } [System.Runtime.InteropServices.ComVisible(false)] public long PeakWorkingSet64 { get { EnsureState(State.HaveNtProcessInfo); return processInfo.workingSetPeak; } } [System.Runtime.InteropServices.ComVisible(false)] public long PeakVirtualMemorySize64 { get { EnsureState(State.HaveNtProcessInfo); return processInfo.virtualBytesPeak; } } private OperatingSystem OperatingSystem { get { if (operatingSystem == null) { operatingSystem = Environment.OSVersion; } return operatingSystem; } } [System.Runtime.InteropServices.ComVisible(false)] public long PrivateMemorySize64 { get { EnsureState(State.HaveNtProcessInfo); return processInfo.privateBytes; } } public int SessionId { get { EnsureState(State.HaveNtProcessInfo); return processInfo.sessionId; } } public ProcessStartInfo StartInfo { get { return startInfo; } [ResourceExposure(ResourceScope.Machine)] set { if (value == null) { throw new ArgumentNullException("value"); } startInfo = value; } } public bool ExpandEnvironmentVariables { get { return expandEnvironmentVariables; } set { expandEnvironmentVariables = value; } } public ISynchronizeInvoke SynchronizingObject { get { if (this.synchronizingObject == null && DesignMode) { IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); if (host != null) { object baseComponent = host.RootComponent; if (baseComponent != null && baseComponent is ISynchronizeInvoke) this.synchronizingObject = (ISynchronizeInvoke)baseComponent; } } return this.synchronizingObject; } set { this.synchronizingObject = value; } } [System.Runtime.InteropServices.ComVisible(false)] public long VirtualMemorySize64 { get { EnsureState(State.HaveNtProcessInfo); return processInfo.virtualBytes; } } public bool EnableRaisingEvents { get { return watchForExit; } set { if (value != watchForExit) { if (Associated) { if (value) { OpenProcessHandle(); EnsureWatchingForExit(); } else { StopWatchingForExit(); } } watchForExit = value; } } } public StreamWriter StandardInput { get { if (standardInput == null) { throw new InvalidOperationException("CantGetStandardIn"); } return standardInput; } } public StreamReader StandardOutput { get { if (standardOutput == null) { throw new InvalidOperationException("CantGetStandardOut"); } if (outputStreamReadMode == StreamReadMode.undefined) { outputStreamReadMode = StreamReadMode.syncMode; } else if (outputStreamReadMode != StreamReadMode.syncMode) { throw new InvalidOperationException("CantMixSyncAsyncOperation"); } return standardOutput; } } public StreamReader StandardError { get { if (standardError == null) { throw new InvalidOperationException("CantGetStandardError"); } if (errorStreamReadMode == StreamReadMode.undefined) { errorStreamReadMode = StreamReadMode.syncMode; } else if (errorStreamReadMode != StreamReadMode.syncMode) { throw new InvalidOperationException("CantMixSyncAsyncOperation"); } return standardError; } } public int WorkingSet { get { EnsureState(State.HaveNtProcessInfo); return unchecked((int)processInfo.workingSet); } } [System.Runtime.InteropServices.ComVisible(false)] public long WorkingSet64 { get { EnsureState(State.HaveNtProcessInfo); return processInfo.workingSet; } } public event EventHandler Exited { add { onExited += value; } remove { onExited -= value; } } /// /// Release the temporary handle we used to get process information. /// If we used the process handle stored in the process object (we have all access to the handle,) don't release it. /// /// void ReleaseProcessHandle(SafeProcessHandle handle) { if (handle == null) { return; } if (haveProcessHandle && handle == m_processHandle) { return; } handle.Close(); } /// /// This is called from the threadpool when a proces exits. /// /// private void CompletionCallback(object context, bool wasSignaled) { StopWatchingForExit(); RaiseOnExited(); } /// /// /// /// Free any resources associated with this component. /// /// protected override void Dispose(bool disposing) { if (!disposed) { if (disposing) { //Dispose managed and unmanaged resources Close(); } this.disposed = true; base.Dispose(disposing); } } /// /// /// Frees any resources associated with this component. /// /// public void Close() { if (Associated) { if (haveProcessHandle) { StopWatchingForExit(); m_processHandle.Close(); m_processHandle = null; haveProcessHandle = false; } haveProcessId = false; isRemoteMachine = false; machineName = "."; raisedOnExited = false; //Don't call close on the Readers and writers //since they might be referenced by somebody else while the //process is still alive but this method called. standardOutput = null; standardInput = null; standardError = null; output = null; error = null; Refresh(); } } /// /// Helper method for checking preconditions when accessing properties. /// /// [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] void EnsureState(State state) { if ((state & State.Associated) != (State)0) if (!Associated) throw new InvalidOperationException("NoAssociatedProcess"); if ((state & State.IsLocal) != (State)0 && isRemoteMachine) { throw new NotSupportedException("NotSupportedRemote"); } if ((state & Process.State.HaveProcessInfo) != (Process.State) 0 && this.processInfo == null) { if ((state & Process.State.HaveId) == (Process.State) 0) this.EnsureState(Process.State.HaveId); this.processInfo = GetProcessInfo(this.processId, this.machineName); if (this.processInfo == null) throw new InvalidOperationException("NoProcessInfo"); } if ((state & State.Exited) != (State)0) { if (!HasExited) { throw new InvalidOperationException("WaitTillExit"); } if (!haveProcessHandle) { throw new InvalidOperationException("NoProcessHandle"); } } } void EnsureWatchingForExit() { if (!watchingForExit) { lock (this) { if (!watchingForExit) { watchingForExit = true; try { this.waitHandle = new ProcessWaitHandle(m_processHandle); this.registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(this.waitHandle, new WaitOrTimerCallback(this.CompletionCallback), null, -1, true); } catch { watchingForExit = false; throw; } } } } } protected void OnExited() { EventHandler exited = onExited; if (exited != null) { if (this.SynchronizingObject != null && this.SynchronizingObject.InvokeRequired) this.SynchronizingObject.BeginInvoke(exited, new object[] { this, EventArgs.Empty }); else exited(this, EventArgs.Empty); } } [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] SafeProcessHandle GetProcessHandle(int access, bool throwIfExited) { if (haveProcessHandle) { if (throwIfExited) { // Since haveProcessHandle is true, we know we have the process handle // open with at least SYNCHRONIZE access, so we can wait on it with // zero timeout to see if the process has exited. ProcessWaitHandle waitHandle = null; try { waitHandle = new ProcessWaitHandle(m_processHandle); if (waitHandle.WaitOne(0, false)) { if (haveProcessId) throw new InvalidOperationException("Process has exited: " + processId); else throw new InvalidOperationException("ProcessHasExitedNoId"); } } finally { if (waitHandle != null) { waitHandle.Close(); } } } return m_processHandle; } else { throw new Exception("(AME) Process handle not available."); } } /// /// Gets a short-term handle to the process, with the given access. If a handle exists, /// then it is reused. If the process has exited, it throws an exception. /// /// SafeProcessHandle GetProcessHandle(int access) { return GetProcessHandle(access, true); } /// /// Opens a long-term handle to the process, with all access. If a handle exists, /// then it is reused. If the process has exited, it throws an exception. /// /// SafeProcessHandle OpenProcessHandle() { return OpenProcessHandle(NativeMethods.PROCESS_ALL_ACCESS); } SafeProcessHandle OpenProcessHandle(Int32 access) { if (!haveProcessHandle) { //Cannot open a new process handle if the object has been disposed, since finalization has been suppressed. if (this.disposed) { throw new ObjectDisposedException(GetType().Name); } SetProcessHandle(GetProcessHandle(access)); } return m_processHandle; } /// /// Raise the Exited event, but make sure we don't do it more than once. /// /// void RaiseOnExited() { if (!raisedOnExited) { lock (this) { if (!raisedOnExited) { raisedOnExited = true; OnExited(); } } } } /// /// /// Discards any information about the associated process /// that has been cached inside the process component. After is called, the /// first request for information for each property causes the process component /// to obtain a new value from the associated process. /// /// public void Refresh() { processInfo = null; threads = null; modules = null; mainWindowTitle = null; exited = false; signaled = false; haveMainWindow = false; haveWorkingSetLimits = false; haveProcessorAffinity = false; havePriorityClass = false; haveExitTime = false; haveResponding = false; havePriorityBoostEnabled = false; } /// /// Helper to associate a process handle with this component. /// /// void SetProcessHandle(SafeProcessHandle processHandle) { this.m_processHandle = processHandle; this.haveProcessHandle = true; if (watchForExit) { EnsureWatchingForExit(); } } /// /// Helper to associate a process id with this component. /// /// [ResourceExposure(ResourceScope.Machine)] void SetProcessId(int processId) { this.processId = processId; this.haveProcessId = true; } /// /// /// Starts a process specified by the property of this /// component and associates it with the /// . If a process resource is reused /// rather than started, the reused process is associated with this /// component. /// /// [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] public bool Start(CreateType type, ref Win32.TokensEx.SafeTokenHandle token) { Close(); ProcessStartInfo startInfo = StartInfo; if (startInfo.FileName.Length == 0) throw new InvalidOperationException("FileNameMissing"); return StartWithCreateProcess(startInfo, type, ref token); } [ResourceExposure(ResourceScope.Process)] [ResourceConsumption(ResourceScope.Process)] private static void CreatePipeWithSecurityAttributes(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, NativeMethods.SECURITY_ATTRIBUTES lpPipeAttributes, int nSize) { bool ret = NativeMethods.CreatePipe(out hReadPipe, out hWritePipe, lpPipeAttributes, nSize); if (!ret || hReadPipe.IsInvalid || hWritePipe.IsInvalid) { throw new Win32Exception(); } } // Using synchronous Anonymous pipes for process input/output redirection means we would end up // wasting a worker threadpool thread per pipe instance. Overlapped pipe IO is desirable, since // it will take advantage of the NT IO completion port infrastructure. But we can't really use // Overlapped I/O for process input/output as it would break Console apps (managed Console class // methods such as WriteLine as well as native CRT functions like printf) which are making an // assumption that the console standard handles (obtained via GetStdHandle()) are opened // for synchronous I/O and hence they can work fine with ReadFile/WriteFile synchrnously! [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] private void CreatePipe(out SafeFileHandle parentHandle, out SafeFileHandle childHandle, bool parentInputs) { NativeMethods.SECURITY_ATTRIBUTES securityAttributesParent = new NativeMethods.SECURITY_ATTRIBUTES(); securityAttributesParent.bInheritHandle = true; SafeFileHandle hTmp = null; try { if (parentInputs) { CreatePipeWithSecurityAttributes(out childHandle, out hTmp, securityAttributesParent, 0); } else { CreatePipeWithSecurityAttributes(out hTmp, out childHandle, securityAttributesParent, 0); } // Duplicate the parent handle to be non-inheritable so that the child process // doesn't have access. This is done for correctness sake, exact reason is unclear. // One potential theory is that child process can do something brain dead like // closing the parent end of the pipe and there by getting into a blocking situation // as parent will not be draining the pipe at the other end anymore. if (!NativeMethods.DuplicateHandle(new HandleRef(this, NativeMethods.GetCurrentProcess()), hTmp, new HandleRef(this, NativeMethods.GetCurrentProcess()), out parentHandle, 0, false, NativeMethods.DUPLICATE_SAME_ACCESS)) { throw new Win32Exception(); } } finally { if (hTmp != null && !hTmp.IsInvalid) { hTmp.Close(); } } } private static StringBuilder BuildCommandLine(string executableFileName, string arguments) { // Construct a StringBuilder with the appropriate command line // to pass to CreateProcess. If the filename isn't already // in quotes, we quote it here. This prevents some security // problems (it specifies exactly which part of the string // is the file to execute). StringBuilder commandLine = new StringBuilder(); string fileName = executableFileName.Trim(); bool fileNameIsQuoted = (fileName.StartsWith("\"", StringComparison.Ordinal) && fileName.EndsWith("\"", StringComparison.Ordinal)); if (!fileNameIsQuoted) { commandLine.Append("\""); } commandLine.Append(fileName); if (!fileNameIsQuoted) { commandLine.Append("\""); } if (!String.IsNullOrEmpty(arguments)) { commandLine.Append(" "); commandLine.Append(arguments); } return commandLine; } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private bool StartWithCreateProcess(ProcessStartInfo startInfo, CreateType type, ref Win32.TokensEx.SafeTokenHandle token) { // See knowledge base article Q190351 for an explanation of the following code. Noteworthy tricky points: // * The handles are duplicated as non-inheritable before they are passed to CreateProcess so // that the child process can not close them // * CreateProcess allows you to redirect all or none of the standard IO handles, so we use // GetStdHandle for the handles that are not being redirected //Cannot start a new process and store its handle if the object has been disposed, since finalization has been suppressed. if (this.disposed) { throw new ObjectDisposedException(GetType().Name); } StringBuilder commandLine = BuildCommandLine(startInfo.FileName, startInfo.Arguments); NativeMethods.STARTUPINFO startupInfo = new NativeMethods.STARTUPINFO(); NativeMethods.PROCESS_INFORMATION processInfo = new NativeMethods.PROCESS_INFORMATION(); SafeProcessHandle procSH = new SafeProcessHandle(); SafeThreadHandle threadSH = new SafeThreadHandle(); bool retVal; int errorCode = 0; // handles used in parent process SafeFileHandle standardInputWritePipeHandle = null; SafeFileHandle standardOutputReadPipeHandle = null; SafeFileHandle standardErrorReadPipeHandle = null; IntPtr environmentPtr = (IntPtr)0; //GCHandle environmentHandle = new GCHandle(); lock (s_CreateProcessLock) { try { // set up the streams if (startInfo.CreateNoWindow && (startInfo.RedirectStandardInput || startInfo.RedirectStandardOutput || startInfo.RedirectStandardError)) { if (startInfo.StandardOutputEncoding != null && !startInfo.RedirectStandardOutput) { throw new InvalidOperationException("StandardOutputEncodingNotAllowed"); } if (startInfo.StandardErrorEncoding != null && !startInfo.RedirectStandardError) { throw new InvalidOperationException("StandardErrorEncodingNotAllowed"); } if (startInfo.RedirectStandardInput) { CreatePipe(out standardInputWritePipeHandle, out startupInfo.hStdInput, true); } else { startupInfo.hStdInput = new SafeFileHandle(NativeMethods.GetStdHandle(NativeMethods.STD_INPUT_HANDLE), false); } 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); } startupInfo.dwFlags = NativeMethods.STARTF_USESTDHANDLES; } // set up the creation flags paramater int creationFlags = 0; if (startInfo.CreateNoWindow) creationFlags |= NativeMethods.CREATE_NO_WINDOW; // set up the environment block parameterhttps://www.beyondtrust.com/assets/documents/BeyondTrust-Microsoft-Vulnerabilities-Report-2021.pdf //if (startInfo.environmentVariables != null) if (true) { creationFlags |= NativeMethods.CREATE_UNICODE_ENVIRONMENT; //byte[] environmentBytes = EnvironmentBlock.ToByteArray(startInfo.environmentVariables, true); //environmentHandle = GCHandle.Alloc(environmentBytes, GCHandleType.Pinned); //environmentPtr = environmentHandle.AddrOfPinnedObject(); Win32.Process.CreateEnvironmentBlock(out environmentPtr, token, false); } /* if (ExpandEnvironmentVariables && startInfo.Arguments.Contains("%")) { Environment.ExpandEnvironmentVariables() var envVars = new Dictionary(StringComparer.OrdinalIgnoreCase); IntPtr next = environmentPtr; while (Marshal.ReadByte(next) != 0) { var str = Marshal.PtrToStringUni(next); // skip first character because windows allows env vars to begin with equal sign var splitPoint = str.IndexOf('=', 1); var envVarName = str.Substring(0, splitPoint); var envVarVal = str.Substring(splitPoint + 1); envVars.Add(envVarName, envVarVal); next = (IntPtr)((Int64)next + (str.Length * 2) + 2); } return envVars; } */ if (!startInfo.CreateNoWindow) { creationFlags |= (int)Win32.Process.ProcessCreationFlags.CREATE_DEFAULT_ERROR_MODE; creationFlags |= (int)Win32.Process.ProcessCreationFlags.CREATE_NEW_CONSOLE; creationFlags |= (int)Win32.Process.ProcessCreationFlags.CREATE_NEW_PROCESS_GROUP; //startupInfo.lpDesktop = "Winsta0\\Default"; } string workingDirectory = startInfo.WorkingDirectory; if (workingDirectory == string.Empty) workingDirectory = Environment.CurrentDirectory; RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { retVal = false; if (type == CreateType.UserToken) { retVal = NativeMethods.CreateProcessAsUser(token, null, // we don't need this since all the info is in commandLine commandLine, // pointer to the command line string null, // pointer to process security attributes, we don't need to inheriat the handle null, // pointer to thread security attributes true, // handle inheritance flag creationFlags, // creation flags environmentPtr, // pointer to new environment block workingDirectory, // pointer to current directory name startupInfo, // pointer to STARTUPINFO processInfo // pointer to PROCESS_INFORMATION ); } else if (type == CreateType.RawToken) { retVal = NativeMethods.CreateProcessWithToken(token, NativeMethods.LogonFlags.LOGON_WITH_PROFILE, null, // we don't need this since all the info is in commandLine commandLine, // pointer to the command line string creationFlags, // creation flags environmentPtr, // pointer to new environment block workingDirectory, // pointer to current directory name startupInfo, // pointer to STARTUPINFO processInfo // pointer to PROCESS_INFORMATION ); } /* retVal = NativeMethods.CreateProcess(null, // we don't need this since all the info is in commandLine commandLine, // pointer to the command line string null, // pointer to process security attributes, we don't need to inheriat the handle null, // pointer to thread security attributes true, // handle inheritance flag creationFlags, // creation flags environmentPtr, // pointer to new environment block workingDirectory, // pointer to current directory name startupInfo, // pointer to STARTUPINFO processInfo // pointer to PROCESS_INFORMATION ); */ if (!retVal) errorCode = Marshal.GetLastWin32Error(); if (processInfo.hProcess != (IntPtr)0 && processInfo.hProcess != (IntPtr)NativeMethods.INVALID_HANDLE_VALUE) procSH.InitialSetHandle(processInfo.hProcess); if (processInfo.hThread != (IntPtr)0 && processInfo.hThread != (IntPtr)NativeMethods.INVALID_HANDLE_VALUE) threadSH.InitialSetHandle(processInfo.hThread); } if (!retVal) { if (errorCode == NativeMethods.ERROR_BAD_EXE_FORMAT || errorCode == NativeMethods.ERROR_EXE_MACHINE_TYPE_MISMATCH) { throw new Win32Exception(errorCode, "InvalidApplication"); } throw new Win32Exception(errorCode); } } finally { // free environment block //if (environmentHandle.IsAllocated) //{ // environmentHandle.Free(); //} Win32.Process.DestroyEnvironmentBlock(environmentPtr); startupInfo.Dispose(); } } if (startInfo.RedirectStandardInput) { standardInput = new StreamWriter(new FileStream(standardInputWritePipeHandle, FileAccess.Write, 4096, false), Console.InputEncoding, 4096); standardInput.AutoFlush = true; } if (startInfo.RedirectStandardOutput) { Encoding enc = (startInfo.StandardOutputEncoding != null) ? startInfo.StandardOutputEncoding : Console.OutputEncoding; standardOutput = new StreamReader(new FileStream(standardOutputReadPipeHandle, FileAccess.Read, 4096, false), enc, true, 4096); } if (startInfo.RedirectStandardError) { Encoding enc = (startInfo.StandardErrorEncoding != null) ? startInfo.StandardErrorEncoding : Console.OutputEncoding; standardError = new StreamReader(new FileStream(standardErrorReadPipeHandle, FileAccess.Read, 4096, false), enc, true, 4096); } bool ret = false; if (!procSH.IsInvalid) { SetProcessHandle(procSH); SetProcessId(processInfo.dwProcessId); threadSH.Close(); ret = true; } return ret; } /* private static string ExpandEnvironmentVariables(string name, Dictionary environment) { switch (name) { case null: case "": return name; default: environment = new Dictionary(StringComparer.OrdinalIgnoreCase) { {"SuSSY", "gussys"}, {"AMoGus", "gussys"}, {"Sussex", "gussys"} }; StringBuilder result = new StringBuilder(); int index = name.IndexOf('%'); if (index > 0) result.Append(name.Substring(index)); int lastValidIndex = -1; while (index != -1) { lastValidIndex = index; var end = name.IndexOf('%', index + 1); if (end == -1) { result.Append('%'); break; } // Double % escape if (end == index + 1) { index = name.IndexOf('%', index + 2); result.Append(index == -1 ? "%" : "%" + name.Substring(end + 1, index - (end + 1))); continue; } lastValidIndex = end; if (environment.TryGetValue(name.Substring(index + 1, end - (index + 1)), out string varValue)) result.Append(varValue); index = name.IndexOf('%', end + 1); if (index != -1) { result.Append(name.Substring(end + 1, index - (end + 1))); } } result.Append(lastValidIndex != -1 ? name.Substring(index + 1, name.Length - (index + 1)) : name); return result.ToString(); } } */ [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private bool StartWithShellExecuteEx(ProcessStartInfo startInfo) { //Cannot start a new process and store its handle if the object has been disposed, since finalization has been suppressed. if (this.disposed) throw new ObjectDisposedException(GetType().Name); if (!String.IsNullOrEmpty(startInfo.UserName) || (startInfo.Password != null)) { throw new InvalidOperationException("CantStartAsUser"); } if (startInfo.RedirectStandardInput || startInfo.RedirectStandardOutput || startInfo.RedirectStandardError) { throw new InvalidOperationException("CantRedirectStreams"); } if (startInfo.StandardErrorEncoding != null) { throw new InvalidOperationException("StandardErrorEncodingNotAllowed"); } if (startInfo.StandardOutputEncoding != null) { throw new InvalidOperationException("StandardOutputEncodingNotAllowed"); } // can't set env vars with ShellExecuteEx... if (startInfo.environmentVariables != null) { throw new InvalidOperationException("CantUseEnvVars"); } NativeMethods.ShellExecuteInfo shellExecuteInfo = new NativeMethods.ShellExecuteInfo(); shellExecuteInfo.fMask = NativeMethods.SEE_MASK_NOCLOSEPROCESS; if (startInfo.ErrorDialog) { shellExecuteInfo.hwnd = startInfo.ErrorDialogParentHandle; } else { shellExecuteInfo.fMask |= NativeMethods.SEE_MASK_FLAG_NO_UI; } switch (startInfo.WindowStyle) { case ProcessWindowStyle.Hidden: shellExecuteInfo.nShow = NativeMethods.SW_HIDE; break; case ProcessWindowStyle.Minimized: shellExecuteInfo.nShow = NativeMethods.SW_SHOWMINIMIZED; break; case ProcessWindowStyle.Maximized: shellExecuteInfo.nShow = NativeMethods.SW_SHOWMAXIMIZED; break; default: shellExecuteInfo.nShow = NativeMethods.SW_SHOWNORMAL; break; } try { if (startInfo.FileName.Length != 0) shellExecuteInfo.lpFile = Marshal.StringToHGlobalAuto(startInfo.FileName); if (startInfo.Verb.Length != 0) shellExecuteInfo.lpVerb = Marshal.StringToHGlobalAuto(startInfo.Verb); if (startInfo.Arguments.Length != 0) shellExecuteInfo.lpParameters = Marshal.StringToHGlobalAuto(startInfo.Arguments); if (startInfo.WorkingDirectory.Length != 0) shellExecuteInfo.lpDirectory = Marshal.StringToHGlobalAuto(startInfo.WorkingDirectory); shellExecuteInfo.fMask |= NativeMethods.SEE_MASK_FLAG_DDEWAIT; ShellExecuteHelper executeHelper = new ShellExecuteHelper(shellExecuteInfo); if (!executeHelper.ShellExecuteOnSTAThread()) { int error = executeHelper.ErrorCode; if (error == 0) { switch ((long)shellExecuteInfo.hInstApp) { case NativeMethods.SE_ERR_FNF: error = NativeMethods.ERROR_FILE_NOT_FOUND; break; case NativeMethods.SE_ERR_PNF: error = NativeMethods.ERROR_PATH_NOT_FOUND; break; case NativeMethods.SE_ERR_ACCESSDENIED: error = NativeMethods.ERROR_ACCESS_DENIED; break; case NativeMethods.SE_ERR_OOM: error = NativeMethods.ERROR_NOT_ENOUGH_MEMORY; break; case NativeMethods.SE_ERR_DDEFAIL: case NativeMethods.SE_ERR_DDEBUSY: case NativeMethods.SE_ERR_DDETIMEOUT: error = NativeMethods.ERROR_DDE_FAIL; break; case NativeMethods.SE_ERR_SHARE: error = NativeMethods.ERROR_SHARING_VIOLATION; break; case NativeMethods.SE_ERR_NOASSOC: error = NativeMethods.ERROR_NO_ASSOCIATION; break; case NativeMethods.SE_ERR_DLLNOTFOUND: error = NativeMethods.ERROR_DLL_NOT_FOUND; break; default: error = (int)shellExecuteInfo.hInstApp; break; } } if (error == NativeMethods.ERROR_BAD_EXE_FORMAT || error == NativeMethods.ERROR_EXE_MACHINE_TYPE_MISMATCH) { throw new Win32Exception(error, "InvalidApplication"); } throw new Win32Exception(error); } } finally { if (shellExecuteInfo.lpFile != (IntPtr)0) Marshal.FreeHGlobal(shellExecuteInfo.lpFile); if (shellExecuteInfo.lpVerb != (IntPtr)0) Marshal.FreeHGlobal(shellExecuteInfo.lpVerb); if (shellExecuteInfo.lpParameters != (IntPtr)0) Marshal.FreeHGlobal(shellExecuteInfo.lpParameters); if (shellExecuteInfo.lpDirectory != (IntPtr)0) Marshal.FreeHGlobal(shellExecuteInfo.lpDirectory); } if (shellExecuteInfo.hProcess != (IntPtr)0) { SafeProcessHandle handle = new SafeProcessHandle(shellExecuteInfo.hProcess); SetProcessHandle(handle); return true; } return false; } /// /// /// Starts a process resource specified by the process start /// information passed in, for example the file name of the process to start. /// Associates the process resource with a new /// component. /// /// [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static Process Start(CreateType type, ProcessStartInfo startInfo, Win32.TokensEx.SafeTokenHandle token) { Process process = new Process(); if (startInfo == null) throw new ArgumentNullException("startInfo"); process.StartInfo = startInfo; if (process.Start(type, ref token)) { return process; } return null; } /// /// /// Stops the /// associated process immediately. /// /// [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public void Kill() { SafeProcessHandle handle = null; try { handle = GetProcessHandle(NativeMethods.PROCESS_TERMINATE); if (!NativeMethods.TerminateProcess(handle, -1)) throw new Win32Exception(); } finally { ReleaseProcessHandle(handle); } } /// /// Make sure we are not watching for process exit. /// /// void StopWatchingForExit() { if (watchingForExit) { lock (this) { if (watchingForExit) { watchingForExit = false; registeredWaitHandle.Unregister(null); waitHandle.Close(); waitHandle = null; registeredWaitHandle = null; } } } } /// /// /// Instructs the component to wait the specified number of milliseconds for the associated process to exit. /// /// public bool WaitForExit(int milliseconds) { SafeProcessHandle handle = null; bool exited; ProcessWaitHandle processWaitHandle = null; try { handle = GetProcessHandle(NativeMethods.SYNCHRONIZE, false); if (handle.IsInvalid) { exited = true; } else { processWaitHandle = new ProcessWaitHandle(handle); if (processWaitHandle.WaitOne(milliseconds, false)) { exited = true; signaled = true; } else { exited = false; signaled = false; } } } finally { if (processWaitHandle != null) { processWaitHandle.Close(); } // If we have a hard timeout, we cannot wait for the streams if (output != null && milliseconds == -1) { output.WaitUtilEOF(); } if (error != null && milliseconds == -1) { error.WaitUtilEOF(); } ReleaseProcessHandle(handle); } if (exited && watchForExit) { RaiseOnExited(); } return exited; } /// /// /// Instructs the component to wait /// indefinitely for the associated process to exit. /// /// public void WaitForExit() { WaitForExit(-1); } /// /// /// Causes the component to wait the /// specified number of milliseconds for the associated process to enter an /// idle state. /// This is only applicable for processes with a user interface, /// therefore a message loop. /// /// public bool WaitForInputIdle(int milliseconds) { SafeProcessHandle handle = null; bool idle; try { handle = GetProcessHandle(NativeMethods.SYNCHRONIZE | NativeMethods.PROCESS_QUERY_INFORMATION); int ret = NativeMethods.WaitForInputIdle(handle, milliseconds); switch (ret) { case NativeMethods.WAIT_OBJECT_0: idle = true; break; case NativeMethods.WAIT_TIMEOUT: idle = false; break; case NativeMethods.WAIT_FAILED: default: throw new InvalidOperationException("InputIdleUnkownError"); } } finally { ReleaseProcessHandle(handle); } return idle; } /// /// /// Instructs the component to wait /// indefinitely for the associated process to enter an idle state. This /// is only applicable for processes with a user interface, therefore a message loop. /// /// public bool WaitForInputIdle() { return WaitForInputIdle(Int32.MaxValue); } // Support for working asynchronously with streams /// /// /// Instructs the component to start /// reading the StandardOutput stream asynchronously. The user can register a callback /// that will be called when a line of data terminated by \n,\r or \r\n is reached, or the end of stream is reached /// then the remaining information is returned. The user can add an event handler to OutputDataReceived. /// /// [System.Runtime.InteropServices.ComVisible(false)] public void BeginOutputReadLine() { if (outputStreamReadMode == StreamReadMode.undefined) { outputStreamReadMode = StreamReadMode.asyncMode; } else if (outputStreamReadMode != StreamReadMode.asyncMode) { throw new InvalidOperationException("CantMixSyncAsyncOperation"); } if (pendingOutputRead) throw new InvalidOperationException("PendingAsyncOperation"); pendingOutputRead = true; // We can't detect if there's a pending sychronous read, tream also doesn't. if (output == null) { if (standardOutput == null) { throw new InvalidOperationException("CantGetStandardOut"); } Stream s = standardOutput.BaseStream; output = new AsyncStreamReader(this, s, new UserCallBack(this.OutputReadNotifyUser), standardOutput.CurrentEncoding); } output.BeginReadLine(); } /// /// /// Instructs the component to start /// reading the StandardError stream asynchronously. The user can register a callback /// that will be called when a line of data terminated by \n,\r or \r\n is reached, or the end of stream is reached /// then the remaining information is returned. The user can add an event handler to ErrorDataReceived. /// /// [System.Runtime.InteropServices.ComVisible(false)] public void BeginErrorReadLine() { if (errorStreamReadMode == StreamReadMode.undefined) { errorStreamReadMode = StreamReadMode.asyncMode; } else if (errorStreamReadMode != StreamReadMode.asyncMode) { throw new InvalidOperationException("CantMixSyncAsyncOperation"); } if (pendingErrorRead) { throw new InvalidOperationException("PendingAsyncOperation"); } pendingErrorRead = true; // We can't detect if there's a pending sychronous read, stream also doesn't. if (error == null) { if (standardError == null) { throw new InvalidOperationException("CantGetStandardError"); } Stream s = standardError.BaseStream; error = new AsyncStreamReader(this, s, new UserCallBack(this.ErrorReadNotifyUser), standardError.CurrentEncoding); } error.BeginReadLine(); } /// /// /// Instructs the component to cancel the asynchronous operation /// specified by BeginOutputReadLine(). /// /// [System.Runtime.InteropServices.ComVisible(false)] public void CancelOutputRead() { if (output != null) { output.CancelOperation(); } else { throw new InvalidOperationException("NoAsyncOperation"); } pendingOutputRead = false; } /// /// /// Instructs the component to cancel the asynchronous operation /// specified by BeginErrorReadLine(). /// /// [System.Runtime.InteropServices.ComVisible(false)] public void CancelErrorRead() { if (error != null) { error.CancelOperation(); } else { throw new InvalidOperationException("No async operation."); } pendingErrorRead = false; } internal void OutputReadNotifyUser(String data) { // To avoid ---- between remove handler and raising the event DataReceivedEventHandler outputDataReceived = OutputDataReceived; if (outputDataReceived != null) { DataReceivedEventArgs e = new DataReceivedEventArgs(data); if (SynchronizingObject != null && SynchronizingObject.InvokeRequired) { SynchronizingObject.Invoke(outputDataReceived, new object[] { this, e }); } else { outputDataReceived(this, e); // Call back to user informing data is available. } } } internal void ErrorReadNotifyUser(String data) { // To avoid ---- between remove handler and raising the event DataReceivedEventHandler errorDataReceived = ErrorDataReceived; if (errorDataReceived != null) { DataReceivedEventArgs e = new DataReceivedEventArgs(data); if (SynchronizingObject != null && SynchronizingObject.InvokeRequired) { SynchronizingObject.Invoke(errorDataReceived, new object[] { this, e }); } else { errorDataReceived(this, e); // Call back to user informing data is available. } } } /// /// A desired internal state. /// /// enum State { HaveId = 0x1, IsLocal = 0x2, IsNt = 0x4, HaveProcessInfo = 0x8, Exited = 0x10, Associated = 0x20, IsWin2k = 0x40, HaveNtProcessInfo = HaveProcessInfo | IsNt } } /// /// This data structure contains information about a process that is collected /// in bulk by querying the operating system. The reason to make this a separate /// structure from the process component is so that we can throw it away all at once /// when Refresh is called on the component. /// /// internal class ProcessInfo { public ArrayList threadInfoList = new ArrayList(); public int basePriority; public string processName; public int processId; public int handleCount; public long poolPagedBytes; public long poolNonpagedBytes; public long virtualBytes; public long virtualBytesPeak; public long workingSetPeak; public long workingSet; public long pageFileBytesPeak; public long pageFileBytes; public long privateBytes; public int mainModuleId; // used only for win9x - id is only for use with CreateToolHelp32 public int sessionId; } /// /// This data structure contains information about a thread in a process that /// is collected in bulk by querying the operating system. The reason to /// make this a separate structure from the ProcessThread component is so that we /// can throw it away all at once when Refresh is called on the component. /// /// internal class ThreadInfo { public int threadId; public int processId; public int basePriority; public int currentPriority; public IntPtr startAddress; public System.Diagnostics.ThreadState threadState; public ThreadWaitReason threadWaitReason; } /// /// This data structure contains information about a module in a process that /// is collected in bulk by querying the operating system. The reason to /// make this a separate structure from the ProcessModule component is so that we /// can throw it away all at once when Refresh is called on the component. /// /// internal class ModuleInfo { public string baseName; public string fileName; public IntPtr baseOfDll; public IntPtr entryPoint; public int sizeOfImage; public int Id; // used only on win9x - for matching up with ProcessInfo.mainModuleId } internal static class EnvironmentBlock { public static byte[] ToByteArray(StringDictionary sd, bool unicode) { // get the keys string[] keys = new string[sd.Count]; byte[] envBlock = null; sd.Keys.CopyTo(keys, 0); // get the values string[] values = new string[sd.Count]; sd.Values.CopyTo(values, 0); // sort both by the keys // Windows 2000 requires the environment block to be sorted by the key // It will first converting the case the strings and do ordinal comparison. Array.Sort(keys, values, OrdinalCaseInsensitiveComparer.Default); // create a list of null terminated "key=val" strings StringBuilder stringBuff = new StringBuilder(); for (int i = 0; i < sd.Count; ++i) { stringBuff.Append(keys[i]); stringBuff.Append('='); stringBuff.Append(values[i]); stringBuff.Append('\0'); } // an extra null at the end indicates end of list. stringBuff.Append('\0'); if (unicode) { envBlock = Encoding.Unicode.GetBytes(stringBuff.ToString()); } else { envBlock = Encoding.Default.GetBytes(stringBuff.ToString()); if (envBlock.Length > UInt16.MaxValue) throw new InvalidOperationException("Environment block is too long."); } return envBlock; } } internal class OrdinalCaseInsensitiveComparer : IComparer { internal static readonly OrdinalCaseInsensitiveComparer Default = new OrdinalCaseInsensitiveComparer(); public int Compare(Object a, Object b) { String sa = a as String; String sb = b as String; if (sa != null && sb != null) { return String.Compare(sa, sb, StringComparison.OrdinalIgnoreCase); } return Comparer.Default.Compare(a, b); } } internal class ShellExecuteHelper { private NativeMethods.ShellExecuteInfo _executeInfo; private int _errorCode; private bool _succeeded; public ShellExecuteHelper(NativeMethods.ShellExecuteInfo executeInfo) { _executeInfo = executeInfo; } [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] public void ShellExecuteFunction() { if (!(_succeeded = NativeMethods.ShellExecuteEx(_executeInfo))) { _errorCode = Marshal.GetLastWin32Error(); } } public bool ShellExecuteOnSTAThread() { // // SHELL API ShellExecute() requires STA in order to work correctly. // If current thread is not a STA thread, we need to call ShellExecute on a new thread. // if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) { ThreadStart threadStart = new ThreadStart(this.ShellExecuteFunction); Thread executionThread = new Thread(threadStart); executionThread.SetApartmentState(ApartmentState.STA); executionThread.Start(); executionThread.Join(); } else { ShellExecuteFunction(); } return _succeeded; } public int ErrorCode { get { return _errorCode; } } } private static long[] CachedBuffer; private static int GetNewBufferSize(int existingBufferSize, int requiredSize) { if (requiredSize == 0) { int num = existingBufferSize * 2; return num >= existingBufferSize ? num : throw new OutOfMemoryException(); } int num1 = requiredSize + 10240; return num1 >= requiredSize ? num1 : throw new OutOfMemoryException(); } internal static ProcessInfo GetProcessInfo(int processId, string machineName) { ProcessInfo[] processInfos = GetProcessInfos((Predicate)(pid => pid == processId)); if (processInfos.Length == 1) return processInfos[0]; return (ProcessInfo)null; } internal static ProcessInfo[] GetProcessInfos(Predicate processIdFilter = null) { int returnedSize = 0; GCHandle gcHandle = new GCHandle(); int num = 131072; long[] numArray = Interlocked.Exchange(ref CachedBuffer, (long[])null); try { int error; do { if (numArray == null) numArray = new long[(num + 7) / 8]; else num = numArray.Length * 8; gcHandle = GCHandle.Alloc((object)numArray, GCHandleType.Pinned); error = NativeMethods.NtQuerySystemInformation(5, gcHandle.AddrOfPinnedObject(), num, out returnedSize); if (error == -1073741820) { if (gcHandle.IsAllocated) gcHandle.Free(); numArray = (long[])null; num = GetNewBufferSize(num, returnedSize); } } while (error == -1073741820); if (error < 0) throw new InvalidOperationException("CouldntGetProcessInfos", (Exception)new Win32Exception(error)); return GetProcessInfos(gcHandle.AddrOfPinnedObject(), processIdFilter); } finally { Interlocked.Exchange(ref CachedBuffer, numArray); if (gcHandle.IsAllocated) gcHandle.Free(); } } private static ProcessInfo[] GetProcessInfos(IntPtr dataPtr, Predicate processIdFilter) { Hashtable hashtable = new Hashtable(60); long num = 0; while (true) { IntPtr ptr1 = (IntPtr)((long)dataPtr + num); NativeMethods.SystemProcessInformation structure1 = new NativeMethods.SystemProcessInformation(); Marshal.PtrToStructure(ptr1, (object)structure1); int int32 = structure1.UniqueProcessId.ToInt32(); if (processIdFilter == null || processIdFilter(int32)) { ProcessInfo processInfo = new ProcessInfo(); processInfo.processId = int32; processInfo.handleCount = (int)structure1.HandleCount; processInfo.sessionId = (int)structure1.SessionId; processInfo.poolPagedBytes = (long)(ulong)structure1.QuotaPagedPoolUsage; processInfo.poolNonpagedBytes = (long)(ulong)structure1.QuotaNonPagedPoolUsage; processInfo.virtualBytes = (long)(ulong)structure1.VirtualSize; processInfo.virtualBytesPeak = (long)(ulong)structure1.PeakVirtualSize; processInfo.workingSetPeak = (long)(ulong)structure1.PeakWorkingSetSize; processInfo.workingSet = (long)(ulong)structure1.WorkingSetSize; processInfo.pageFileBytesPeak = (long)(ulong)structure1.PeakPagefileUsage; processInfo.pageFileBytes = (long)(ulong)structure1.PagefileUsage; processInfo.privateBytes = (long)(ulong)structure1.PrivatePageCount; processInfo.basePriority = structure1.BasePriority; if (structure1.NamePtr == IntPtr.Zero) { processInfo.processName = processInfo.processId != 4 ? (processInfo.processId != 0 ? processInfo.processId.ToString((IFormatProvider)CultureInfo.InvariantCulture) : "Idle") : "System"; } else { string str = GetProcessShortName(Marshal.PtrToStringUni(structure1.NamePtr, (int)structure1.NameLength / 2)); processInfo.processName = str; } hashtable[(object)processInfo.processId] = (object)processInfo; IntPtr ptr2 = (IntPtr)((long)ptr1 + (long)Marshal.SizeOf((object)structure1)); for (int index = 0; (long)index < (long)structure1.NumberOfThreads; ++index) { NativeMethods.SystemThreadInformation structure2 = new NativeMethods.SystemThreadInformation(); Marshal.PtrToStructure(ptr2, (object)structure2); processInfo.threadInfoList.Add((object)new ThreadInfo() { processId = (int)structure2.UniqueProcess, threadId = (int)structure2.UniqueThread, basePriority = structure2.BasePriority, currentPriority = structure2.Priority, startAddress = structure2.StartAddress, threadState = (System.Diagnostics.ThreadState)structure2.ThreadState, threadWaitReason = GetThreadWaitReason((int)structure2.WaitReason) }); ptr2 = (IntPtr)((long)ptr2 + (long)Marshal.SizeOf((object)structure2)); } } if (structure1.NextEntryOffset != 0U) num += (long)structure1.NextEntryOffset; else break; } ProcessInfo[] processInfos = new ProcessInfo[hashtable.Values.Count]; hashtable.Values.CopyTo((Array)processInfos, 0); return processInfos; } internal static ThreadWaitReason GetThreadWaitReason(int value) { switch (value) { case 0: case 7: return ThreadWaitReason.Executive; case 1: case 8: return ThreadWaitReason.FreePage; case 2: case 9: return ThreadWaitReason.PageIn; case 3: case 10: return ThreadWaitReason.SystemAllocation; case 4: case 11: return ThreadWaitReason.ExecutionDelay; case 5: case 12: return ThreadWaitReason.Suspended; case 6: case 13: return ThreadWaitReason.UserRequest; case 14: return ThreadWaitReason.EventPairHigh; case 15: return ThreadWaitReason.EventPairLow; case 16: return ThreadWaitReason.LpcReceive; case 17: return ThreadWaitReason.LpcReply; case 18: return ThreadWaitReason.VirtualMemory; case 19: return ThreadWaitReason.PageOut; default: return ThreadWaitReason.Unknown; } } internal static string GetProcessShortName(string name) { if (string.IsNullOrEmpty(name)) return string.Empty; int num1 = -1; int startIndex1 = -1; for (int index = 0; index < name.Length; ++index) { if (name[index] == '\\') num1 = index; else if (name[index] == '.') startIndex1 = index; } int num2 = startIndex1 != -1 ? (!string.Equals(".exe", name.Substring(startIndex1), StringComparison.OrdinalIgnoreCase) ? name.Length - 1 : startIndex1 - 1) : name.Length - 1; int startIndex2 = num1 != -1 ? num1 + 1 : 0; return name.Substring(startIndex2, num2 - startIndex2 + 1); } [HostProtection(MayLeakOnAbort = true)] internal static class NativeMethods { public static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); public const int STARTF_USESTDHANDLES = 0x00000100; public const int STD_INPUT_HANDLE = -10; public const int STD_OUTPUT_HANDLE = -11; public const int STD_ERROR_HANDLE = -12; public const int STILL_ACTIVE = 0x00000103; public const int SW_HIDE = 0; public const int WAIT_OBJECT_0 = 0x00000000; public const int WAIT_FAILED = unchecked((int)0xFFFFFFFF); public const int WAIT_TIMEOUT = 0x00000102; public const int WAIT_ABANDONED = 0x00000080; public const int ERROR_BAD_EXE_FORMAT = 193; public const int ERROR_EXE_MACHINE_TYPE_MISMATCH = 216; [DllImport("ntdll.dll", CharSet = CharSet.Auto)] public static extern int NtQuerySystemInformation( int query, IntPtr dataPtr, int size, out int returnedSize); [StructLayout(LayoutKind.Sequential)] internal class SystemProcessInformation { internal uint NextEntryOffset; internal uint NumberOfThreads; private long SpareLi1; private long SpareLi2; private long SpareLi3; private long CreateTime; private long UserTime; private long KernelTime; internal ushort NameLength; internal ushort MaximumNameLength; internal IntPtr NamePtr; internal int BasePriority; internal IntPtr UniqueProcessId; internal IntPtr InheritedFromUniqueProcessId; internal uint HandleCount; internal uint SessionId; internal UIntPtr PageDirectoryBase; internal UIntPtr PeakVirtualSize; internal UIntPtr VirtualSize; internal uint PageFaultCount; internal UIntPtr PeakWorkingSetSize; internal UIntPtr WorkingSetSize; internal UIntPtr QuotaPeakPagedPoolUsage; internal UIntPtr QuotaPagedPoolUsage; internal UIntPtr QuotaPeakNonPagedPoolUsage; internal UIntPtr QuotaNonPagedPoolUsage; internal UIntPtr PagefileUsage; internal UIntPtr PeakPagefileUsage; internal UIntPtr PrivatePageCount; private long ReadOperationCount; private long WriteOperationCount; private long OtherOperationCount; private long ReadTransferCount; private long WriteTransferCount; private long OtherTransferCount; } [StructLayout(LayoutKind.Sequential)] internal class SystemThreadInformation { private long KernelTime; private long UserTime; private long CreateTime; private uint WaitTime; internal IntPtr StartAddress; internal IntPtr UniqueProcess; internal IntPtr UniqueThread; internal int Priority; internal int BasePriority; internal uint ContextSwitches; internal uint ThreadState; internal uint WaitReason; } [StructLayout(LayoutKind.Sequential)] internal class STARTUPINFO { public int cb; public IntPtr lpReserved = IntPtr.Zero; public string lpDesktop = null; public IntPtr lpTitle = IntPtr.Zero; public int dwX = 0; public int dwY = 0; public int dwXSize = 0; public int dwYSize = 0; public int dwXCountChars = 0; public int dwYCountChars = 0; public int dwFillAttribute = 0; public int dwFlags; public short wShowWindow = 0; public short cbReserved2 = 0; public IntPtr lpReserved2 = IntPtr.Zero; public SafeFileHandle hStdInput = new SafeFileHandle(IntPtr.Zero, false); public SafeFileHandle hStdOutput = new SafeFileHandle(IntPtr.Zero, false); public SafeFileHandle hStdError = new SafeFileHandle(IntPtr.Zero, false); public STARTUPINFO() { cb = Marshal.SizeOf(this); } public void Dispose() { // close the handles created for child process if (hStdInput != null && !hStdInput.IsInvalid) { hStdInput.Close(); hStdInput = null; } if (hStdOutput != null && !hStdOutput.IsInvalid) { hStdOutput.Close(); hStdOutput = null; } if (hStdError != null && !hStdError.IsInvalid) { hStdError.Close(); hStdError = null; } } } [StructLayout(LayoutKind.Sequential)] internal class SECURITY_ATTRIBUTES { public int nLength = 12; public SafeLocalMemHandle lpSecurityDescriptor = new SafeLocalMemHandle(IntPtr.Zero, false); public bool bInheritHandle; } [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] [ResourceExposure(ResourceScope.None)] public static extern bool GetExitCodeProcess(SafeProcessHandle processHandle, out int exitCode); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] [ResourceExposure(ResourceScope.None)] public static extern bool GetProcessTimes(SafeProcessHandle handle, out long creation, out long exit, out long kernel, out long user); [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] [ResourceExposure(ResourceScope.Process)] public static extern IntPtr GetStdHandle(int whichHandle); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] [ResourceExposure(ResourceScope.Process)] public static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, SECURITY_ATTRIBUTES lpPipeAttributes, int nSize); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)] [ResourceExposure(ResourceScope.Process)] public static extern bool CreateProcess([MarshalAs(UnmanagedType.LPTStr)] string lpApplicationName, // LPCTSTR StringBuilder lpCommandLine, // LPTSTR - note: CreateProcess might insert a null somewhere in this string SECURITY_ATTRIBUTES lpProcessAttributes, // LPSECURITY_ATTRIBUTES SECURITY_ATTRIBUTES lpThreadAttributes, // LPSECURITY_ATTRIBUTES bool bInheritHandles, // BOOL int dwCreationFlags, // DWORD IntPtr lpEnvironment, // LPVOID [MarshalAs(UnmanagedType.LPTStr)] string lpCurrentDirectory, // LPCTSTR STARTUPINFO lpStartupInfo, // LPSTARTUPINFO PROCESS_INFORMATION lpProcessInformation // LPPROCESS_INFORMATION ); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] [ResourceExposure(ResourceScope.Machine)] public static extern bool TerminateProcess(SafeProcessHandle processHandle, int exitCode); [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] [ResourceExposure(ResourceScope.Process)] public static extern IntPtr GetCurrentProcess(); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)] [SuppressUnmanagedCodeSecurityAttribute] [ResourceExposure(ResourceScope.Machine)] public static extern bool CreateProcessAsUser(Win32.TokensEx.SafeTokenHandle hToken, string lpApplicationName, StringBuilder lpCommandLine, SECURITY_ATTRIBUTES lpProcessAttributes, SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, STARTUPINFO lpStartupInfo, PROCESS_INFORMATION lpProcessInformation); [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Auto)] internal static extern bool CreateProcessWithToken( Win32.TokensEx.SafeTokenHandle hToken, LogonFlags dwLogonFlags, string lpApplicationName, StringBuilder lpCommandLine, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, STARTUPINFO lpStartupInfo, PROCESS_INFORMATION lpProcessInformation); [DllImport("advapi32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true, BestFitMapping = false)] [ResourceExposure(ResourceScope.Machine)] internal static extern bool CreateProcessWithLogonW(string userName, string domain, IntPtr password, LogonFlags logonFlags, [MarshalAs(UnmanagedType.LPTStr)] string appName, StringBuilder cmdLine, int creationFlags, IntPtr environmentBlock, [MarshalAs(UnmanagedType.LPTStr)] string lpCurrentDirectory, // LPCTSTR STARTUPINFO lpStartupInfo, PROCESS_INFORMATION lpProcessInformation); //TODO: TOKEN [StructLayout(LayoutKind.Sequential)] internal class PROCESS_INFORMATION { public IntPtr hProcess = IntPtr.Zero; public IntPtr hThread = IntPtr.Zero; public int dwProcessId = 0; public int dwThreadId = 0; } [Flags] internal enum LogonFlags { LOGON_WITH_PROFILE = 0x00000001, LOGON_NETCREDENTIALS_ONLY = 0x00000002 } public const int QS_KEY = 0x0001, QS_MOUSEMOVE = 0x0002, QS_MOUSEBUTTON = 0x0004, QS_POSTMESSAGE = 0x0008, QS_TIMER = 0x0010, QS_PAINT = 0x0020, QS_SENDMESSAGE = 0x0040, QS_HOTKEY = 0x0080, QS_ALLPOSTMESSAGE = 0x0100, QS_MOUSE = QS_MOUSEMOVE | QS_MOUSEBUTTON, QS_INPUT = QS_MOUSE | QS_KEY, QS_ALLEVENTS = QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY, QS_ALLINPUT = QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE; [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [ResourceExposure(ResourceScope.None)] public static extern int WaitForInputIdle(SafeProcessHandle handle, int milliseconds); [DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true)] [ResourceExposure(ResourceScope.Machine)] public static extern bool ShellExecuteEx(ShellExecuteInfo info); [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true, BestFitMapping = false)] [ResourceExposure(ResourceScope.Machine)] public static extern bool DuplicateHandle(HandleRef hSourceProcessHandle, SafeHandle hSourceHandle, HandleRef hTargetProcess, out SafeFileHandle targetHandle, int dwDesiredAccess, bool bInheritHandle, int dwOptions); [DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true, BestFitMapping = false)] [ResourceExposure(ResourceScope.Machine)] public static extern bool DuplicateHandle(HandleRef hSourceProcessHandle, SafeHandle hSourceHandle, HandleRef hTargetProcess, out SafeWaitHandle targetHandle, int dwDesiredAccess, bool bInheritHandle, int dwOptions); [DllImport("user32.dll", CharSet = CharSet.Auto, BestFitMapping = true)] [ResourceExposure(ResourceScope.None)] public static extern int GetWindowText(HandleRef hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll", CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern int GetWindowTextLength(HandleRef hWnd); [DllImport("user32.dll", CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern IntPtr SendMessageTimeout(HandleRef hWnd, int msg, IntPtr wParam, IntPtr lParam, int flags, int timeout, out IntPtr pdwResult); [DllImport("user32.dll", CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern int GetWindowLong(HandleRef hWnd, int nIndex); [DllImport("user32.dll", CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern int PostMessage(HandleRef hwnd, int msg, IntPtr wparam, IntPtr lparam); [StructLayout(LayoutKind.Sequential)] internal class ShellExecuteInfo { public int cbSize; public int fMask; public IntPtr hwnd = (IntPtr)0; public IntPtr lpVerb = (IntPtr)0; public IntPtr lpFile = (IntPtr)0; public IntPtr lpParameters = (IntPtr)0; public IntPtr lpDirectory = (IntPtr)0; public int nShow; public IntPtr hInstApp = (IntPtr)0; public IntPtr lpIDList = (IntPtr)0; public IntPtr lpClass = (IntPtr)0; public IntPtr hkeyClass = (IntPtr)0; public int dwHotKey = 0; public IntPtr hIcon = (IntPtr)0; public IntPtr hProcess = (IntPtr)0; [ResourceExposure(ResourceScope.Machine)] public ShellExecuteInfo() { cbSize = Marshal.SizeOf(this); } } [StructLayout(LayoutKind.Sequential)] internal struct LUID { public int LowPart; public int HighPart; } public const int SEE_MASK_NOCLOSEPROCESS = 0x00000040; public const int SEE_MASK_CONNECTNETDRV = 0x00000080; public const int SEE_MASK_FLAG_DDEWAIT = 0x00000100; public const int SEE_MASK_DOENVSUBST = 0x00000200; public const int SEE_MASK_FLAG_NO_UI = 0x00000400; public const int PROCESS_TERMINATE = 0x0001; public const int PROCESS_QUERY_INFORMATION = 0x0400; public const int PROCESS_QUERY_LIMITED_INFORMATION = 0x1000; public const int STANDARD_RIGHTS_REQUIRED = 0x000F0000; public const int SYNCHRONIZE = 0x00100000; public const int PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF; public const int READ_CONTROL = 0x00020000; public const int STANDARD_RIGHTS_READ = READ_CONTROL; public const int KEY_QUERY_VALUE = 0x0001; public const int KEY_ENUMERATE_SUB_KEYS = 0x0008; public const int KEY_NOTIFY = 0x0010; public const int ERROR_BROKEN_PIPE = 109; public const int ERROR_NO_DATA = 232; public const int ERROR_HANDLE_EOF = 38; public const int ERROR_IO_INCOMPLETE = 996; public const int ERROR_IO_PENDING = 997; public const int ERROR_FILE_EXISTS = 0x50; public const int ERROR_FILENAME_EXCED_RANGE = 0xCE; // filename too long. public const int ERROR_MORE_DATA = 234; public const int ERROR_CANCELLED = 1223; public const int ERROR_FILE_NOT_FOUND = 2; public const int ERROR_PATH_NOT_FOUND = 3; public const int ERROR_ACCESS_DENIED = 5; public const int ERROR_INVALID_HANDLE = 6; public const int ERROR_NOT_ENOUGH_MEMORY = 8; public const int ERROR_BAD_COMMAND = 22; public const int ERROR_SHARING_VIOLATION = 32; public const int ERROR_OPERATION_ABORTED = 995; public const int ERROR_NO_ASSOCIATION = 1155; public const int ERROR_DLL_NOT_FOUND = 1157; public const int ERROR_DDE_FAIL = 1156; public const int ERROR_INVALID_PARAMETER = 87; public const int ERROR_PARTIAL_COPY = 299; public const int ERROR_SUCCESS = 0; public const int ERROR_ALREADY_EXISTS = 183; public const int ERROR_COUNTER_TIMEOUT = 1121; public const int DUPLICATE_CLOSE_SOURCE = 1; public const int DUPLICATE_SAME_ACCESS = 2; public const int SE_ERR_FNF = 2; public const int SE_ERR_PNF = 3; public const int SE_ERR_ACCESSDENIED = 5; public const int SE_ERR_OOM = 8; public const int SE_ERR_DLLNOTFOUND = 32; public const int SE_ERR_SHARE = 26; public const int SE_ERR_ASSOCINCOMPLETE = 27; public const int SE_ERR_DDETIMEOUT = 28; public const int SE_ERR_DDEFAIL = 29; public const int SE_ERR_DDEBUSY = 30; public const int SE_ERR_NOASSOC = 31; public const int CREATE_NO_WINDOW = 0x08000000; public const int CREATE_SUSPENDED = 0x00000004; public const int CREATE_UNICODE_ENVIRONMENT = 0x00000400; public const int SMTO_ABORTIFHUNG = 0x0002; public const int GWL_STYLE = -16; public const int GCL_WNDPROC = -24; public const int GWL_WNDPROC = -4; public const int WS_DISABLED = 0x08000000; public const int WM_NULL = 0x0000; public const int WM_CLOSE = 0x0010; public const int SW_SHOWNORMAL = 1; public const int SW_NORMAL = 1; public const int SW_SHOWMINIMIZED = 2; public const int SW_SHOWMAXIMIZED = 3; public const int SW_MAXIMIZE = 3; public const int SW_SHOWNOACTIVATE = 4; public const int SW_SHOW = 5; public const int SW_MINIMIZE = 6; public const int SW_SHOWMINNOACTIVE = 7; public const int SW_SHOWNA = 8; public const int SW_RESTORE = 9; public const int SW_SHOWDEFAULT = 10; public const int SW_MAX = 10; public const int GW_OWNER = 4; public const int WHITENESS = 0x00FF0062; } internal delegate void UserCallBack(String data); internal class AsyncStreamReader : IDisposable { internal const int DefaultBufferSize = 1024; // Byte buffer size private const int MinBufferSize = 128; private Stream stream; private Encoding encoding; private Decoder decoder; private byte[] byteBuffer; private char[] charBuffer; // Record the number of valid bytes in the byteBuffer, for a few checks. // This is the maximum number of chars we can get from one call to // ReadBuffer. Used so ReadBuffer can tell when to copy data into // a user's char[] directly, instead of our internal char[]. private int _maxCharsPerBuffer; // Store a backpointer to the process class, to check for user callbacks private Process process; // Delegate to call user function. private UserCallBack userCallBack; // Internal Cancel operation private bool cancelOperation; private ManualResetEvent eofEvent; private Queue messageQueue; private StringBuilder sb; private bool bLastCarriageReturn; // Cache the last position scanned in sb when searching for lines. private int currentLinePos; internal AsyncStreamReader(Process process, Stream stream, UserCallBack callback, Encoding encoding) : this(process, stream, callback, encoding, DefaultBufferSize) { } // Creates a new AsyncStreamReader for the given stream. The // character encoding is set by encoding and the buffer size, // in number of 16-bit characters, is set by bufferSize. // internal AsyncStreamReader(Process process, Stream stream, UserCallBack callback, Encoding encoding, int bufferSize) { Init(process, stream, callback, encoding, bufferSize); messageQueue = new Queue(); } private void Init(Process process, Stream stream, UserCallBack callback, Encoding encoding, int bufferSize) { this.process = process; this.stream = stream; this.encoding = encoding; this.userCallBack = callback; decoder = encoding.GetDecoder(); if (bufferSize < MinBufferSize) bufferSize = MinBufferSize; byteBuffer = new byte[bufferSize]; _maxCharsPerBuffer = encoding.GetMaxCharCount(bufferSize); charBuffer = new char[_maxCharsPerBuffer]; cancelOperation = false; eofEvent = new ManualResetEvent(false); sb = null; this.bLastCarriageReturn = false; } public virtual void Close() { Dispose(true); } void IDisposable.Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { if (stream != null) stream.Close(); } if (stream != null) { stream = null; encoding = null; decoder = null; byteBuffer = null; charBuffer = null; } if (eofEvent != null) { eofEvent.Close(); eofEvent = null; } } public virtual Encoding CurrentEncoding { get { return encoding; } } public virtual Stream BaseStream { get { return stream; } } // User calls BeginRead to start the asynchronous read internal void BeginReadLine() { if (cancelOperation) { cancelOperation = false; } if (sb == null) { sb = new StringBuilder(DefaultBufferSize); stream.BeginRead(byteBuffer, 0, byteBuffer.Length, new AsyncCallback(ReadBuffer), null); } else { FlushMessageQueue(); } } internal void CancelOperation() { cancelOperation = true; } // This is the async callback function. Only one thread could/should call this. private void ReadBuffer(IAsyncResult ar) { int byteLen; try { byteLen = stream.EndRead(ar); } catch (IOException) { // We should ideally consume errors from operations getting cancelled // so that we don't crash the unsuspecting parent with an unhandled exc. // This seems to come in 2 forms of exceptions (depending on platform and scenario), // namely OperationCanceledException and IOException (for errorcode that we don't // map explicitly). byteLen = 0; // Treat this as EOF } catch (OperationCanceledException) { // We should consume any OperationCanceledException from child read here // so that we don't crash the parent with an unhandled exc byteLen = 0; // Treat this as EOF } if (byteLen == 0) { // We're at EOF, we won't call this function again from here on. lock (messageQueue) { if (sb.Length != 0) { messageQueue.Enqueue(sb.ToString()); sb.Length = 0; } messageQueue.Enqueue(null); } try { // UserCallback could throw, we should still set the eofEvent FlushMessageQueue(); } finally { eofEvent.Set(); } } else { int charLen = decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, 0); sb.Append(charBuffer, 0, charLen); GetLinesFromStringBuilder(); stream.BeginRead(byteBuffer, 0, byteBuffer.Length, new AsyncCallback(ReadBuffer), null); } } // Read lines stored in StringBuilder and the buffer we just read into. // A line is defined as a sequence of characters followed by // a carriage return ('\r'), a line feed ('\n'), or a carriage return // immediately followed by a line feed. The resulting string does not // contain the terminating carriage return and/or line feed. The returned // value is null if the end of the input stream has been reached. // private void GetLinesFromStringBuilder() { int currentIndex = currentLinePos; int lineStart = 0; int len = sb.Length; // skip a beginning '\n' character of new block if last block ended // with '\r' if (bLastCarriageReturn && (len > 0) && sb[0] == '\n') { currentIndex = 1; lineStart = 1; bLastCarriageReturn = false; } while (currentIndex < len) { char ch = sb[currentIndex]; // Note the following common line feed chars: // \n - UNIX \r\n - DOS \r - Mac if (ch == '\r' || ch == '\n') { string s = sb.ToString(lineStart, currentIndex - lineStart); lineStart = currentIndex + 1; // skip the "\n" character following "\r" character if ((ch == '\r') && (lineStart < len) && (sb[lineStart] == '\n')) { lineStart++; currentIndex++; } lock (messageQueue) { messageQueue.Enqueue(s); } } currentIndex++; } // Protect length as IndexOutOfRangeException was being thrown when less than a // character's worth of bytes was read at the beginning of a line. if (len > 0 && sb[len - 1] == '\r') { bLastCarriageReturn = true; } // Keep the rest characaters which can't form a new line in string builder. if (lineStart < len) { if (lineStart == 0) { // we found no breaklines, in this case we cache the position // so next time we don't have to restart from the beginning currentLinePos = currentIndex; } else { sb.Remove(0, lineStart); currentLinePos = 0; } } else { sb.Length = 0; currentLinePos = 0; } FlushMessageQueue(); } private void FlushMessageQueue() { while (true) { // When we call BeginReadLine, we also need to flush the queue // So there could be a ---- between the ReadBuffer and BeginReadLine // We need to take lock before DeQueue. if (messageQueue.Count > 0) { lock (messageQueue) { if (messageQueue.Count > 0) { string s = (string)messageQueue.Dequeue(); // skip if the read is the read is cancelled // this might happen inside UserCallBack // However, continue to drain the queue if (!cancelOperation) { userCallBack(s); } } } } else { break; } } } // Wait until we hit EOF. This is called from Process.WaitForExit // We will lose some information if we don't do this. internal void WaitUtilEOF() { if (eofEvent != null) { eofEvent.WaitOne(); eofEvent.Close(); eofEvent = null; } } } public delegate void DataReceivedEventHandler(Object sender, DataReceivedEventArgs e); public class DataReceivedEventArgs : EventArgs { internal string _data; internal DataReceivedEventArgs(string data) => this._data = data; public string Data => this._data; } internal class ProcessWaitHandle : WaitHandle { [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] internal ProcessWaitHandle(SafeProcessHandle processHandle) : base() { SafeWaitHandle waitHandle = null; bool succeeded = NativeMethods.DuplicateHandle(new HandleRef(this, NativeMethods.GetCurrentProcess()), processHandle, new HandleRef(this, NativeMethods.GetCurrentProcess()), out waitHandle, 0, false, NativeMethods.DUPLICATE_SAME_ACCESS); if (!succeeded) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } this.SafeWaitHandle = waitHandle; } } [SuppressUnmanagedCodeSecurityAttribute] internal sealed class SafeThreadHandle : SafeHandleZeroOrMinusOneIsInvalid { internal SafeThreadHandle() : base(true) { } internal void InitialSetHandle(IntPtr h) { Debug.Assert(base.IsInvalid, "Safe handle should only be set once"); base.SetHandle(h); } override protected bool ReleaseHandle() { return CloseHandle(handle); } [DllImport("kernel32.dll", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)] public static extern bool CloseHandle(IntPtr handle); } [HostProtectionAttribute(MayLeakOnAbort = true)] [SuppressUnmanagedCodeSecurityAttribute] internal sealed class SafeLocalMemHandle : SafeHandleZeroOrMinusOneIsInvalid { [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] internal SafeLocalMemHandle(IntPtr existingHandle, bool ownsHandle) : base(ownsHandle) { SetHandle(existingHandle); } [DllImport("kernel32.dll")] [ResourceExposure(ResourceScope.None)] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] private static extern IntPtr LocalFree(IntPtr hMem); override protected bool ReleaseHandle() { return LocalFree(handle) == IntPtr.Zero; } } [SuppressUnmanagedCodeSecurityAttribute] public sealed class SafeProcessHandle : SafeHandleZeroOrMinusOneIsInvalid { internal static SafeProcessHandle InvalidHandle = new SafeProcessHandle(IntPtr.Zero); // Note that OpenProcess returns 0 on failure internal SafeProcessHandle() : base(true) { } internal SafeProcessHandle(IntPtr handle) : base(true) { SetHandle(handle); } [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] public SafeProcessHandle(IntPtr existingHandle, bool ownsHandle) : base(ownsHandle) { SetHandle(existingHandle); } [DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)] [ResourceExposure(ResourceScope.Machine)] internal static extern SafeProcessHandle OpenProcess(int access, bool inherit, int processId); internal void InitialSetHandle(IntPtr h) { Debug.Assert(base.IsInvalid, "Safe handle should only be set once"); base.handle = h; } override protected bool ReleaseHandle() { return CloseHandle(handle); } [DllImport("kernel32.dll", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)] public static extern bool CloseHandle(IntPtr handle); } [TypeConverter(typeof(ExpandableObjectConverter)), // Disabling partial trust scenarios PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust"), HostProtection(SharedState = true, SelfAffectingProcessMgmt = true)] public sealed class ProcessStartInfo { string fileName; string arguments; string directory; string verb; ProcessWindowStyle windowStyle; bool errorDialog; IntPtr errorDialogParentHandle; bool useShellExecute = false; string userName; string domain; SecureString password; string passwordInClearText; bool loadUserProfile; bool redirectStandardInput = false; bool redirectStandardOutput = false; bool redirectStandardError = false; Encoding standardOutputEncoding; Encoding standardErrorEncoding; bool createNoWindow = false; WeakReference weakParentProcess; internal StringDictionary environmentVariables; /// /// Default constructor. At least the /// property must be set before starting the process. /// public ProcessStartInfo() { } internal ProcessStartInfo(Process parent) { this.weakParentProcess = new WeakReference(parent); } /// /// Specifies the name of the application or document that is to be started. /// [ResourceExposure(ResourceScope.Machine)] public ProcessStartInfo(string fileName) { this.fileName = fileName; } /// /// Specifies the name of the application that is to be started, as well as a set /// of command line arguments to pass to the application. /// [ResourceExposure(ResourceScope.Machine)] public ProcessStartInfo(string fileName, string arguments) { this.fileName = fileName; this.arguments = arguments; } /// /// /// Specifies the verb to use when opening the filename. For example, the "print" /// verb will print a document specified using . /// Each file extension has it's own set of verbs, which can be obtained using the /// property. /// The default verb can be specified using "". /// /// /// Discuss 'opening' vs. 'starting.' I think the part about the /// default verb was a dev comment. /// Find out what /// that means. /// /// public string Verb { get { if (verb == null) return string.Empty; return verb; } set { verb = value; } } public string Arguments { get { if (arguments == null) return string.Empty; return arguments; } set { arguments = value; } } public bool CreateNoWindow { get { return createNoWindow; } set { createNoWindow = value; } } public StringDictionary EnvironmentVariables { [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] get { // Note: // Creating a detached ProcessStartInfo will pre-populate the environment // with current environmental variables. // When used with an existing Process.ProcessStartInfo the following behavior // * Desktop - Populates with current Environment (rather than that of the process) if (environmentVariables == null) { environmentVariables = new StringDictionaryWithComparer(); // if not in design mode, initialize the child environment block with all the parent variables if (!(this.weakParentProcess != null && this.weakParentProcess.IsAlive && ((Component)this.weakParentProcess.Target).Site != null && ((Component)this.weakParentProcess.Target).Site.DesignMode)) { foreach (DictionaryEntry entry in System.Environment.GetEnvironmentVariables()) environmentVariables.Add((string)entry.Key, (string)entry.Value); } } return environmentVariables; } } private IDictionary environment; public IDictionary Environment { get { if (environment == null) { environment = this.EnvironmentVariables.AsGenericDictionary(); } return environment; } } public bool RedirectStandardInput { get { return redirectStandardInput; } set { redirectStandardInput = value; } } public bool RedirectStandardOutput { get { return redirectStandardOutput; } set { redirectStandardOutput = value; } } public bool RedirectStandardError { get { return redirectStandardError; } set { redirectStandardError = value; } } public Encoding StandardErrorEncoding { get { return standardErrorEncoding; } set { standardErrorEncoding = value; } } public Encoding StandardOutputEncoding { get { return standardOutputEncoding; } set { standardOutputEncoding = value; } } public bool UseShellExecute { get { return useShellExecute; } set { useShellExecute = value; } } /// /// Returns the set of verbs associated with the file specified by the /// property. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string[] Verbs { [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] get { ArrayList verbs = new ArrayList(); RegistryKey key = null; string extension = Path.GetExtension(FileName); try { if (extension != null && extension.Length > 0) { key = Registry.ClassesRoot.OpenSubKey(extension); if (key != null) { string value = (string)key.GetValue(String.Empty); key.Close(); key = Registry.ClassesRoot.OpenSubKey(value + "\\shell"); if (key != null) { string[] names = key.GetSubKeyNames(); for (int i = 0; i < names.Length; i++) if (string.Compare(names[i], "new", StringComparison.OrdinalIgnoreCase) != 0) verbs.Add(names[i]); key.Close(); key = null; } } } } finally { if (key != null) key.Close(); } string[] temp = new string[verbs.Count]; verbs.CopyTo(temp, 0); return temp; } } public string UserName { get { if (userName == null) { return string.Empty; } else { return userName; } } set { userName = value; } } public SecureString Password { get { return password; } set { password = value; } } public string PasswordInClearText { get { return passwordInClearText; } set { passwordInClearText = value; } } public string Domain { get { if (domain == null) { return string.Empty; } else { return domain; } } set { domain = value; } } public bool LoadUserProfile { get { return loadUserProfile; } set { loadUserProfile = value; } } public string FileName { [ResourceExposure(ResourceScope.Machine)] get { if (fileName == null) return string.Empty; return fileName; } [ResourceExposure(ResourceScope.Machine)] set { fileName = value; } } public string WorkingDirectory { [ResourceExposure(ResourceScope.Machine)] get { if (directory == null) return string.Empty; return directory; } [ResourceExposure(ResourceScope.Machine)] set { directory = value; } } public bool ErrorDialog { get { return errorDialog; } set { errorDialog = value; } } [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IntPtr ErrorDialogParentHandle { get { return errorDialogParentHandle; } set { errorDialogParentHandle = value; } } public ProcessWindowStyle WindowStyle { get { return windowStyle; } set { if (!Enum.IsDefined(typeof(ProcessWindowStyle), value)) throw new InvalidEnumArgumentException("value", (int)value, typeof(ProcessWindowStyle)); windowStyle = value; } } } [Serializable] internal class StringDictionaryWithComparer : StringDictionary { public StringDictionaryWithComparer() : this((IEqualityComparer)StringComparer.OrdinalIgnoreCase) { } public StringDictionaryWithComparer(IEqualityComparer comparer) => this.ReplaceHashtable(new Hashtable(comparer)); public override string this[string key] { get => key != null ? (string)this.contents[(object)key] : throw new ArgumentNullException(nameof(key)); set { if (key == null) throw new ArgumentNullException(nameof(key)); this.contents[(object)key] = (object)value; } } public override void Add(string key, string value) { if (key == null) throw new ArgumentNullException(nameof(key)); this.contents.Add((object)key, (object)value); } public override bool ContainsKey(string key) => key != null ? this.contents.ContainsKey((object)key) : throw new ArgumentNullException(nameof(key)); public override void Remove(string key) { if (key == null) throw new ArgumentNullException(nameof(key)); this.contents.Remove((object)key); } } [Serializable] public class StringDictionary : IEnumerable { internal Hashtable contents = new Hashtable(); /// Gets the number of key/value pairs in the . /// The number of key/value pairs in the . /// Retrieving the value of this property is an O(1) operation. public virtual int Count => this.contents.Count; /// Gets a value indicating whether access to the is synchronized (thread safe). /// /// if access to the is synchronized (thread safe); otherwise, . public virtual bool IsSynchronized => this.contents.IsSynchronized; /// Gets or sets the value associated with the specified key. /// The key whose value to get or set. /// The value associated with the specified key. If the specified key is not found, Get returns , and Set creates a new entry with the specified key. /// /// is . public virtual string this[string key] { get { if (key == null) throw new ArgumentNullException(nameof(key)); return (string)this.contents[(object)key.ToLower(CultureInfo.InvariantCulture)]; } set { if (key == null) throw new ArgumentNullException(nameof(key)); this.contents[(object)key.ToLower(CultureInfo.InvariantCulture)] = (object)value; } } /// Gets a collection of keys in the . /// An that provides the keys in the . public virtual ICollection Keys => this.contents.Keys; /// Gets an object that can be used to synchronize access to the . /// An that can be used to synchronize access to the . public virtual object SyncRoot => this.contents.SyncRoot; /// Gets a collection of values in the . /// An that provides the values in the . public virtual ICollection Values => this.contents.Values; /// Adds an entry with the specified key and value into the . /// The key of the entry to add. /// The value of the entry to add. The value can be . /// /// is . /// An entry with the same key already exists in the . /// The is read-only. public virtual void Add(string key, string value) { if (key == null) throw new ArgumentNullException(nameof(key)); this.contents.Add((object)key.ToLower(CultureInfo.InvariantCulture), (object)value); } /// Removes all entries from the . /// The is read-only. public virtual void Clear() => this.contents.Clear(); /// Determines if the contains a specific key. /// The key to locate in the . /// /// if the contains an entry with the specified key; otherwise, . /// The key is . public virtual bool ContainsKey(string key) { if (key == null) throw new ArgumentNullException(nameof(key)); return this.contents.ContainsKey((object)key.ToLower(CultureInfo.InvariantCulture)); } /// Determines if the contains a specific value. /// The value to locate in the . The value can be . /// /// if the contains an element with the specified value; otherwise, . public virtual bool ContainsValue(string value) => this.contents.ContainsValue((object)value); /// Copies the string dictionary values to a one-dimensional instance at the specified index. /// The one-dimensional that is the destination of the values copied from the . /// The index in the array where copying begins. /// /// is multidimensional. /// -or- /// The number of elements in the is greater than the available space from to the end of . /// /// is . /// /// is less than the lower bound of . public virtual void CopyTo(Array array, int index) => this.contents.CopyTo(array, index); /// Returns an enumerator that iterates through the string dictionary. /// An that iterates through the string dictionary. public virtual IEnumerator GetEnumerator() => (IEnumerator)this.contents.GetEnumerator(); /// Removes the entry with the specified key from the string dictionary. /// The key of the entry to remove. /// The key is . /// The is read-only. public virtual void Remove(string key) { if (key == null) throw new ArgumentNullException(nameof(key)); this.contents.Remove((object)key.ToLower(CultureInfo.InvariantCulture)); } internal void ReplaceHashtable(Hashtable useThisHashtableInstead) => this.contents = useThisHashtableInstead; internal IDictionary AsGenericDictionary() => (IDictionary)new StringDictionary.GenericAdapter(this); private class GenericAdapter : IDictionary, ICollection>, IEnumerable>, IEnumerable { private StringDictionary m_stringDictionary; private StringDictionary.GenericAdapter.ICollectionToGenericCollectionAdapter _values; private StringDictionary.GenericAdapter.ICollectionToGenericCollectionAdapter _keys; internal GenericAdapter(StringDictionary stringDictionary) => this.m_stringDictionary = stringDictionary; public void Add(string key, string value) => this[key] = value; public bool ContainsKey(string key) => this.m_stringDictionary.ContainsKey(key); public void Clear() => this.m_stringDictionary.Clear(); public int Count => this.m_stringDictionary.Count; public string this[string key] { get { if (key == null) throw new ArgumentNullException(nameof(key)); return this.m_stringDictionary.ContainsKey(key) ? this.m_stringDictionary[key] : throw new KeyNotFoundException(); } set { if (key == null) throw new ArgumentNullException(nameof(key)); this.m_stringDictionary[key] = value; } } public ICollection Keys { get { if (this._keys == null) this._keys = new StringDictionary.GenericAdapter.ICollectionToGenericCollectionAdapter(this.m_stringDictionary, StringDictionary.GenericAdapter.KeyOrValue.Key); return (ICollection)this._keys; } } public ICollection Values { get { if (this._values == null) this._values = new StringDictionary.GenericAdapter.ICollectionToGenericCollectionAdapter(this.m_stringDictionary, StringDictionary.GenericAdapter.KeyOrValue.Value); return (ICollection)this._values; } } public bool Remove(string key) { if (!this.m_stringDictionary.ContainsKey(key)) return false; this.m_stringDictionary.Remove(key); return true; } public bool TryGetValue(string key, out string value) { if (!this.m_stringDictionary.ContainsKey(key)) { value = (string)null; return false; } value = this.m_stringDictionary[key]; return true; } void ICollection>.Add(KeyValuePair item) => this.m_stringDictionary.Add(item.Key, item.Value); bool ICollection>.Contains(KeyValuePair item) { string str; return this.TryGetValue(item.Key, out str) && str.Equals(item.Value); } void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException(nameof(array), "ArgumentNull_Array"); if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex), "ArgumentOutOfRange_NeedNonNegNum"); if (array.Length - arrayIndex < this.Count) throw new ArgumentException("Arg_ArrayPlusOffTooSmall"); int num = arrayIndex; foreach (DictionaryEntry dictionaryEntry in this.m_stringDictionary) array[num++] = new KeyValuePair((string)dictionaryEntry.Key, (string)dictionaryEntry.Value); } bool ICollection>.IsReadOnly => false; bool ICollection>.Remove(KeyValuePair item) { if (!((ICollection>)this).Contains(item)) return false; this.m_stringDictionary.Remove(item.Key); return true; } IEnumerator IEnumerable.GetEnumerator() => (IEnumerator)this.GetEnumerator(); public IEnumerator> GetEnumerator() { foreach (DictionaryEntry dictionaryEntry in this.m_stringDictionary) yield return new KeyValuePair((string)dictionaryEntry.Key, (string)dictionaryEntry.Value); } internal enum KeyOrValue { Key, Value, } private class ICollectionToGenericCollectionAdapter : ICollection, IEnumerable, IEnumerable { private StringDictionary _internal; private StringDictionary.GenericAdapter.KeyOrValue _keyOrValue; public ICollectionToGenericCollectionAdapter(StringDictionary source, StringDictionary.GenericAdapter.KeyOrValue keyOrValue) { this._internal = source != null ? source : throw new ArgumentNullException(nameof(source)); this._keyOrValue = keyOrValue; } public void Add(string item) => this.ThrowNotSupportedException(); public void Clear() => this.ThrowNotSupportedException(); public void ThrowNotSupportedException() { if (this._keyOrValue == StringDictionary.GenericAdapter.KeyOrValue.Key) throw new NotSupportedException("NotSupported_KeyCollectionSet"); throw new NotSupportedException("NotSupported_ValueCollectionSet"); } public bool Contains(string item) => this._keyOrValue == StringDictionary.GenericAdapter.KeyOrValue.Key ? this._internal.ContainsKey(item) : this._internal.ContainsValue(item); public void CopyTo(string[] array, int arrayIndex) => this.GetUnderlyingCollection().CopyTo((Array)array, arrayIndex); public int Count => this._internal.Count; public bool IsReadOnly => true; public bool Remove(string item) { this.ThrowNotSupportedException(); return false; } private ICollection GetUnderlyingCollection() => this._keyOrValue == StringDictionary.GenericAdapter.KeyOrValue.Key ? this._internal.Keys : this._internal.Values; public IEnumerator GetEnumerator() { foreach (string underlying in (IEnumerable)this.GetUnderlyingCollection()) yield return underlying; } IEnumerator IEnumerable.GetEnumerator() => this.GetUnderlyingCollection().GetEnumerator(); } } } } }