diff options
author | Ludovic Henry <ludovic@xamarin.com> | 2016-02-08 21:56:29 +0300 |
---|---|---|
committer | Ludovic Henry <ludovic@xamarin.com> | 2016-02-12 16:56:38 +0300 |
commit | d30e99d172ec5074ce6deb44bc37f9a9ecaef7fe (patch) | |
tree | 94a5b3d3df3e72acc7d652f09ea0d1d8e7d131d9 /mcs/class/System/System.Diagnostics/Process.cs | |
parent | 35889d4bc2be1f5e7a68623e3fc33cf24080c2ee (diff) |
[System] Import System.Diagnostics.Process
Diffstat (limited to 'mcs/class/System/System.Diagnostics/Process.cs')
-rw-r--r-- | mcs/class/System/System.Diagnostics/Process.cs | 1049 |
1 files changed, 171 insertions, 878 deletions
diff --git a/mcs/class/System/System.Diagnostics/Process.cs b/mcs/class/System/System.Diagnostics/Process.cs index 46224521045..09340e7d045 100644 --- a/mcs/class/System/System.Diagnostics/Process.cs +++ b/mcs/class/System/System.Diagnostics/Process.cs @@ -43,16 +43,12 @@ using System.Security.Permissions; using System.Collections.Generic; using System.Security; using System.Threading; +using Microsoft.Win32; using Microsoft.Win32.SafeHandles; -namespace System.Diagnostics { - - [DefaultEvent ("Exited"), DefaultProperty ("StartInfo")] - [Designer ("System.Diagnostics.Design.ProcessDesigner, " + Consts.AssemblySystem_Design)] - [PermissionSet (SecurityAction.LinkDemand, Unrestricted = true)] - [PermissionSet (SecurityAction.InheritanceDemand, Unrestricted = true)] - [MonitoringDescription ("Represents a system process")] - public class Process : Component +namespace System.Diagnostics +{ + public partial class Process : Component { [StructLayout(LayoutKind.Sequential)] private struct ProcInfo @@ -74,21 +70,12 @@ namespace System.Diagnostics { public bool LoadUserProfile; }; - IntPtr process_handle; - int pid; - int enable_raising_events; - Thread background_wait_for_exit_thread; - ISynchronizeInvoke synchronizingObject; - EventHandler exited_event; + string process_name; /* Private constructor called from other methods */ - private Process(IntPtr handle, int id) { - process_handle = handle; - pid=id; - } - - public Process () - { + private Process (SafeProcessHandle handle, int id) { + m_processHandle = handle; + SetProcessId (id); } [MonoTODO] @@ -98,67 +85,6 @@ namespace System.Diagnostics { get { return 0; } } - [DefaultValue (false), Browsable (false)] - [MonitoringDescription ("Check for exiting of the process to raise the apropriate event.")] - public bool EnableRaisingEvents { - get { - return enable_raising_events == 1; - } - set { - if (value && Interlocked.Exchange (ref enable_raising_events, 1) == 0) - StartBackgroundWaitForExit (); - } - } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern static int ExitCode_internal(IntPtr handle); - - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)] - [MonitoringDescription ("The exit code of the process.")] - public int ExitCode { - get { - if (process_handle == IntPtr.Zero) - throw new InvalidOperationException ("Process has not been started."); - - int code = ExitCode_internal (process_handle); - if (code == 259) - throw new InvalidOperationException ("The process must exit before getting the requested information."); - - return code; - } - } - - /* Returns the process start time in Windows file - * times (ticks from DateTime(1/1/1601 00:00 GMT)) - */ - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern static long ExitTime_internal(IntPtr handle); - - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)] - [MonitoringDescription ("The exit time of the process.")] - public DateTime ExitTime { - get { - if (process_handle == IntPtr.Zero) - throw new InvalidOperationException ("Process has not been started."); - - if (!HasExited) - throw new InvalidOperationException ("The process must exit before " + - "getting the requested information."); - - return(DateTime.FromFileTime(ExitTime_internal(process_handle))); - } - } - - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)] - [MonitoringDescription ("Handle for this process.")] - public IntPtr Handle { - get { - if (process_handle == IntPtr.Zero) - throw new InvalidOperationException ("No process is associated with this object."); - return(process_handle); - } - } - [MonoTODO] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] [MonitoringDescription ("Handles for this process.")] @@ -169,44 +95,6 @@ namespace System.Diagnostics { } [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)] - [MonitoringDescription ("Determines if the process is still running.")] - public bool HasExited { - get { - if (process_handle == IntPtr.Zero) - throw new InvalidOperationException ("Process has not been started."); - - int exitcode = ExitCode_internal (process_handle); - - if(exitcode==259) { - /* STILL_ACTIVE */ - return(false); - } else { - return(true); - } - } - } - - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] - [MonitoringDescription ("Process identifier.")] - public int Id { - get { - if (pid == 0) - throw new InvalidOperationException ("Process ID has not been set."); - - return(pid); - } - } - - [MonoTODO] - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)] - [MonitoringDescription ("The name of the computer running the process.")] - public string MachineName { - get { - return("localhost"); - } - } - - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)] [MonitoringDescription ("The main module of the process.")] public ProcessModule MainModule { get { @@ -232,86 +120,39 @@ namespace System.Diagnostics { } } - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern static bool GetWorkingSet_internal(IntPtr handle, out int min, out int max); - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern static bool SetWorkingSet_internal(IntPtr handle, int min, int max, bool use_min); - - /* LAMESPEC: why is this an IntPtr not a plain int? */ - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] - [MonitoringDescription ("The maximum working set for this process.")] - public IntPtr MaxWorkingSet { - get { - if(HasExited) - throw new InvalidOperationException( - "The process " + ProcessName + - " (ID " + Id + ") has exited"); - - int min; - int max; - bool ok=GetWorkingSet_internal(process_handle, out min, out max); - if(ok==false) { - throw new Win32Exception(); - } - - return((IntPtr)max); - } - set { - if(HasExited) { - throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited"); - } - - bool ok=SetWorkingSet_internal(process_handle, 0, value.ToInt32(), false); - if(ok==false) { - throw new Win32Exception(); - } - } - } - - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] - [MonitoringDescription ("The minimum working set for this process.")] - public IntPtr MinWorkingSet { - get { - if(HasExited) - throw new InvalidOperationException( - "The process " + ProcessName + - " (ID " + Id + ") has exited"); - - int min; - int max; - bool ok= GetWorkingSet_internal (process_handle, out min, out max); - if(!ok) - throw new Win32Exception(); - return ((IntPtr) min); - } - set { - if(HasExited) - throw new InvalidOperationException( - "The process " + ProcessName + - " (ID " + Id + ") has exited"); - - bool ok = SetWorkingSet_internal (process_handle, value.ToInt32(), 0, true); - if (!ok) - throw new Win32Exception(); - } - } - /* Returns the list of process modules. The main module is * element 0. */ [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern ProcessModule[] GetModules_internal(IntPtr handle); - private ProcessModuleCollection module_collection; - + ProcessModule[] GetModules_internal (SafeProcessHandle handle) + { + bool release = false; + try { + handle.DangerousAddRef (ref release); + return GetModules_internal (handle.DangerousGetHandle ()); + } finally { + if (release) + handle.DangerousRelease (); + } + } + [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)] [MonitoringDescription ("The modules that are loaded as part of this process.")] public ProcessModuleCollection Modules { get { - if (module_collection == null) - module_collection = new ProcessModuleCollection( - GetModules_internal (process_handle)); - return(module_collection); + if (modules == null) { + SafeProcessHandle handle = null; + try { + handle = GetProcessHandle (NativeMethods.PROCESS_QUERY_INFORMATION); + modules = new ProcessModuleCollection (GetModules_internal (handle)); + } finally { + ReleaseProcessHandle (handle); + } + } + + return modules; } } @@ -363,7 +204,7 @@ namespace System.Diagnostics { public int PeakVirtualMemorySize { get { int error; - return (int)GetProcessData (pid, 8, out error); + return (int)GetProcessData (processId, 8, out error); } } @@ -373,7 +214,7 @@ namespace System.Diagnostics { public int PeakWorkingSet { get { int error; - return (int)GetProcessData (pid, 5, out error); + return (int)GetProcessData (processId, 5, out error); } } @@ -393,7 +234,7 @@ namespace System.Diagnostics { public long PagedMemorySize64 { get { int error; - return GetProcessData (pid, 12, out error); + return GetProcessData (processId, 12, out error); } } @@ -422,7 +263,7 @@ namespace System.Diagnostics { public long PeakVirtualMemorySize64 { get { int error; - return GetProcessData (pid, 8, out error); + return GetProcessData (processId, 8, out error); } } @@ -432,7 +273,7 @@ namespace System.Diagnostics { public long PeakWorkingSet64 { get { int error; - return GetProcessData (pid, 5, out error); + return GetProcessData (processId, 5, out error); } } @@ -447,55 +288,13 @@ namespace System.Diagnostics { } } - [MonoLimitation ("Under Unix, only root is allowed to raise the priority.")] - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] - [MonitoringDescription ("The relative process priority.")] - public ProcessPriorityClass PriorityClass { - get { - if (process_handle == IntPtr.Zero) - throw new InvalidOperationException ("Process has not been started."); - - int error; - int prio = GetPriorityClass (process_handle, out error); - if (prio == 0) - throw new Win32Exception (error); - return (ProcessPriorityClass) prio; - } - set { - if (!Enum.IsDefined (typeof (ProcessPriorityClass), value)) - throw new InvalidEnumArgumentException ( - "value", (int) value, - typeof (ProcessPriorityClass)); - - if (process_handle == IntPtr.Zero) - throw new InvalidOperationException ("Process has not been started."); - - int error; - if (!SetPriorityClass (process_handle, (int) value, out error)) { - CheckExited (); - throw new Win32Exception (error); - } - } - } - - void CheckExited () { - if (HasExited) - throw new InvalidOperationException (String.Format ("Cannot process request because the process ({0}) has exited.", Id)); - } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - static extern int GetPriorityClass (IntPtr handle, out int error); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - static extern bool SetPriorityClass (IntPtr handle, int priority, out int error); - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] [MonitoringDescription ("The amount of memory exclusively used by this process.")] [Obsolete ("Use PrivateMemorySize64")] public int PrivateMemorySize { get { int error; - return (int)GetProcessData (pid, 6, out error); + return (int)GetProcessData (processId, 6, out error); } } @@ -506,54 +305,45 @@ namespace System.Diagnostics { get { throw new NotImplementedException (); } } - /* the meaning of type is as follows: 0: user, 1: system, 2: total */ [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern static long Times (IntPtr handle, int type); + private extern static string ProcessName_internal(IntPtr handle); - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] - [MonitoringDescription ("The amount of processing time spent in the OS core for this process.")] - public TimeSpan PrivilegedProcessorTime { - get { - return new TimeSpan (Times (process_handle, 1)); + static string ProcessName_internal(SafeProcessHandle handle) + { + bool release = false; + try { + handle.DangerousAddRef (ref release); + return ProcessName_internal (handle.DangerousGetHandle ()); + } finally { + if (release) + handle.DangerousRelease (); } } - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern static string ProcessName_internal(IntPtr handle); - - private string process_name=null; - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] [MonitoringDescription ("The name of this process.")] public string ProcessName { get { - if(process_name==null) { - - if (process_handle == IntPtr.Zero) - throw new InvalidOperationException ("No process is associated with this object."); - - process_name=ProcessName_internal(process_handle); - /* If process_name is _still_ - * null, assume the process - * has exited - */ - if (process_name == null) - throw new InvalidOperationException ("Process has exited, so the requested information is not available."); - - /* Strip the suffix (if it - * exists) simplistically - * instead of removing any - * trailing \.???, so we dont - * get stupid results on sane - * systems - */ - if(process_name.EndsWith(".exe") || - process_name.EndsWith(".bat") || - process_name.EndsWith(".com")) { - process_name=process_name.Substring(0, process_name.Length-4); + if (process_name == null) { + SafeProcessHandle handle = null; + try { + handle = GetProcessHandle (NativeMethods.PROCESS_QUERY_INFORMATION); + + process_name = ProcessName_internal (handle); + + /* If process_name is _still_ null, assume the process has exited */ + if (process_name == null) + throw new InvalidOperationException ("Process has exited, so the requested information is not available."); + + /* Strip the suffix (if it exists) simplistically instead of removing + * any trailing \.???, so we dont get stupid results on sane systems */ + if(process_name.EndsWith(".exe") || process_name.EndsWith(".bat") || process_name.EndsWith(".com")) + process_name = process_name.Substring (0, process_name.Length - 4); + } finally { + ReleaseProcessHandle (handle); } } - return(process_name); + return process_name; } } @@ -577,79 +367,13 @@ namespace System.Diagnostics { } } -#if MONO_FEATURE_PROCESS_START - private StreamReader error_stream=null; - bool error_stream_exposed; - - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)] - [MonitoringDescription ("The standard error stream of this process.")] - public StreamReader StandardError { - get { - if (error_stream == null) - throw new InvalidOperationException("Standard error has not been redirected"); - - if ((async_mode & AsyncModes.AsyncError) != 0) - throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads."); - - async_mode |= AsyncModes.SyncError; - - error_stream_exposed = true; - return(error_stream); - } - } - - private StreamWriter input_stream=null; - bool input_stream_exposed; - - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)] - [MonitoringDescription ("The standard input stream of this process.")] - public StreamWriter StandardInput { - get { - if (input_stream == null) - throw new InvalidOperationException("Standard input has not been redirected"); - - input_stream_exposed = true; - return(input_stream); - } - } - - private StreamReader output_stream=null; - bool output_stream_exposed; - - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)] - [MonitoringDescription ("The standard output stream of this process.")] - public StreamReader StandardOutput { - get { - if (output_stream == null) - throw new InvalidOperationException("Standard output has not been redirected"); - - if ((async_mode & AsyncModes.AsyncOutput) != 0) - throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads."); - - async_mode |= AsyncModes.SyncOutput; - - output_stream_exposed = true; - return(output_stream); - } - } - - private ProcessStartInfo start_info=null; - - [DesignerSerializationVisibility (DesignerSerializationVisibility.Content), Browsable (false)] - [MonitoringDescription ("Information for the start of this process.")] +#if !MONO_FEATURE_PROCESS_START + [Obsolete ("Process.StartInfo is not supported on the current platform.", true)] public ProcessStartInfo StartInfo { - get { - if (start_info == null) - start_info = new ProcessStartInfo(); - return start_info; - } - set { - if (value == null) - throw new ArgumentNullException("value"); - start_info = value; - } + get { throw new PlatformNotSupportedException ("Process.StartInfo is not supported on the current platform."); } + set { throw new PlatformNotSupportedException ("Process.StartInfo is not supported on the current platform."); } } -#else + [Obsolete ("Process.StandardError is not supported on the current platform.", true)] public StreamReader StandardError { get { throw new PlatformNotSupportedException ("Process.StandardError is not supported on the current platform."); } @@ -664,59 +388,20 @@ namespace System.Diagnostics { public StreamReader StandardOutput { get { throw new PlatformNotSupportedException ("Process.StandardOutput is not supported on the current platform."); } } - - [Obsolete ("Process.StartInfo is not supported on the current platform.", true)] - public ProcessStartInfo StartInfo { - get { throw new PlatformNotSupportedException ("Process.StartInfo is not supported on the current platform."); } - set { throw new PlatformNotSupportedException ("Process.StartInfo is not supported on the current platform."); } - } -#endif // MONO_FEATURE_PROCESS_START - - /* Returns the process start time in Windows file - * times (ticks from DateTime(1/1/1601 00:00 GMT)) - */ - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern static long StartTime_internal(IntPtr handle); - - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] - [MonitoringDescription ("The time this process started.")] - public DateTime StartTime { - get { - return(DateTime.FromFileTime(StartTime_internal(process_handle))); - } - } - - [DefaultValue (null), Browsable (false)] - [MonitoringDescription ("The object that is used to synchronize event handler calls for this process.")] - public ISynchronizeInvoke SynchronizingObject { - get { return synchronizingObject; } - set { synchronizingObject = value; } - } +#endif // !MONO_FEATURE_PROCESS_START [MonoTODO] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] [MonitoringDescription ("The number of threads of this process.")] public ProcessThreadCollection Threads { get { - // This'll return a correctly-sized array of empty ProcessThreads for now. - int error; - return new ProcessThreadCollection(new ProcessThread[GetProcessData (pid, 0, out error)]); - } - } - - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] - [MonitoringDescription ("The total CPU time spent for this process.")] - public TimeSpan TotalProcessorTime { - get { - return new TimeSpan (Times (process_handle, 2)); - } - } + if (threads == null) { + int error; + // This'll return a correctly-sized array of empty ProcessThreads for now. + threads = new ProcessThreadCollection(new ProcessThread [GetProcessData (processId, 0, out error)]); + } - [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] - [MonitoringDescription ("The CPU time spent for this process in user mode.")] - public TimeSpan UserProcessorTime { - get { - return new TimeSpan (Times (process_handle, 0)); + return threads; } } @@ -726,7 +411,7 @@ namespace System.Diagnostics { public int VirtualMemorySize { get { int error; - return (int)GetProcessData (pid, 7, out error); + return (int)GetProcessData (processId, 7, out error); } } @@ -736,7 +421,7 @@ namespace System.Diagnostics { public int WorkingSet { get { int error; - return (int)GetProcessData (pid, 4, out error); + return (int)GetProcessData (processId, 4, out error); } } @@ -746,7 +431,7 @@ namespace System.Diagnostics { public long PrivateMemorySize64 { get { int error; - return GetProcessData (pid, 6, out error); + return GetProcessData (processId, 6, out error); } } @@ -756,7 +441,7 @@ namespace System.Diagnostics { public long VirtualMemorySize64 { get { int error; - return GetProcessData (pid, 7, out error); + return GetProcessData (processId, 7, out error); } } @@ -766,56 +451,23 @@ namespace System.Diagnostics { public long WorkingSet64 { get { int error; - return GetProcessData (pid, 4, out error); + return GetProcessData (processId, 4, out error); } } - public void Close() - { - Dispose (true); - } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - extern static bool Kill_internal (IntPtr handle, int signo); - - /* int kill -> 1 KILL, 2 CloseMainWindow */ - bool Close (int signo) - { - if (process_handle == IntPtr.Zero) - throw new SystemException ("No process to kill."); - - int exitcode = ExitCode_internal (process_handle); - if (exitcode != 259) - throw new InvalidOperationException ("The process already finished."); - - return Kill_internal (process_handle, signo); - } - public bool CloseMainWindow () { - return Close (2); - } - - [MonoTODO] - public static void EnterDebugMode() { + SafeProcessHandle handle = null; + try { + handle = GetProcessHandle (NativeMethods.PROCESS_TERMINATE); + return NativeMethods.TerminateProcess(handle, -2); + } finally { + ReleaseProcessHandle(handle); + } } [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern static IntPtr GetProcess_internal(int pid); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern static int GetPid_internal(); - - public static Process GetCurrentProcess() - { - int pid = GetPid_internal(); - IntPtr proc = GetProcess_internal(pid); - - if (proc == IntPtr.Zero) - throw new SystemException("Can't find current process"); - - return (new Process (proc, pid)); - } public static Process GetProcessById(int processId) { @@ -824,7 +476,7 @@ namespace System.Diagnostics { if (proc == IntPtr.Zero) throw new ArgumentException ("Can't find process with ID " + processId.ToString ()); - return (new Process (proc, processId)); + return (new Process (new SafeProcessHandle (proc, false), processId)); } [MonoTODO ("There is no support for retrieving process information from a remote machine")] @@ -903,51 +555,48 @@ namespace System.Diagnostics { throw new NotImplementedException(); } - public void Kill () + private static bool IsLocalMachine (string machineName) { - Close (1); - } - - [MonoTODO] - public static void LeaveDebugMode() { - } + if (machineName == "." || machineName.Length == 0) + return true; - public void Refresh () - { - // FIXME: should refresh any cached data we might have about - // the process (currently we have none). + return (string.Compare (machineName, Environment.MachineName, true) == 0); } #if MONO_FEATURE_PROCESS_START [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern static bool ShellExecuteEx_internal(ProcessStartInfo startInfo, - ref ProcInfo proc_info); + private extern static bool ShellExecuteEx_internal(ProcessStartInfo startInfo, ref ProcInfo proc_info); [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern static bool CreateProcess_internal(ProcessStartInfo startInfo, - IntPtr stdin, - IntPtr stdout, - IntPtr stderr, - ref ProcInfo proc_info); + private extern static bool CreateProcess_internal(ProcessStartInfo startInfo, IntPtr stdin, IntPtr stdout, IntPtr stderr, ref ProcInfo proc_info); - private static bool Start_shell (ProcessStartInfo startInfo, Process process) + bool StartWithShellExecuteEx (ProcessStartInfo startInfo) { - ProcInfo proc_info=new ProcInfo(); - bool ret; + if (this.disposed) + throw new ObjectDisposedException (GetType ().Name); - if (startInfo.RedirectStandardInput || - startInfo.RedirectStandardOutput || - startInfo.RedirectStandardError) { - throw new InvalidOperationException ("UseShellExecute must be false when redirecting I/O."); - } + if (!String.IsNullOrEmpty(startInfo.UserName) || (startInfo.Password != null)) + throw new InvalidOperationException(SR.GetString(SR.CantStartAsUser)); + + if (startInfo.RedirectStandardInput || startInfo.RedirectStandardOutput || startInfo.RedirectStandardError) + throw new InvalidOperationException(SR.GetString(SR.CantRedirectStreams)); - if (startInfo.HaveEnvVars) - throw new InvalidOperationException ("UseShellExecute must be false in order to use environment variables."); + if (startInfo.StandardErrorEncoding != null) + throw new InvalidOperationException(SR.GetString(SR.StandardErrorEncodingNotAllowed)); + + if (startInfo.StandardOutputEncoding != null) + throw new InvalidOperationException(SR.GetString(SR.StandardOutputEncodingNotAllowed)); + + // can't set env vars with ShellExecuteEx... + if (startInfo.environmentVariables != null) + throw new InvalidOperationException(SR.GetString(SR.CantUseEnvVars)); + + ProcInfo proc_info = new ProcInfo(); + bool ret; FillUserInfo (startInfo, ref proc_info); try { - ret = ShellExecuteEx_internal (startInfo, - ref proc_info); + ret = ShellExecuteEx_internal (startInfo, ref proc_info); } finally { if (proc_info.Password != IntPtr.Zero) Marshal.ZeroFreeBSTR (proc_info.Password); @@ -957,10 +606,13 @@ namespace System.Diagnostics { throw new Win32Exception (-proc_info.pid); } - process.process_handle = proc_info.process_handle; - process.pid = proc_info.pid; - process.StartBackgroundWaitForExit (); - return(ret); + m_processHandle = new SafeProcessHandle (proc_info.process_handle, true); + haveProcessHandle = true; + SetProcessId (proc_info.pid); + + EnsureWatchingForExit (); + + return ret; } // @@ -1012,8 +664,32 @@ namespace System.Diagnostics { } } - static bool Start_noshell (ProcessStartInfo startInfo, Process process) + static bool IsWindows { + get + { + PlatformID platform = Environment.OSVersion.Platform; + if (platform == PlatformID.Win32S || + platform == PlatformID.Win32Windows || + platform == PlatformID.Win32NT || + platform == PlatformID.WinCE) { + return true; + } + return false; + } + } + + bool StartWithCreateProcess (ProcessStartInfo startInfo) + { + if (startInfo.StandardOutputEncoding != null && !startInfo.RedirectStandardOutput) + throw new InvalidOperationException (SR.GetString(SR.StandardOutputEncodingNotAllowed)); + + if (startInfo.StandardErrorEncoding != null && !startInfo.RedirectStandardError) + throw new InvalidOperationException (SR.GetString(SR.StandardErrorEncodingNotAllowed)); + + if (this.disposed) + throw new ObjectDisposedException (GetType ().Name); + var proc_info = new ProcInfo (); if (startInfo.HaveEnvVars) { @@ -1061,11 +737,8 @@ namespace System.Diagnostics { // close them there (fork makes exact copy of parent's descriptors) // if (!CreateProcess_internal (startInfo, stdin_read, stdout_write, stderr_write, ref proc_info)) { - throw new Win32Exception (-proc_info.pid, - "ApplicationName='" + startInfo.FileName + - "', CommandLine='" + startInfo.Arguments + - "', CurrentDirectory='" + startInfo.WorkingDirectory + - "', Native error= " + Win32Exception.W32ErrorMessage (-proc_info.pid)); + throw new Win32Exception (-proc_info.pid, "ApplicationName='" + startInfo.FileName + "', CommandLine='" + startInfo.Arguments + + "', CurrentDirectory='" + startInfo.WorkingDirectory + "', Native error= " + Win32Exception.W32ErrorMessage (-proc_info.pid)); } } catch { if (startInfo.RedirectStandardInput) { @@ -1097,8 +770,9 @@ namespace System.Diagnostics { } } - process.process_handle = proc_info.process_handle; - process.pid = proc_info.pid; + m_processHandle = new SafeProcessHandle (proc_info.process_handle, true); + haveProcessHandle = true; + SetProcessId (proc_info.pid); if (startInfo.RedirectStandardInput) { // @@ -1112,7 +786,7 @@ namespace System.Diagnostics { #else var stdinEncoding = Console.InputEncoding; #endif - process.input_stream = new StreamWriter (new FileStream (stdin_write, FileAccess.Write, true, 8192), stdinEncoding) { + standardInput = new StreamWriter (new FileStream (stdin_write, FileAccess.Write, true, 8192), stdinEncoding) { AutoFlush = true }; } @@ -1122,7 +796,7 @@ namespace System.Diagnostics { Encoding stdoutEncoding = startInfo.StandardOutputEncoding ?? Console.Out.Encoding; - process.output_stream = new StreamReader (new FileStream (stdout_read, FileAccess.Read, true, 8192), stdoutEncoding, true); + standardOutput = new StreamReader (new FileStream (stdout_read, FileAccess.Read, true, 8192), stdoutEncoding, true); } if (startInfo.RedirectStandardError) { @@ -1130,10 +804,10 @@ namespace System.Diagnostics { Encoding stderrEncoding = startInfo.StandardErrorEncoding ?? Console.Out.Encoding; - process.error_stream = new StreamReader (new FileStream (stderr_read, FileAccess.Read, true, 8192), stderrEncoding, true); + standardError = new StreamReader (new FileStream (stderr_read, FileAccess.Read, true, 8192), stderrEncoding, true); } - process.StartBackgroundWaitForExit (); + EnsureWatchingForExit (); return true; } @@ -1151,70 +825,6 @@ namespace System.Diagnostics { proc_info.LoadUserProfile = startInfo.LoadUserProfile; } } - - private static bool Start_common (ProcessStartInfo startInfo, - Process process) - { - if (startInfo.FileName.Length == 0) - throw new InvalidOperationException("File name has not been set"); - - if (startInfo.StandardErrorEncoding != null && !startInfo.RedirectStandardError) - throw new InvalidOperationException ("StandardErrorEncoding is only supported when standard error is redirected"); - if (startInfo.StandardOutputEncoding != null && !startInfo.RedirectStandardOutput) - throw new InvalidOperationException ("StandardOutputEncoding is only supported when standard output is redirected"); - - if (startInfo.UseShellExecute) { - if (startInfo.UserName.Length != 0) - throw new InvalidOperationException ("UseShellExecute must be false if an explicit UserName is specified when starting a process"); - return (Start_shell (startInfo, process)); - } else { - return (Start_noshell (startInfo, process)); - } - } - - public bool Start () - { - if (process_handle != IntPtr.Zero) { - Process_free_internal (process_handle); - process_handle = IntPtr.Zero; - } - return Start_common(start_info, this); - } - - public static Process Start (ProcessStartInfo startInfo) - { - if (startInfo == null) - throw new ArgumentNullException ("startInfo"); - - Process process = new Process(); - process.StartInfo = startInfo; - if (Start_common(startInfo, process) && process.process_handle != IntPtr.Zero) - return process; - return null; - } - - public static Process Start (string fileName) - { - return Start (new ProcessStartInfo (fileName)); - } - - public static Process Start(string fileName, string arguments) - { - return Start (new ProcessStartInfo (fileName, arguments)); - } - - public static Process Start(string fileName, string username, SecureString password, string domain) { - return Start(fileName, null, username, password, domain); - } - - public static Process Start(string fileName, string arguments, string username, SecureString password, string domain) { - ProcessStartInfo psi = new ProcessStartInfo(fileName, arguments); - psi.UserName = username; - psi.Password = password; - psi.Domain = domain; - psi.UseShellExecute = false; - return Start(psi); - } #else [Obsolete ("Process.Start is not supported on the current platform.", true)] public bool Start () @@ -1253,192 +863,7 @@ namespace System.Diagnostics { } #endif // MONO_FEATURE_PROCESS_START - public override string ToString() - { - return(base.ToString() + " (" + this.ProcessName + ")"); - } - - /* Waits up to ms milliseconds for process 'handle' to - * exit. ms can be <0 to mean wait forever. - */ - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern bool WaitForExit_internal(IntPtr handle, int ms); - - public void WaitForExit () - { - WaitForExit (-1); - } - - public bool WaitForExit(int milliseconds) { - int ms = milliseconds; - if (ms == int.MaxValue) - ms = -1; - - if (process_handle == IntPtr.Zero) - throw new InvalidOperationException ("No process is associated with this object."); - - if (!WaitForExit_internal (process_handle, ms)) - return false; - -#if MONO_FEATURE_PROCESS_START - if (async_output != null) - async_output.WaitUtilEOF (); - - if (async_error != null) - async_error.WaitUtilEOF (); -#endif // MONO_FEATURE_PROCESS_START - - if (EnableRaisingEvents) - OnExited (); - - return true; - } - - /* Waits up to ms milliseconds for process 'handle' to - * wait for input. ms can be <0 to mean wait forever. - */ - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern bool WaitForInputIdle_internal(IntPtr handle, int ms); - - // The internal call is only implemented properly on Windows. - [MonoTODO] - public bool WaitForInputIdle() { - return WaitForInputIdle (-1); - } - - // The internal call is only implemented properly on Windows. - [MonoTODO] - public bool WaitForInputIdle(int milliseconds) { - return WaitForInputIdle_internal (process_handle, milliseconds); - } - - private static bool IsLocalMachine (string machineName) - { - if (machineName == "." || machineName.Length == 0) - return true; - - return (string.Compare (machineName, Environment.MachineName, true) == 0); - } - - [Browsable (true)] - [MonitoringDescription ("Raised when it receives output data")] - public event DataReceivedEventHandler OutputDataReceived; - [Browsable (true)] - [MonitoringDescription ("Raised when it receives error data")] - public event DataReceivedEventHandler ErrorDataReceived; - -#if MONO_FEATURE_PROCESS_START - [Flags] - enum AsyncModes { - NoneYet = 0, - SyncOutput = 1, - SyncError = 1 << 1, - AsyncOutput = 1 << 2, - AsyncError = 1 << 3 - } - - AsyncModes async_mode; - AsyncStreamReader async_output; - AsyncStreamReader async_error; - - [ComVisibleAttribute(false)] - public void BeginOutputReadLine () - { - if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false) - throw new InvalidOperationException ("Standard output has not been redirected or process has not been started."); - - if ((async_mode & AsyncModes.SyncOutput) != 0) - throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads."); - - if ((async_mode & AsyncModes.AsyncOutput) != 0) - throw new InvalidOperationException ("An async read operation has already been started on the stream."); - - async_mode |= AsyncModes.AsyncOutput; - - if (async_output == null) - async_output = new AsyncStreamReader (this, output_stream.BaseStream, new UserCallBack(this.OutputReadNotifyUser), output_stream.CurrentEncoding); - - async_output.BeginReadLine (); - } - - void OutputReadNotifyUser (String data) - { - // To avoid ---- between remove handler and raising the event - DataReceivedEventHandler outputDataReceived = OutputDataReceived; - if (outputDataReceived != null) { - if (SynchronizingObject != null && SynchronizingObject.InvokeRequired) - SynchronizingObject.Invoke (outputDataReceived, new object[] { this, new DataReceivedEventArgs (data) }); - else - outputDataReceived (this, new DataReceivedEventArgs (data)); // Call back to user informing data is available. - } - } - - [ComVisibleAttribute(false)] - public void CancelOutputRead () - { - if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false) - throw new InvalidOperationException ("Standard output has not been redirected or process has not been started."); - - if ((async_mode & AsyncModes.SyncOutput) != 0) - throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations."); - - if (async_output == null) - throw new InvalidOperationException ("No async operation in progress."); - - async_output.CancelOperation (); - - async_mode &= ~AsyncModes.AsyncOutput; - } - - [ComVisibleAttribute(false)] - public void BeginErrorReadLine () - { - if (process_handle == IntPtr.Zero || error_stream == null || StartInfo.RedirectStandardError == false) - throw new InvalidOperationException ("Standard error has not been redirected or process has not been started."); - - if ((async_mode & AsyncModes.SyncError) != 0) - throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads."); - - if ((async_mode & AsyncModes.AsyncError) != 0) - throw new InvalidOperationException ("An async read operation has already been started on the stream."); - - async_mode |= AsyncModes.AsyncError; - - if (async_error == null) - async_error = new AsyncStreamReader (this, error_stream.BaseStream, new UserCallBack(this.ErrorReadNotifyUser), error_stream.CurrentEncoding); - - async_error.BeginReadLine (); - } - - void ErrorReadNotifyUser (String data) - { - // To avoid ---- between remove handler and raising the event - DataReceivedEventHandler errorDataReceived = ErrorDataReceived; - if (errorDataReceived != null) { - if (SynchronizingObject != null && SynchronizingObject.InvokeRequired) - SynchronizingObject.Invoke (errorDataReceived, new object[] { this, new DataReceivedEventArgs (data) }); - else - errorDataReceived (this, new DataReceivedEventArgs (data)); // Call back to user informing data is available. - } - } - - [ComVisibleAttribute(false)] - public void CancelErrorRead () - { - if (process_handle == IntPtr.Zero || error_stream == null || StartInfo.RedirectStandardError == false) - throw new InvalidOperationException ("Standard error has not been redirected or process has not been started."); - - if ((async_mode & AsyncModes.SyncOutput) != 0) - throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations."); - - if (async_error == null) - throw new InvalidOperationException ("No async operation in progress."); - - async_error.CancelOperation (); - - async_mode &= ~AsyncModes.AsyncError; - } -#else +#if !MONO_FEATURE_PROCESS_START [Obsolete ("Process.BeginOutputReadLine is not supported on the current platform.", true)] public void BeginOutputReadLine () { @@ -1462,156 +887,24 @@ namespace System.Diagnostics { { throw new PlatformNotSupportedException ("Process.BeginOutputReadLine is not supported on the current platform."); } -#endif // MONO_FEATURE_PROCESS_START - - [Category ("Behavior")] - [MonitoringDescription ("Raised when this process exits.")] - public event EventHandler Exited { - add { - if (process_handle != IntPtr.Zero && HasExited) { - value.BeginInvoke (null, null, null, null); - } else { - exited_event += value; - if (exited_event != null) - StartBackgroundWaitForExit (); - } - } - remove { - exited_event -= value; - } - } - - // Closes the system process handle - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern void Process_free_internal(IntPtr handle); - - int disposed; - - protected override void Dispose(bool disposing) { - // Check to see if Dispose has already been called. - if (disposed != 0 || Interlocked.CompareExchange (ref disposed, 1, 0) != 0) - return; - - // If this is a call to Dispose, - // dispose all managed resources. - if (disposing) { -#if MONO_FEATURE_PROCESS_START - async_output = null; - async_error = null; - - if (input_stream != null) { - if (!input_stream_exposed) - input_stream.Close (); - input_stream = null; - } - if (output_stream != null) { - if (!output_stream_exposed) - output_stream.Close (); - output_stream = null; - } - if (error_stream != null) { - if (!error_stream_exposed) - error_stream.Close (); - error_stream = null; - } -#endif // MONO_FEATURE_PROCESS_START - } - - // Release unmanaged resources - - if (process_handle!=IntPtr.Zero) { - Process_free_internal (process_handle); - process_handle = IntPtr.Zero; - } - - base.Dispose (disposing); - } - - ~Process () - { - Dispose (false); - } - - int on_exited_called = 0; +#endif // !MONO_FEATURE_PROCESS_START - protected void OnExited() - { - if (on_exited_called != 0 || Interlocked.CompareExchange (ref on_exited_called, 1, 0) != 0) - return; - - var cb = exited_event; - if (cb == null) + /// <devdoc> + /// Raise the Exited event, but make sure we don't do it more than once. + /// </devdoc> + /// <internalonly/> + void RaiseOnExited() { + if (!watchForExit) return; - - if (synchronizingObject != null) { - synchronizingObject.BeginInvoke (cb, new object [] { this, EventArgs.Empty }); - } else { - foreach (EventHandler d in cb.GetInvocationList ()) { - try { - d (this, EventArgs.Empty); - } catch { + if (!raisedOnExited) { + lock (this) { + if (!raisedOnExited) { + raisedOnExited = true; + OnExited(); } } } } - - static bool IsWindows - { - get - { - PlatformID platform = Environment.OSVersion.Platform; - if (platform == PlatformID.Win32S || - platform == PlatformID.Win32Windows || - platform == PlatformID.Win32NT || - platform == PlatformID.WinCE) { - return true; - } - return false; - } - } - - void StartBackgroundWaitForExit () - { - IntPtr handle = process_handle; - - if (enable_raising_events == 0) - return; - if (exited_event == null) - return; - if (handle == IntPtr.Zero) - return; - if (background_wait_for_exit_thread != null) - return; - - Thread t = new Thread (_ => { - if (!WaitForExit_internal (handle, -1)) - return; - - if (EnableRaisingEvents) - OnExited (); - }); - - t.IsBackground = true; - - if (Interlocked.CompareExchange (ref background_wait_for_exit_thread, t, null) == null) - t.Start (); - } - - class ProcessWaitHandle : WaitHandle - { - [MethodImplAttribute (MethodImplOptions.InternalCall)] - private extern static IntPtr ProcessHandle_duplicate (IntPtr handle); - - public ProcessWaitHandle (IntPtr handle) - { - // Need to keep a reference to this handle, - // in case the Process object is collected - Handle = ProcessHandle_duplicate (handle); - - // When the wait handle is disposed, the duplicated handle will be - // closed, so no need to override dispose (bug #464628). - } - } } } |