2 // System.Diagnostics.Process.cs
5 // Dick Porter (dick@ximian.com)
6 // Andreas Nahr (ClassDevelopment@A-SoftTech.com)
7 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
9 // (C) 2002 Ximian, Inc.
10 // (C) 2003 Andreas Nahr
11 // (c) 2004,2005,2006 Novell, Inc. (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 using System.ComponentModel;
38 using System.ComponentModel.Design;
39 using System.Runtime.CompilerServices;
40 using System.Runtime.InteropServices;
41 using System.Security.Permissions;
42 using System.Collections;
43 using System.Security;
44 using System.Threading;
46 namespace System.Diagnostics {
48 [DefaultEvent ("Exited"), DefaultProperty ("StartInfo")]
49 [Designer ("System.Diagnostics.Design.ProcessDesigner, " + Consts.AssemblySystem_Design)]
50 [PermissionSet (SecurityAction.LinkDemand, Unrestricted = true)]
51 [PermissionSet (SecurityAction.InheritanceDemand, Unrestricted = true)]
53 [MonitoringDescription ("Represents a system process")]
55 public class Process : Component
57 [StructLayout(LayoutKind.Sequential)]
58 private struct ProcInfo
60 public IntPtr process_handle;
61 /* If thread_handle is ever needed for
62 * something, take out the CloseHandle() in
63 * the Start_internal icall in
64 * mono/metadata/process.c
66 public IntPtr thread_handle;
67 public int pid; // Contains -GetLastError () on failure.
69 public string [] envKeys;
70 public string [] envValues;
71 public string UserName;
73 public IntPtr Password;
74 public bool LoadUserProfile;
77 IntPtr process_handle;
79 bool enableRaisingEvents;
81 ISynchronizeInvoke synchronizingObject;
82 EventHandler exited_event;
86 /* Private constructor called from other methods */
87 private Process(IntPtr handle, int id) {
88 process_handle=handle;
97 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
98 [MonitoringDescription ("Base process priority.")]
99 public int BasePriority {
105 void StartExitCallbackIfNeeded ()
108 bool start = (!already_waiting && enableRaisingEvents && exited_event != null);
109 if (start && process_handle != IntPtr.Zero && !HasExited) {
110 WaitOrTimerCallback cb = new WaitOrTimerCallback (CBOnExit);
111 ProcessWaitHandle h = new ProcessWaitHandle (process_handle);
112 ThreadPool.RegisterWaitForSingleObject (h, cb, this, -1, true);
113 already_waiting = true;
118 [DefaultValue (false), Browsable (false)]
119 [MonitoringDescription ("Check for exiting of the process to raise the apropriate event.")]
120 public bool EnableRaisingEvents {
122 return enableRaisingEvents;
125 bool prev = enableRaisingEvents;
126 enableRaisingEvents = value;
127 if (enableRaisingEvents && !prev)
128 StartExitCallbackIfNeeded ();
133 [MethodImplAttribute(MethodImplOptions.InternalCall)]
134 private extern static int ExitCode_internal(IntPtr handle);
136 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
137 [MonitoringDescription ("The exit code of the process.")]
138 public int ExitCode {
140 if (process_handle == IntPtr.Zero)
141 throw new InvalidOperationException ("Process has not been started.");
143 int code = ExitCode_internal (process_handle);
145 throw new InvalidOperationException ("The process must exit before " +
146 "getting the requested information.");
152 /* Returns the process start time in Windows file
153 * times (ticks from DateTime(1/1/1601 00:00 GMT))
155 [MethodImplAttribute(MethodImplOptions.InternalCall)]
156 private extern static long ExitTime_internal(IntPtr handle);
158 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
159 [MonitoringDescription ("The exit time of the process.")]
160 public DateTime ExitTime {
162 if (process_handle == IntPtr.Zero)
163 throw new InvalidOperationException ("Process has not been started.");
166 throw new InvalidOperationException ("The process must exit before " +
167 "getting the requested information.");
169 return(DateTime.FromFileTime(ExitTime_internal(process_handle)));
173 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
174 [MonitoringDescription ("Handle for this process.")]
175 public IntPtr Handle {
177 return(process_handle);
182 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
183 [MonitoringDescription ("Handles for this process.")]
184 public int HandleCount {
190 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
191 [MonitoringDescription ("Determines if the process is still running.")]
192 public bool HasExited {
194 if (process_handle == IntPtr.Zero)
195 throw new InvalidOperationException ("Process has not been started.");
197 int exitcode = ExitCode_internal (process_handle);
208 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
209 [MonitoringDescription ("Process identifier.")]
213 throw new InvalidOperationException ("Process ID has not been set.");
221 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
222 [MonitoringDescription ("The name of the computer running the process.")]
223 public string MachineName {
229 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
230 [MonitoringDescription ("The main module of the process.")]
231 public ProcessModule MainModule {
233 return(this.Modules[0]);
238 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
239 [MonitoringDescription ("The handle of the main window of the process.")]
240 public IntPtr MainWindowHandle {
247 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
248 [MonitoringDescription ("The title of the main window of the process.")]
249 public string MainWindowTitle {
255 [MethodImplAttribute(MethodImplOptions.InternalCall)]
256 private extern static bool GetWorkingSet_internal(IntPtr handle, out int min, out int max);
257 [MethodImplAttribute(MethodImplOptions.InternalCall)]
258 private extern static bool SetWorkingSet_internal(IntPtr handle, int min, int max, bool use_min);
260 /* LAMESPEC: why is this an IntPtr not a plain int? */
261 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
262 [MonitoringDescription ("The maximum working set for this process.")]
263 public IntPtr MaxWorkingSet {
266 throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
271 bool ok=GetWorkingSet_internal(process_handle, out min, out max);
273 throw new Win32Exception();
280 throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
283 bool ok=SetWorkingSet_internal(process_handle, 0, value.ToInt32(), false);
285 throw new Win32Exception();
290 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
291 [MonitoringDescription ("The minimum working set for this process.")]
292 public IntPtr MinWorkingSet {
295 throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
300 bool ok=GetWorkingSet_internal(process_handle, out min, out max);
302 throw new Win32Exception();
309 throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
312 bool ok=SetWorkingSet_internal(process_handle, value.ToInt32(), 0, true);
314 throw new Win32Exception();
319 /* Returns the list of process modules. The main module is
322 [MethodImplAttribute(MethodImplOptions.InternalCall)]
323 private extern ProcessModule[] GetModules_internal();
325 private ProcessModuleCollection module_collection;
327 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
328 [MonitoringDescription ("The modules that are loaded as part of this process.")]
329 public ProcessModuleCollection Modules {
331 if(module_collection==null) {
332 module_collection=new ProcessModuleCollection(GetModules_internal());
335 return(module_collection);
341 [Obsolete ("Use NonpagedSystemMemorySize64")]
343 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
344 [MonitoringDescription ("The number of bytes that are not pageable.")]
345 public int NonpagedSystemMemorySize {
353 [Obsolete ("Use PagedMemorySize64")]
355 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
356 [MonitoringDescription ("The number of bytes that are paged.")]
357 public int PagedMemorySize {
365 [Obsolete ("Use PagedSystemMemorySize64")]
367 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
368 [MonitoringDescription ("The amount of paged system memory in bytes.")]
369 public int PagedSystemMemorySize {
377 [Obsolete ("Use PeakPagedMemorySize64")]
379 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
380 [MonitoringDescription ("The maximum amount of paged memory used by this process.")]
381 public int PeakPagedMemorySize {
389 [Obsolete ("Use PeakVirtualMemorySize64")]
391 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
392 [MonitoringDescription ("The maximum amount of virtual memory used by this process.")]
393 public int PeakVirtualMemorySize {
401 [Obsolete ("Use PeakWorkingSet64")]
403 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
404 [MonitoringDescription ("The maximum amount of system memory used by this process.")]
405 public int PeakWorkingSet {
413 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
414 [MonitoringDescription ("The number of bytes that are not pageable.")]
416 public long NonpagedSystemMemorySize64 {
423 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
424 [MonitoringDescription ("The number of bytes that are paged.")]
426 public long PagedMemorySize64 {
433 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
434 [MonitoringDescription ("The amount of paged system memory in bytes.")]
436 public long PagedSystemMemorySize64 {
443 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
444 [MonitoringDescription ("The maximum amount of paged memory used by this process.")]
446 public long PeakPagedMemorySize64 {
453 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
454 [MonitoringDescription ("The maximum amount of virtual memory used by this process.")]
456 public long PeakVirtualMemorySize64 {
463 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
464 [MonitoringDescription ("The maximum amount of system memory used by this process.")]
466 public long PeakWorkingSet64 {
474 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
475 [MonitoringDescription ("Process will be of higher priority while it is actively used.")]
476 public bool PriorityBoostEnabled {
484 [MonoLimitation ("Under Unix, only root is allowed to raise the priority.")]
485 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
486 [MonitoringDescription ("The relative process priority.")]
487 public ProcessPriorityClass PriorityClass {
490 int prio = GetPriorityClass (process_handle, out error);
492 throw new Win32Exception (error);
493 return (ProcessPriorityClass) prio;
496 // LAMESPEC: not documented on MSDN for NET_1_1
497 if (!Enum.IsDefined (typeof (ProcessPriorityClass), value))
498 throw new InvalidEnumArgumentException ();
501 if (!SetPriorityClass (process_handle, (int) value, out error))
502 throw new Win32Exception (error);
506 [MethodImplAttribute(MethodImplOptions.InternalCall)]
507 static extern int GetPriorityClass (IntPtr handle, out int error);
509 [MethodImplAttribute(MethodImplOptions.InternalCall)]
510 static extern bool SetPriorityClass (IntPtr handle, int priority, out int error);
513 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
514 [MonitoringDescription ("The amount of memory exclusively used by this process.")]
516 [Obsolete ("Use PrivateMemorySize64")]
518 public int PrivateMemorySize {
524 /* the meaning of type is as follows: 0: user, 1: system, 2: total */
525 [MethodImplAttribute(MethodImplOptions.InternalCall)]
526 private extern static long Times (IntPtr handle, int type);
528 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
529 [MonitoringDescription ("The amount of processing time spent in the OS core for this process.")]
530 public TimeSpan PrivilegedProcessorTime {
532 return new TimeSpan (Times (process_handle, 1));
536 [MethodImplAttribute(MethodImplOptions.InternalCall)]
537 private extern static string ProcessName_internal(IntPtr handle);
539 private string process_name=null;
541 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
542 [MonitoringDescription ("The name of this process.")]
543 public string ProcessName {
545 if(process_name==null) {
546 process_name=ProcessName_internal(process_handle);
547 /* If process_name is _still_
548 * null, assume the process
551 if(process_name==null) {
552 throw new SystemException("The process has exited");
555 /* Strip the suffix (if it
556 * exists) simplistically
557 * instead of removing any
558 * trailing \.???, so we dont
559 * get stupid results on sane
562 if(process_name.EndsWith(".exe") ||
563 process_name.EndsWith(".bat") ||
564 process_name.EndsWith(".com")) {
565 process_name=process_name.Substring(0, process_name.Length-4);
568 return(process_name);
573 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
574 [MonitoringDescription ("Allowed processor that can be used by this process.")]
575 public IntPtr ProcessorAffinity {
584 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
585 [MonitoringDescription ("Is this process responsive.")]
586 public bool Responding {
592 private StreamReader error_stream=null;
594 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
595 [MonitoringDescription ("The standard error stream of this process.")]
596 public StreamReader StandardError {
598 if (error_stream == null) {
599 throw new InvalidOperationException("Standard error has not been redirected");
602 if ((async_mode & AsyncModes.AsyncError) != 0)
603 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
605 async_mode |= AsyncModes.SyncError;
608 return(error_stream);
612 private StreamWriter input_stream=null;
614 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
615 [MonitoringDescription ("The standard input stream of this process.")]
616 public StreamWriter StandardInput {
618 if (input_stream == null) {
619 throw new InvalidOperationException("Standard input has not been redirected");
622 return(input_stream);
626 private StreamReader output_stream=null;
628 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
629 [MonitoringDescription ("The standard output stream of this process.")]
630 public StreamReader StandardOutput {
632 if (output_stream == null) {
633 throw new InvalidOperationException("Standard output has not been redirected");
636 if ((async_mode & AsyncModes.AsyncOutput) != 0)
637 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
639 async_mode |= AsyncModes.SyncOutput;
642 return(output_stream);
646 private ProcessStartInfo start_info=null;
648 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content), Browsable (false)]
649 [MonitoringDescription ("Information for the start of this process.")]
650 public ProcessStartInfo StartInfo {
652 if(start_info==null) {
653 start_info=new ProcessStartInfo();
660 throw new ArgumentException("value is null");
667 /* Returns the process start time in Windows file
668 * times (ticks from DateTime(1/1/1601 00:00 GMT))
670 [MethodImplAttribute(MethodImplOptions.InternalCall)]
671 private extern static long StartTime_internal(IntPtr handle);
673 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
674 [MonitoringDescription ("The time this process started.")]
675 public DateTime StartTime {
677 return(DateTime.FromFileTime(StartTime_internal(process_handle)));
681 [DefaultValue (null), Browsable (false)]
682 [MonitoringDescription ("The object that is used to synchronize event handler calls for this process.")]
683 public ISynchronizeInvoke SynchronizingObject {
684 get { return synchronizingObject; }
685 set { synchronizingObject = value; }
689 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
690 [MonitoringDescription ("The number of threads of this process.")]
691 public ProcessThreadCollection Threads {
697 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
698 [MonitoringDescription ("The total CPU time spent for this process.")]
699 public TimeSpan TotalProcessorTime {
701 return new TimeSpan (Times (process_handle, 2));
705 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
706 [MonitoringDescription ("The CPU time spent for this process in user mode.")]
707 public TimeSpan UserProcessorTime {
709 return new TimeSpan (Times (process_handle, 0));
715 [Obsolete ("Use VirtualMemorySize64")]
717 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
718 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
719 public int VirtualMemorySize {
727 [Obsolete ("Use WorkingSet64")]
729 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
730 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
731 public int WorkingSet {
739 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
740 [MonitoringDescription ("The amount of memory exclusively used by this process.")]
742 public long PrivateMemorySize64 {
749 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
750 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
752 public long VirtualMemorySize64 {
759 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
760 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
762 public long WorkingSet64 {
774 [MethodImplAttribute(MethodImplOptions.InternalCall)]
775 extern static bool Kill_internal (IntPtr handle, int signo);
777 /* int kill -> 1 KILL, 2 CloseMainWindow */
778 bool Close (int signo)
780 if (process_handle == IntPtr.Zero)
781 throw new SystemException ("No process to kill.");
783 int exitcode = ExitCode_internal (process_handle);
785 throw new InvalidOperationException ("The process already finished.");
787 return Kill_internal (process_handle, signo);
790 public bool CloseMainWindow ()
796 public static void EnterDebugMode() {
799 [MethodImplAttribute(MethodImplOptions.InternalCall)]
800 private extern static IntPtr GetProcess_internal(int pid);
802 [MethodImplAttribute(MethodImplOptions.InternalCall)]
803 private extern static int GetPid_internal();
805 public static Process GetCurrentProcess()
807 int pid = GetPid_internal();
808 IntPtr proc = GetProcess_internal(pid);
810 if (proc == IntPtr.Zero) {
811 throw new SystemException("Can't find current process");
814 return (new Process (proc, pid));
817 public static Process GetProcessById(int processId)
819 IntPtr proc = GetProcess_internal(processId);
821 if (proc == IntPtr.Zero) {
822 throw new ArgumentException ("Can't find process with ID " + processId.ToString ());
825 return (new Process (proc, processId));
828 [MonoTODO ("There is no support for retrieving process information from a remote machine")]
829 public static Process GetProcessById(int processId, string machineName) {
830 if (machineName == null)
831 throw new ArgumentNullException ("machineName");
833 if (!IsLocalMachine (machineName))
834 throw new NotImplementedException ();
836 return GetProcessById (processId);
839 [MethodImplAttribute(MethodImplOptions.InternalCall)]
840 private extern static int[] GetProcesses_internal();
842 public static Process[] GetProcesses()
844 int [] pids = GetProcesses_internal ();
845 ArrayList proclist = new ArrayList ();
847 for (int i = 0; i < pids.Length; i++) {
849 proclist.Add (GetProcessById (pids [i]));
850 } catch (SystemException) {
851 /* The process might exit
853 * GetProcesses_internal and
859 return ((Process []) proclist.ToArray (typeof (Process)));
862 [MonoTODO ("There is no support for retrieving process information from a remote machine")]
863 public static Process[] GetProcesses(string machineName) {
864 if (machineName == null)
865 throw new ArgumentNullException ("machineName");
867 if (!IsLocalMachine (machineName))
868 throw new NotImplementedException ();
870 return GetProcesses ();
873 public static Process[] GetProcessesByName(string processName)
875 Process [] procs = GetProcesses();
876 ArrayList proclist = new ArrayList();
878 for (int i = 0; i < procs.Length; i++) {
880 if (String.Compare (processName,
881 procs [i].ProcessName,
883 proclist.Add (procs [i]);
887 return ((Process[]) proclist.ToArray (typeof(Process)));
891 public static Process[] GetProcessesByName(string processName, string machineName) {
892 throw new NotImplementedException();
901 public static void LeaveDebugMode() {
904 public void Refresh ()
906 // FIXME: should refresh any cached data we might have about
907 // the process (currently we have none).
910 [MethodImplAttribute(MethodImplOptions.InternalCall)]
911 private extern static bool ShellExecuteEx_internal(ProcessStartInfo startInfo,
912 ref ProcInfo proc_info);
914 [MethodImplAttribute(MethodImplOptions.InternalCall)]
915 private extern static bool CreateProcess_internal(ProcessStartInfo startInfo,
919 ref ProcInfo proc_info);
921 private static bool Start_shell (ProcessStartInfo startInfo,
924 ProcInfo proc_info=new ProcInfo();
927 if (startInfo.RedirectStandardInput ||
928 startInfo.RedirectStandardOutput ||
929 startInfo.RedirectStandardError) {
930 throw new InvalidOperationException ("UseShellExecute must be false when redirecting I/O.");
933 if (startInfo.HaveEnvVars) {
934 throw new InvalidOperationException ("UseShellExecute must be false in order to use environment variables.");
937 FillUserInfo (startInfo, ref proc_info);
939 ret = ShellExecuteEx_internal (startInfo,
942 if (proc_info.Password != IntPtr.Zero)
943 Marshal.FreeBSTR (proc_info.Password);
944 proc_info.Password = IntPtr.Zero;
947 throw new Win32Exception (-proc_info.pid);
950 process.process_handle = proc_info.process_handle;
951 process.pid = proc_info.pid;
953 process.StartExitCallbackIfNeeded ();
958 private static bool Start_noshell (ProcessStartInfo startInfo,
961 if (Path.IsPathRooted (startInfo.FileName) && !File.Exists (startInfo.FileName))
962 throw new FileNotFoundException ("Executable not found: " + startInfo.FileName);
963 ProcInfo proc_info=new ProcInfo();
964 IntPtr stdin_rd, stdin_wr;
970 if (startInfo.HaveEnvVars) {
971 string [] strs = new string [startInfo.EnvironmentVariables.Count];
972 startInfo.EnvironmentVariables.Keys.CopyTo (strs, 0);
973 proc_info.envKeys = strs;
975 strs = new string [startInfo.EnvironmentVariables.Count];
976 startInfo.EnvironmentVariables.Values.CopyTo (strs, 0);
977 proc_info.envValues = strs;
980 if (startInfo.RedirectStandardInput == true) {
981 ret = MonoIO.CreatePipe (out stdin_rd,
984 throw new IOException ("Error creating standard input pipe");
987 stdin_rd = MonoIO.ConsoleInput;
988 /* This is required to stop the
989 * &$*£ing stupid compiler moaning
990 * that stdin_wr is unassigned, below.
992 stdin_wr = (IntPtr)0;
995 if (startInfo.RedirectStandardOutput == true) {
997 ret = MonoIO.CreatePipe (out out_rd,
1000 process.stdout_rd = out_rd;
1002 if (startInfo.RedirectStandardInput == true) {
1003 MonoIO.Close (stdin_rd, out error);
1004 MonoIO.Close (stdin_wr, out error);
1007 throw new IOException ("Error creating standard output pipe");
1010 process.stdout_rd = (IntPtr)0;
1011 stdout_wr = MonoIO.ConsoleOutput;
1014 if (startInfo.RedirectStandardError == true) {
1016 ret = MonoIO.CreatePipe (out err_rd,
1019 process.stderr_rd = err_rd;
1021 if (startInfo.RedirectStandardInput == true) {
1022 MonoIO.Close (stdin_rd, out error);
1023 MonoIO.Close (stdin_wr, out error);
1025 if (startInfo.RedirectStandardOutput == true) {
1026 MonoIO.Close (process.stdout_rd, out error);
1027 MonoIO.Close (stdout_wr, out error);
1030 throw new IOException ("Error creating standard error pipe");
1033 process.stderr_rd = (IntPtr)0;
1034 stderr_wr = MonoIO.ConsoleError;
1037 FillUserInfo (startInfo, ref proc_info);
1039 ret = CreateProcess_internal (startInfo,
1040 stdin_rd, stdout_wr, stderr_wr,
1043 if (proc_info.Password != IntPtr.Zero)
1044 Marshal.FreeBSTR (proc_info.Password);
1045 proc_info.Password = IntPtr.Zero;
1048 if (startInfo.RedirectStandardInput == true) {
1049 MonoIO.Close (stdin_rd, out error);
1050 MonoIO.Close (stdin_wr, out error);
1053 if (startInfo.RedirectStandardOutput == true) {
1054 MonoIO.Close (process.stdout_rd, out error);
1055 MonoIO.Close (stdout_wr, out error);
1058 if (startInfo.RedirectStandardError == true) {
1059 MonoIO.Close (process.stderr_rd, out error);
1060 MonoIO.Close (stderr_wr, out error);
1063 throw new Win32Exception (-proc_info.pid,
1064 "ApplicationName='"+startInfo.FileName+
1065 "', CommandLine='"+startInfo.Arguments+
1066 "', CurrentDirectory='"+startInfo.WorkingDirectory+
1067 "', PATH='"+startInfo.EnvironmentVariables["PATH"]+"'");
1070 process.process_handle = proc_info.process_handle;
1071 process.pid = proc_info.pid;
1073 if (startInfo.RedirectStandardInput == true) {
1074 MonoIO.Close (stdin_rd, out error);
1075 process.input_stream = new StreamWriter (new FileStream (stdin_wr, FileAccess.Write, true), Console.Out.Encoding);
1076 process.input_stream.AutoFlush = true;
1080 Encoding stdoutEncoding = startInfo.StandardOutputEncoding ?? Console.Out.Encoding;
1081 Encoding stderrEncoding = startInfo.StandardErrorEncoding ?? Console.Out.Encoding;
1083 Encoding stdoutEncoding = Console.Out.Encoding;
1084 Encoding stderrEncoding = stdoutEncoding;
1087 if (startInfo.RedirectStandardOutput == true) {
1088 MonoIO.Close (stdout_wr, out error);
1089 process.output_stream = new StreamReader (new FileStream (process.stdout_rd, FileAccess.Read, true), stdoutEncoding);
1092 if (startInfo.RedirectStandardError == true) {
1093 MonoIO.Close (stderr_wr, out error);
1094 process.error_stream = new StreamReader (new FileStream (process.stderr_rd, FileAccess.Read, true), stderrEncoding);
1097 process.StartExitCallbackIfNeeded ();
1102 // Note that ProcInfo.Password must be freed.
1103 private static void FillUserInfo (ProcessStartInfo startInfo, ref ProcInfo proc_info)
1106 if (startInfo.UserName != null) {
1107 proc_info.UserName = startInfo.UserName;
1108 proc_info.Domain = startInfo.Domain;
1109 if (startInfo.Password != null)
1110 proc_info.Password = Marshal.SecureStringToBSTR (startInfo.Password);
1112 proc_info.Password = IntPtr.Zero;
1113 proc_info.LoadUserProfile = startInfo.LoadUserProfile;
1118 private static bool Start_common (ProcessStartInfo startInfo,
1121 if(startInfo.FileName == null ||
1122 startInfo.FileName == "") {
1123 throw new InvalidOperationException("File name has not been set");
1127 if (startInfo.StandardErrorEncoding != null && !startInfo.RedirectStandardError)
1128 throw new InvalidOperationException ("StandardErrorEncoding is only supported when standard error is redirected");
1129 if (startInfo.StandardOutputEncoding != null && !startInfo.RedirectStandardOutput)
1130 throw new InvalidOperationException ("StandardOutputEncoding is only supported when standard output is redirected");
1133 if (startInfo.UseShellExecute) {
1135 if (startInfo.UserName != null)
1136 throw new InvalidOperationException ("UserShellExecute must be false if an explicit UserName is specified when starting a process");
1138 return (Start_shell (startInfo, process));
1140 return (Start_noshell (startInfo, process));
1144 public bool Start() {
1147 if (process_handle != IntPtr.Zero) {
1148 Process_free_internal (process_handle);
1149 process_handle = IntPtr.Zero;
1151 ret=Start_common(start_info, this);
1156 public static Process Start(ProcessStartInfo startInfo) {
1158 Process process=new Process();
1161 process.StartInfo = startInfo;
1162 ret=Start_common(startInfo, process);
1171 public static Process Start(string fileName) {
1172 return Start(new ProcessStartInfo(fileName));
1175 public static Process Start(string fileName,
1177 return Start(new ProcessStartInfo(fileName, arguments));
1181 public static Process Start(string fileName, string username, SecureString password, string domain) {
1182 return Start(fileName, null, username, password, domain);
1185 public static Process Start(string fileName, string arguments, string username, SecureString password, string domain) {
1186 ProcessStartInfo psi = new ProcessStartInfo(fileName, arguments);
1187 psi.UserName = username;
1188 psi.Password = password;
1189 psi.Domain = domain;
1190 psi.UseShellExecute = false;
1195 public override string ToString() {
1196 return(base.ToString() +
1197 " (" + this.ProcessName + ")");
1200 /* Waits up to ms milliseconds for process 'handle' to
1201 * exit. ms can be <0 to mean wait forever.
1203 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1204 private extern bool WaitForExit_internal(IntPtr handle, int ms);
1206 public void WaitForExit ()
1211 public bool WaitForExit(int milliseconds) {
1212 int ms = milliseconds;
1213 if (ms == int.MaxValue)
1217 DateTime start = DateTime.UtcNow;
1218 if (async_output != null && !async_output.IsCompleted) {
1219 if (false == async_output.WaitHandle.WaitOne (ms, false))
1220 return false; // Timed out
1223 DateTime now = DateTime.UtcNow;
1224 ms -= (int) (now - start).TotalMilliseconds;
1231 if (async_error != null && !async_error.IsCompleted) {
1232 if (false == async_error.WaitHandle.WaitOne (ms, false))
1233 return false; // Timed out
1236 ms -= (int) (DateTime.UtcNow - start).TotalMilliseconds;
1242 return WaitForExit_internal (process_handle, ms);
1246 public bool WaitForInputIdle() {
1251 public bool WaitForInputIdle(int milliseconds) {
1255 private static bool IsLocalMachine (string machineName)
1257 if (machineName == "." || machineName.Length == 0)
1260 return (string.Compare (machineName, Environment.MachineName, true) == 0);
1265 [MonitoringDescription ("Raised when it receives output data")]
1266 public event DataReceivedEventHandler OutputDataReceived;
1268 [MonitoringDescription ("Raised when it receives error data")]
1269 public event DataReceivedEventHandler ErrorDataReceived;
1271 void OnOutputDataReceived (string str)
1273 if (OutputDataReceived != null)
1274 OutputDataReceived (this, new DataReceivedEventArgs (str));
1277 void OnErrorDataReceived (string str)
1279 if (ErrorDataReceived != null)
1280 ErrorDataReceived (this, new DataReceivedEventArgs (str));
1288 AsyncOutput = 1 << 2,
1292 [StructLayout (LayoutKind.Sequential)]
1293 sealed class ProcessAsyncReader
1296 The following fields match those of SocketAsyncResult.
1297 This is so that changes needed in the runtime to handle
1298 asynchronous reads are trivial
1300 /* DON'T shuffle fields around. DON'T remove fields */
1302 public IntPtr handle;
1303 public object state;
1304 public AsyncCallback callback;
1305 public ManualResetEvent wait_handle;
1307 public Exception delayedException;
1309 public object EndPoint;
1310 byte [] buffer = new byte [4196];
1313 public int SockFlags;
1315 public object acc_socket;
1317 public bool completed_sync;
1319 bool err_out; // true -> stdout, false -> stderr
1321 public int operation = 8; // MAGIC NUMBER: see Socket.cs:AsyncOperation
1325 // These fields are not in SocketAsyncResult
1328 StringBuilder sb = new StringBuilder ();
1329 public AsyncReadHandler ReadHandler;
1331 public ProcessAsyncReader (Process process, IntPtr handle, bool err_out)
1333 this.process = process;
1334 this.handle = handle;
1335 stream = new FileStream (handle, FileAccess.Read, false);
1336 this.ReadHandler = new AsyncReadHandler (AddInput);
1337 this.err_out = err_out;
1340 public void AddInput ()
1343 int nread = stream.Read (buffer, 0, buffer.Length);
1346 if (wait_handle != null)
1353 sb.Append (Encoding.Default.GetString (buffer, 0, nread));
1355 // Just in case the encoding fails...
1356 for (int i = 0; i < nread; i++) {
1357 sb.Append ((char) buffer [i]);
1362 ReadHandler.BeginInvoke (null, this);
1366 void Flush (bool last)
1368 if (sb.Length == 0 ||
1369 (err_out && process.output_canceled) ||
1370 (!err_out && process.error_canceled))
1373 string total = sb.ToString ();
1375 string [] strs = total.Split ('\n');
1376 int len = strs.Length;
1380 for (int i = 0; i < len - 1; i++) {
1382 process.OnOutputDataReceived (strs [i]);
1384 process.OnErrorDataReceived (strs [i]);
1387 string end = strs [len - 1];
1388 if (last || (len == 1 && end == "")) {
1390 process.OnOutputDataReceived (end);
1392 process.OnErrorDataReceived (end);
1399 public bool IsCompleted {
1400 get { return completed; }
1403 public WaitHandle WaitHandle {
1406 if (wait_handle == null)
1407 wait_handle = new ManualResetEvent (completed);
1414 AsyncModes async_mode;
1415 bool output_canceled;
1416 bool error_canceled;
1417 ProcessAsyncReader async_output;
1418 ProcessAsyncReader async_error;
1419 delegate void AsyncReadHandler ();
1421 [ComVisibleAttribute(false)]
1422 public void BeginOutputReadLine ()
1424 if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1425 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1427 if ((async_mode & AsyncModes.SyncOutput) != 0)
1428 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
1430 async_mode |= AsyncModes.AsyncOutput;
1431 output_canceled = false;
1432 if (async_output == null) {
1433 async_output = new ProcessAsyncReader (this, stdout_rd, true);
1434 async_output.ReadHandler.BeginInvoke (null, async_output);
1438 [ComVisibleAttribute(false)]
1439 public void CancelOutputRead ()
1441 if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1442 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1444 if ((async_mode & AsyncModes.SyncOutput) != 0)
1445 throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
1447 if (async_output == null)
1448 throw new InvalidOperationException ("No async operation in progress.");
1450 output_canceled = true;
1453 [ComVisibleAttribute(false)]
1454 public void BeginErrorReadLine ()
1456 if (process_handle == IntPtr.Zero || error_stream == null || StartInfo.RedirectStandardError == false)
1457 throw new InvalidOperationException ("Standard error has not been redirected or process has not been started.");
1459 if ((async_mode & AsyncModes.SyncError) != 0)
1460 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
1462 async_mode |= AsyncModes.AsyncError;
1463 error_canceled = false;
1464 if (async_error == null) {
1465 async_error = new ProcessAsyncReader (this, stderr_rd, false);
1466 async_error.ReadHandler.BeginInvoke (null, async_error);
1470 [ComVisibleAttribute(false)]
1471 public void CancelErrorRead ()
1473 if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1474 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1476 if ((async_mode & AsyncModes.SyncOutput) != 0)
1477 throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
1479 if (async_error == null)
1480 throw new InvalidOperationException ("No async operation in progress.");
1482 error_canceled = true;
1486 [Category ("Behavior")]
1487 [MonitoringDescription ("Raised when this process exits.")]
1488 public event EventHandler Exited {
1490 if (process_handle != IntPtr.Zero && HasExited) {
1491 value.BeginInvoke (null, null, null, null);
1493 exited_event = (EventHandler) Delegate.Combine (exited_event, value);
1494 if (exited_event != null)
1495 StartExitCallbackIfNeeded ();
1499 exited_event = (EventHandler) Delegate.Remove (exited_event, value);
1503 // Closes the system process handle
1504 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1505 private extern void Process_free_internal(IntPtr handle);
1507 private bool disposed = false;
1509 protected override void Dispose(bool disposing) {
1510 // Check to see if Dispose has already been called.
1511 if(this.disposed == false) {
1513 // If this is a call to Dispose,
1514 // dispose all managed resources.
1519 // Release unmanaged resources
1522 if(process_handle!=IntPtr.Zero) {
1524 Process_free_internal(process_handle);
1525 process_handle=IntPtr.Zero;
1528 if (input_stream != null) {
1529 input_stream.Close();
1530 input_stream = null;
1533 if (output_stream != null) {
1534 output_stream.Close();
1535 output_stream = null;
1538 if (error_stream != null) {
1539 error_stream.Close();
1540 error_stream = null;
1544 base.Dispose (disposing);
1552 static void CBOnExit (object state, bool unused)
1554 Process p = (Process) state;
1558 protected void OnExited()
1560 if (exited_event == null)
1563 if (synchronizingObject == null) {
1564 foreach (EventHandler d in exited_event.GetInvocationList ()) {
1566 d (this, EventArgs.Empty);
1572 object [] args = new object [] {this, EventArgs.Empty};
1573 synchronizingObject.BeginInvoke (exited_event, args);
1576 class ProcessWaitHandle : WaitHandle
1578 public ProcessWaitHandle (IntPtr handle)
1583 protected override void Dispose (bool explicitDisposing)
1585 // Do nothing, we don't own the handle and we won't close it.