CLI tool for running Playbooks
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3340 lines
151 KiB

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;
}
}
/// <devdoc>
/// 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.
/// </devdoc>
/// <internalonly/>
void ReleaseProcessHandle(SafeProcessHandle handle)
{
if (handle == null)
{
return;
}
if (haveProcessHandle && handle == m_processHandle)
{
return;
}
handle.Close();
}
/// <devdoc>
/// This is called from the threadpool when a proces exits.
/// </devdoc>
/// <internalonly/>
private void CompletionCallback(object context, bool wasSignaled)
{
StopWatchingForExit();
RaiseOnExited();
}
/// <internalonly/>
/// <devdoc>
/// <para>
/// Free any resources associated with this component.
/// </para>
/// </devdoc>
protected override void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
//Dispose managed and unmanaged resources
Close();
}
this.disposed = true;
base.Dispose(disposing);
}
}
/// <devdoc>
/// <para>
/// Frees any resources associated with this component.
/// </para>
/// </devdoc>
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();
}
}
/// <devdoc>
/// Helper method for checking preconditions when accessing properties.
/// </devdoc>
/// <internalonly/>
[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.");
}
}
/// <devdoc>
/// 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.
/// </devdoc>
/// <internalonly/>
SafeProcessHandle GetProcessHandle(int access)
{
return GetProcessHandle(access, true);
}
/// <devdoc>
/// 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.
/// </devdoc>
/// <internalonly/>
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;
}
/// <devdoc>
/// Raise the Exited event, but make sure we don't do it more than once.
/// </devdoc>
/// <internalonly/>
void RaiseOnExited()
{
if (!raisedOnExited)
{
lock (this)
{
if (!raisedOnExited)
{
raisedOnExited = true;
OnExited();
}
}
}
}
/// <devdoc>
/// <para>
/// Discards any information about the associated process
/// that has been cached inside the process component. After <see cref='System.Diagnostics.Process.Refresh'/> is called, the
/// first request for information for each property causes the process component
/// to obtain a new value from the associated process.
/// </para>
/// </devdoc>
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;
}
/// <devdoc>
/// Helper to associate a process handle with this component.
/// </devdoc>
/// <internalonly/>
void SetProcessHandle(SafeProcessHandle processHandle)
{
this.m_processHandle = processHandle;
this.haveProcessHandle = true;
if (watchForExit)
{
EnsureWatchingForExit();
}
}
/// <devdoc>
/// Helper to associate a process id with this component.
/// </devdoc>
/// <internalonly/>
[ResourceExposure(ResourceScope.Machine)]
void SetProcessId(int processId)
{
this.processId = processId;
this.haveProcessId = true;
}
/// <devdoc>
/// <para>
/// Starts a process specified by the <see cref='System.Diagnostics.Process.StartInfo'/> property of this <see cref='System.Diagnostics.Process'/>
/// component and associates it with the
/// <see cref='System.Diagnostics.Process'/> . If a process resource is reused
/// rather than started, the reused process is associated with this <see cref='System.Diagnostics.Process'/>
/// component.
/// </para>
/// </devdoc>
[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<string, string>(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<string, string> environment)
{
switch (name)
{
case null:
case "":
return name;
default:
environment = new Dictionary<string, string>(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;
}
/// <devdoc>
/// <para>
/// 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 <see cref='System.Diagnostics.Process'/>
/// component.
/// </para>
/// </devdoc>
[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;
}
/// <devdoc>
/// <para>
/// Stops the
/// associated process immediately.
/// </para>
/// </devdoc>
[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);
}
}
/// <devdoc>
/// Make sure we are not watching for process exit.
/// </devdoc>
/// <internalonly/>
void StopWatchingForExit()
{
if (watchingForExit)
{
lock (this)
{
if (watchingForExit)
{
watchingForExit = false;
registeredWaitHandle.Unregister(null);
waitHandle.Close();
waitHandle = null;
registeredWaitHandle = null;
}
}
}
}
/// <devdoc>
/// <para>
/// Instructs the <see cref='System.Diagnostics.Process'/> component to wait the specified number of milliseconds for the associated process to exit.
/// </para>
/// </devdoc>
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;
}
/// <devdoc>
/// <para>
/// Instructs the <see cref='System.Diagnostics.Process'/> component to wait
/// indefinitely for the associated process to exit.
/// </para>
/// </devdoc>
public void WaitForExit()
{
WaitForExit(-1);
}
/// <devdoc>
/// <para>
/// Causes the <see cref='System.Diagnostics.Process'/> 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.
/// </para>
/// </devdoc>
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;
}
/// <devdoc>
/// <para>
/// Instructs the <see cref='System.Diagnostics.Process'/> 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.
/// </para>
/// </devdoc>
public bool WaitForInputIdle()
{
return WaitForInputIdle(Int32.MaxValue);
}
// Support for working asynchronously with streams
/// <devdoc>
/// <para>
/// Instructs the <see cref='System.Diagnostics.Process'/> 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.
/// </para>
/// </devdoc>
[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();
}
/// <devdoc>
/// <para>
/// Instructs the <see cref='System.Diagnostics.Process'/> 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.
/// </para>
/// </devdoc>
[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();
}
/// <devdoc>
/// <para>
/// Instructs the <see cref='System.Diagnostics.Process'/> component to cancel the asynchronous operation
/// specified by BeginOutputReadLine().
/// </para>
/// </devdoc>
[System.Runtime.InteropServices.ComVisible(false)]
public void CancelOutputRead()
{
if (output != null)
{
output.CancelOperation();
}
else
{
throw new InvalidOperationException("NoAsyncOperation");
}
pendingOutputRead = false;
}
/// <devdoc>
/// <para>
/// Instructs the <see cref='System.Diagnostics.Process'/> component to cancel the asynchronous operation
/// specified by BeginErrorReadLine().
/// </para>
/// </devdoc>
[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.
}
}
}
/// <summary>
/// A desired internal state.
/// </summary>
/// <internalonly/>
enum State
{
HaveId = 0x1,
IsLocal = 0x2,
IsNt = 0x4,
HaveProcessInfo = 0x8,
Exited = 0x10,
Associated = 0x20,
IsWin2k = 0x40,
HaveNtProcessInfo = HaveProcessInfo | IsNt
}
}
/// <devdoc>
/// 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.
/// </devdoc>
/// <internalonly/>
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;
}
/// <devdoc>
/// 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.
/// </devdoc>
/// <internalonly/>
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;
}
/// <devdoc>
/// 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.
/// </devdoc>
/// <internalonly/>
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<int>)(pid => pid == processId));
if (processInfos.Length == 1) return processInfos[0];
return (ProcessInfo)null;
}
internal static ProcessInfo[] GetProcessInfos(Predicate<int> processIdFilter = null)
{
int returnedSize = 0;
GCHandle gcHandle = new GCHandle();
int num = 131072;
long[] numArray = Interlocked.Exchange<long[]>(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<long[]>(ref CachedBuffer, numArray);
if (gcHandle.IsAllocated) gcHandle.Free();
}
}
private static ProcessInfo[] GetProcessInfos(IntPtr dataPtr, Predicate<int> 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;
/// <devdoc>
/// Default constructor. At least the <see cref='System.Diagnostics.ProcessStartInfo.FileName'/>
/// property must be set before starting the process.
/// </devdoc>
public ProcessStartInfo() { }
internal ProcessStartInfo(Process parent)
{
this.weakParentProcess = new WeakReference(parent);
}
/// <devdoc>
/// Specifies the name of the application or document that is to be started.
/// </devdoc>
[ResourceExposure(ResourceScope.Machine)]
public ProcessStartInfo(string fileName)
{
this.fileName = fileName;
}
/// <devdoc>
/// 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.
/// </devdoc>
[ResourceExposure(ResourceScope.Machine)]
public ProcessStartInfo(string fileName, string arguments)
{
this.fileName = fileName;
this.arguments = arguments;
}
/// <devdoc>
/// <para>
/// Specifies the verb to use when opening the filename. For example, the "print"
/// verb will print a document specified using <see cref='System.Diagnostics.ProcessStartInfo.FileName'/>.
/// Each file extension has it's own set of verbs, which can be obtained using the
/// <see cref='System.Diagnostics.ProcessStartInfo.Verbs'/> property.
/// The default verb can be specified using "".
/// </para>
/// <note type="rnotes">
/// Discuss 'opening' vs. 'starting.' I think the part about the
/// default verb was a dev comment.
/// Find out what
/// that means.
/// </note>
/// </devdoc>
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<string, string> environment;
public IDictionary<string, string> 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;
}
}
/// <devdoc>
/// Returns the set of verbs associated with the file specified by the
/// <see cref='System.Diagnostics.ProcessStartInfo.FileName'/> property.
/// </devdoc>
[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();
/// <summary>Gets the number of key/value pairs in the <see cref="T:System.Collections.Specialized.StringDictionary" />.</summary>
/// <returns>The number of key/value pairs in the <see cref="T:System.Collections.Specialized.StringDictionary" />.
/// Retrieving the value of this property is an O(1) operation.</returns>
public virtual int Count => this.contents.Count;
/// <summary>Gets a value indicating whether access to the <see cref="T:System.Collections.Specialized.StringDictionary" /> is synchronized (thread safe).</summary>
/// <returns>
/// <see langword="true" /> if access to the <see cref="T:System.Collections.Specialized.StringDictionary" /> is synchronized (thread safe); otherwise, <see langword="false" />.</returns>
public virtual bool IsSynchronized => this.contents.IsSynchronized;
/// <summary>Gets or sets the value associated with the specified key.</summary>
/// <param name="key">The key whose value to get or set.</param>
/// <returns>The value associated with the specified key. If the specified key is not found, Get returns <see langword="null" />, and Set creates a new entry with the specified key.</returns>
/// <exception cref="T:System.ArgumentNullException">
/// <paramref name="key" /> is <see langword="null" />.</exception>
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;
}
}
/// <summary>Gets a collection of keys in the <see cref="T:System.Collections.Specialized.StringDictionary" />.</summary>
/// <returns>An <see cref="T:System.Collections.ICollection" /> that provides the keys in the <see cref="T:System.Collections.Specialized.StringDictionary" />.</returns>
public virtual ICollection Keys => this.contents.Keys;
/// <summary>Gets an object that can be used to synchronize access to the <see cref="T:System.Collections.Specialized.StringDictionary" />.</summary>
/// <returns>An <see cref="T:System.Object" /> that can be used to synchronize access to the <see cref="T:System.Collections.Specialized.StringDictionary" />.</returns>
public virtual object SyncRoot => this.contents.SyncRoot;
/// <summary>Gets a collection of values in the <see cref="T:System.Collections.Specialized.StringDictionary" />.</summary>
/// <returns>An <see cref="T:System.Collections.ICollection" /> that provides the values in the <see cref="T:System.Collections.Specialized.StringDictionary" />.</returns>
public virtual ICollection Values => this.contents.Values;
/// <summary>Adds an entry with the specified key and value into the <see cref="T:System.Collections.Specialized.StringDictionary" />.</summary>
/// <param name="key">The key of the entry to add.</param>
/// <param name="value">The value of the entry to add. The value can be <see langword="null" />.</param>
/// <exception cref="T:System.ArgumentNullException">
/// <paramref name="key" /> is <see langword="null" />.</exception>
/// <exception cref="T:System.ArgumentException">An entry with the same key already exists in the <see cref="T:System.Collections.Specialized.StringDictionary" />.</exception>
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Specialized.StringDictionary" /> is read-only.</exception>
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);
}
/// <summary>Removes all entries from the <see cref="T:System.Collections.Specialized.StringDictionary" />.</summary>
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Specialized.StringDictionary" /> is read-only.</exception>
public virtual void Clear() => this.contents.Clear();
/// <summary>Determines if the <see cref="T:System.Collections.Specialized.StringDictionary" /> contains a specific key.</summary>
/// <param name="key">The key to locate in the <see cref="T:System.Collections.Specialized.StringDictionary" />.</param>
/// <returns>
/// <see langword="true" /> if the <see cref="T:System.Collections.Specialized.StringDictionary" /> contains an entry with the specified key; otherwise, <see langword="false" />.</returns>
/// <exception cref="T:System.ArgumentNullException">The key is <see langword="null" />.</exception>
public virtual bool ContainsKey(string key)
{
if (key == null) throw new ArgumentNullException(nameof(key));
return this.contents.ContainsKey((object)key.ToLower(CultureInfo.InvariantCulture));
}
/// <summary>Determines if the <see cref="T:System.Collections.Specialized.StringDictionary" /> contains a specific value.</summary>
/// <param name="value">The value to locate in the <see cref="T:System.Collections.Specialized.StringDictionary" />. The value can be <see langword="null" />.</param>
/// <returns>
/// <see langword="true" /> if the <see cref="T:System.Collections.Specialized.StringDictionary" /> contains an element with the specified value; otherwise, <see langword="false" />.</returns>
public virtual bool ContainsValue(string value) => this.contents.ContainsValue((object)value);
/// <summary>Copies the string dictionary values to a one-dimensional <see cref="T:System.Array" /> instance at the specified index.</summary>
/// <param name="array">The one-dimensional <see cref="T:System.Array" /> that is the destination of the values copied from the <see cref="T:System.Collections.Specialized.StringDictionary" />.</param>
/// <param name="index">The index in the array where copying begins.</param>
/// <exception cref="T:System.ArgumentException">
/// <paramref name="array" /> is multidimensional.
/// -or-
/// The number of elements in the <see cref="T:System.Collections.Specialized.StringDictionary" /> is greater than the available space from <paramref name="index" /> to the end of <paramref name="array" />.</exception>
/// <exception cref="T:System.ArgumentNullException">
/// <paramref name="array" /> is <see langword="null" />.</exception>
/// <exception cref="T:System.ArgumentOutOfRangeException">
/// <paramref name="index" /> is less than the lower bound of <paramref name="array" />.</exception>
public virtual void CopyTo(Array array, int index) => this.contents.CopyTo(array, index);
/// <summary>Returns an enumerator that iterates through the string dictionary.</summary>
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> that iterates through the string dictionary.</returns>
public virtual IEnumerator GetEnumerator() => (IEnumerator)this.contents.GetEnumerator();
/// <summary>Removes the entry with the specified key from the string dictionary.</summary>
/// <param name="key">The key of the entry to remove.</param>
/// <exception cref="T:System.ArgumentNullException">The key is <see langword="null" />.</exception>
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Specialized.StringDictionary" /> is read-only.</exception>
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<string, string> AsGenericDictionary() => (IDictionary<string, string>)new StringDictionary.GenericAdapter(this);
private class GenericAdapter : IDictionary<string, string>, ICollection<KeyValuePair<string, string>>, IEnumerable<KeyValuePair<string, string>>, 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<string> Keys
{
get
{
if (this._keys == null)
this._keys = new StringDictionary.GenericAdapter.ICollectionToGenericCollectionAdapter(this.m_stringDictionary, StringDictionary.GenericAdapter.KeyOrValue.Key);
return (ICollection<string>)this._keys;
}
}
public ICollection<string> Values
{
get
{
if (this._values == null)
this._values = new StringDictionary.GenericAdapter.ICollectionToGenericCollectionAdapter(this.m_stringDictionary, StringDictionary.GenericAdapter.KeyOrValue.Value);
return (ICollection<string>)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<KeyValuePair<string, string>>.Add(KeyValuePair<string, string> item) => this.m_stringDictionary.Add(item.Key, item.Value);
bool ICollection<KeyValuePair<string, string>>.Contains(KeyValuePair<string, string> item)
{
string str;
return this.TryGetValue(item.Key, out str) && str.Equals(item.Value);
}
void ICollection<KeyValuePair<string, string>>.CopyTo(KeyValuePair<string, string>[] 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, string>((string)dictionaryEntry.Key, (string)dictionaryEntry.Value);
}
bool ICollection<KeyValuePair<string, string>>.IsReadOnly => false;
bool ICollection<KeyValuePair<string, string>>.Remove(KeyValuePair<string, string> item)
{
if (!((ICollection<KeyValuePair<string, string>>)this).Contains(item)) return false;
this.m_stringDictionary.Remove(item.Key);
return true;
}
IEnumerator IEnumerable.GetEnumerator() => (IEnumerator)this.GetEnumerator();
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
foreach (DictionaryEntry dictionaryEntry in this.m_stringDictionary) yield return new KeyValuePair<string, string>((string)dictionaryEntry.Key, (string)dictionaryEntry.Value);
}
internal enum KeyOrValue
{
Key,
Value,
}
private class ICollectionToGenericCollectionAdapter : ICollection<string>, IEnumerable<string>, 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<string> GetEnumerator()
{
foreach (string underlying in (IEnumerable)this.GetUnderlyingCollection()) yield return underlying;
}
IEnumerator IEnumerable.GetEnumerator() => this.GetUnderlyingCollection().GetEnumerator();
}
}
}
}
}