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) {
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.");
220 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
221 [MonitoringDescription ("The name of the computer running the process.")]
222 public string MachineName {
228 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
229 [MonitoringDescription ("The main module of the process.")]
230 public ProcessModule MainModule {
232 return(this.Modules[0]);
237 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
238 [MonitoringDescription ("The handle of the main window of the process.")]
239 public IntPtr MainWindowHandle {
246 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
247 [MonitoringDescription ("The title of the main window of the process.")]
248 public string MainWindowTitle {
254 [MethodImplAttribute(MethodImplOptions.InternalCall)]
255 private extern static bool GetWorkingSet_internal(IntPtr handle, out int min, out int max);
256 [MethodImplAttribute(MethodImplOptions.InternalCall)]
257 private extern static bool SetWorkingSet_internal(IntPtr handle, int min, int max, bool use_min);
259 /* LAMESPEC: why is this an IntPtr not a plain int? */
260 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
261 [MonitoringDescription ("The maximum working set for this process.")]
262 public IntPtr MaxWorkingSet {
265 throw new InvalidOperationException(
266 "The process " + ProcessName +
267 " (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(
296 "The process " + ProcessName +
297 " (ID " + Id + ") has exited");
301 bool ok= GetWorkingSet_internal (process_handle, out min, out max);
303 throw new Win32Exception();
304 return ((IntPtr) min);
308 throw new InvalidOperationException(
309 "The process " + ProcessName +
310 " (ID " + Id + ") has exited");
312 bool ok = SetWorkingSet_internal (process_handle, value.ToInt32(), 0, true);
314 throw new Win32Exception();
318 /* Returns the list of process modules. The main module is
321 [MethodImplAttribute(MethodImplOptions.InternalCall)]
322 private extern ProcessModule[] GetModules_internal(IntPtr handle);
324 private ProcessModuleCollection module_collection;
326 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
327 [MonitoringDescription ("The modules that are loaded as part of this process.")]
328 public ProcessModuleCollection Modules {
330 if (module_collection == null)
331 module_collection = new ProcessModuleCollection(
332 GetModules_internal (process_handle));
333 return(module_collection);
337 /* data type is from the MonoProcessData enum in mono-proclib.h in the runtime */
338 [MethodImplAttribute(MethodImplOptions.InternalCall)]
339 private extern static long GetProcessData (int pid, int data_type, out int error);
343 [Obsolete ("Use NonpagedSystemMemorySize64")]
345 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
346 [MonitoringDescription ("The number of bytes that are not pageable.")]
347 public int NonpagedSystemMemorySize {
355 [Obsolete ("Use PagedMemorySize64")]
357 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
358 [MonitoringDescription ("The number of bytes that are paged.")]
359 public int PagedMemorySize {
367 [Obsolete ("Use PagedSystemMemorySize64")]
369 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
370 [MonitoringDescription ("The amount of paged system memory in bytes.")]
371 public int PagedSystemMemorySize {
379 [Obsolete ("Use PeakPagedMemorySize64")]
381 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
382 [MonitoringDescription ("The maximum amount of paged memory used by this process.")]
383 public int PeakPagedMemorySize {
390 [Obsolete ("Use PeakVirtualMemorySize64")]
392 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
393 [MonitoringDescription ("The maximum amount of virtual memory used by this process.")]
394 public int PeakVirtualMemorySize {
397 return (int)GetProcessData (pid, 8, out error);
402 [Obsolete ("Use PeakWorkingSet64")]
404 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
405 [MonitoringDescription ("The maximum amount of system memory used by this process.")]
406 public int PeakWorkingSet {
409 return (int)GetProcessData (pid, 5, out error);
415 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
416 [MonitoringDescription ("The number of bytes that are not pageable.")]
418 public long NonpagedSystemMemorySize64 {
425 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
426 [MonitoringDescription ("The number of bytes that are paged.")]
428 public long PagedMemorySize64 {
435 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
436 [MonitoringDescription ("The amount of paged system memory in bytes.")]
438 public long PagedSystemMemorySize64 {
445 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
446 [MonitoringDescription ("The maximum amount of paged memory used by this process.")]
448 public long PeakPagedMemorySize64 {
454 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
455 [MonitoringDescription ("The maximum amount of virtual memory used by this process.")]
457 public long PeakVirtualMemorySize64 {
460 return GetProcessData (pid, 8, out error);
464 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
465 [MonitoringDescription ("The maximum amount of system memory used by this process.")]
467 public long PeakWorkingSet64 {
470 return GetProcessData (pid, 5, out error);
476 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
477 [MonitoringDescription ("Process will be of higher priority while it is actively used.")]
478 public bool PriorityBoostEnabled {
486 [MonoLimitation ("Under Unix, only root is allowed to raise the priority.")]
487 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
488 [MonitoringDescription ("The relative process priority.")]
489 public ProcessPriorityClass PriorityClass {
491 if (process_handle == IntPtr.Zero)
492 throw new InvalidOperationException ("Process has not been started.");
495 int prio = GetPriorityClass (process_handle, out error);
497 throw new Win32Exception (error);
498 return (ProcessPriorityClass) prio;
501 if (!Enum.IsDefined (typeof (ProcessPriorityClass), value))
502 throw new InvalidEnumArgumentException (
503 "value", (int) value,
504 typeof (ProcessPriorityClass));
506 if (process_handle == IntPtr.Zero)
507 throw new InvalidOperationException ("Process has not been started.");
510 if (!SetPriorityClass (process_handle, (int) value, out error))
511 throw new Win32Exception (error);
515 [MethodImplAttribute(MethodImplOptions.InternalCall)]
516 static extern int GetPriorityClass (IntPtr handle, out int error);
518 [MethodImplAttribute(MethodImplOptions.InternalCall)]
519 static extern bool SetPriorityClass (IntPtr handle, int priority, out int error);
521 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
522 [MonitoringDescription ("The amount of memory exclusively used by this process.")]
524 [Obsolete ("Use PrivateMemorySize64")]
526 public int PrivateMemorySize {
529 return (int)GetProcessData (pid, 6, out error);
534 [MonoNotSupported ("")]
535 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
536 [MonitoringDescription ("The session ID for this process.")]
537 public int SessionId {
538 get { throw new NotImplementedException (); }
542 /* the meaning of type is as follows: 0: user, 1: system, 2: total */
543 [MethodImplAttribute(MethodImplOptions.InternalCall)]
544 private extern static long Times (IntPtr handle, int type);
546 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
547 [MonitoringDescription ("The amount of processing time spent in the OS core for this process.")]
548 public TimeSpan PrivilegedProcessorTime {
550 return new TimeSpan (Times (process_handle, 1));
554 [MethodImplAttribute(MethodImplOptions.InternalCall)]
555 private extern static string ProcessName_internal(IntPtr handle);
557 private string process_name=null;
559 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
560 [MonitoringDescription ("The name of this process.")]
561 public string ProcessName {
563 if(process_name==null) {
565 if (process_handle == IntPtr.Zero)
566 throw new InvalidOperationException ("No process is associated with this object.");
568 process_name=ProcessName_internal(process_handle);
569 /* If process_name is _still_
570 * null, assume the process
573 if (process_name == null)
574 throw new InvalidOperationException ("Process has exited, so the requested information is not available.");
576 /* Strip the suffix (if it
577 * exists) simplistically
578 * instead of removing any
579 * trailing \.???, so we dont
580 * get stupid results on sane
583 if(process_name.EndsWith(".exe") ||
584 process_name.EndsWith(".bat") ||
585 process_name.EndsWith(".com")) {
586 process_name=process_name.Substring(0, process_name.Length-4);
589 return(process_name);
594 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
595 [MonitoringDescription ("Allowed processor that can be used by this process.")]
596 public IntPtr ProcessorAffinity {
605 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
606 [MonitoringDescription ("Is this process responsive.")]
607 public bool Responding {
613 private StreamReader error_stream=null;
615 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
616 [MonitoringDescription ("The standard error stream of this process.")]
617 public StreamReader StandardError {
619 if (error_stream == null)
620 throw new InvalidOperationException("Standard error has not been redirected");
623 if ((async_mode & AsyncModes.AsyncError) != 0)
624 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
626 async_mode |= AsyncModes.SyncError;
629 return(error_stream);
633 private StreamWriter input_stream=null;
635 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
636 [MonitoringDescription ("The standard input stream of this process.")]
637 public StreamWriter StandardInput {
639 if (input_stream == null)
640 throw new InvalidOperationException("Standard input has not been redirected");
642 return(input_stream);
646 private StreamReader output_stream=null;
648 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
649 [MonitoringDescription ("The standard output stream of this process.")]
650 public StreamReader StandardOutput {
652 if (output_stream == null)
653 throw new InvalidOperationException("Standard output has not been redirected");
656 if ((async_mode & AsyncModes.AsyncOutput) != 0)
657 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
659 async_mode |= AsyncModes.SyncOutput;
662 return(output_stream);
666 private ProcessStartInfo start_info=null;
668 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content), Browsable (false)]
669 [MonitoringDescription ("Information for the start of this process.")]
670 public ProcessStartInfo StartInfo {
672 if (start_info == null)
673 start_info = new ProcessStartInfo();
678 throw new ArgumentNullException("value");
683 /* Returns the process start time in Windows file
684 * times (ticks from DateTime(1/1/1601 00:00 GMT))
686 [MethodImplAttribute(MethodImplOptions.InternalCall)]
687 private extern static long StartTime_internal(IntPtr handle);
689 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
690 [MonitoringDescription ("The time this process started.")]
691 public DateTime StartTime {
693 return(DateTime.FromFileTime(StartTime_internal(process_handle)));
697 [DefaultValue (null), Browsable (false)]
698 [MonitoringDescription ("The object that is used to synchronize event handler calls for this process.")]
699 public ISynchronizeInvoke SynchronizingObject {
700 get { return synchronizingObject; }
701 set { synchronizingObject = value; }
705 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
706 [MonitoringDescription ("The number of threads of this process.")]
707 public ProcessThreadCollection Threads {
709 return ProcessThreadCollection.GetEmpty ();
713 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
714 [MonitoringDescription ("The total CPU time spent for this process.")]
715 public TimeSpan TotalProcessorTime {
717 return new TimeSpan (Times (process_handle, 2));
721 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
722 [MonitoringDescription ("The CPU time spent for this process in user mode.")]
723 public TimeSpan UserProcessorTime {
725 return new TimeSpan (Times (process_handle, 0));
730 [Obsolete ("Use VirtualMemorySize64")]
732 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
733 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
734 public int VirtualMemorySize {
737 return (int)GetProcessData (pid, 7, out error);
742 [Obsolete ("Use WorkingSet64")]
744 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
745 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
746 public int WorkingSet {
749 return (int)GetProcessData (pid, 4, out error);
754 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
755 [MonitoringDescription ("The amount of memory exclusively used by this process.")]
757 public long PrivateMemorySize64 {
760 return GetProcessData (pid, 6, out error);
764 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
765 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
767 public long VirtualMemorySize64 {
770 return GetProcessData (pid, 7, out error);
774 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
775 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
777 public long WorkingSet64 {
780 return GetProcessData (pid, 4, out error);
790 [MethodImplAttribute(MethodImplOptions.InternalCall)]
791 extern static bool Kill_internal (IntPtr handle, int signo);
793 /* int kill -> 1 KILL, 2 CloseMainWindow */
794 bool Close (int signo)
796 if (process_handle == IntPtr.Zero)
797 throw new SystemException ("No process to kill.");
799 int exitcode = ExitCode_internal (process_handle);
801 throw new InvalidOperationException ("The process already finished.");
803 return Kill_internal (process_handle, signo);
806 public bool CloseMainWindow ()
812 public static void EnterDebugMode() {
815 [MethodImplAttribute(MethodImplOptions.InternalCall)]
816 private extern static IntPtr GetProcess_internal(int pid);
818 [MethodImplAttribute(MethodImplOptions.InternalCall)]
819 private extern static int GetPid_internal();
821 public static Process GetCurrentProcess()
823 int pid = GetPid_internal();
824 IntPtr proc = GetProcess_internal(pid);
826 if (proc == IntPtr.Zero)
827 throw new SystemException("Can't find current process");
829 return (new Process (proc, pid));
832 public static Process GetProcessById(int processId)
834 IntPtr proc = GetProcess_internal(processId);
836 if (proc == IntPtr.Zero)
837 throw new ArgumentException ("Can't find process with ID " + processId.ToString ());
839 return (new Process (proc, processId));
842 [MonoTODO ("There is no support for retrieving process information from a remote machine")]
843 public static Process GetProcessById(int processId, string machineName) {
844 if (machineName == null)
845 throw new ArgumentNullException ("machineName");
847 if (!IsLocalMachine (machineName))
848 throw new NotImplementedException ();
850 return GetProcessById (processId);
853 [MethodImplAttribute(MethodImplOptions.InternalCall)]
854 private extern static int[] GetProcesses_internal();
856 public static Process[] GetProcesses()
858 int [] pids = GetProcesses_internal ();
860 return new Process [0];
862 ArrayList proclist = new ArrayList (pids.Length);
863 for (int i = 0; i < pids.Length; i++) {
865 proclist.Add (GetProcessById (pids [i]));
866 } catch (SystemException) {
867 /* The process might exit
869 * GetProcesses_internal and
875 return ((Process []) proclist.ToArray (typeof (Process)));
878 [MonoTODO ("There is no support for retrieving process information from a remote machine")]
879 public static Process[] GetProcesses(string machineName) {
880 if (machineName == null)
881 throw new ArgumentNullException ("machineName");
883 if (!IsLocalMachine (machineName))
884 throw new NotImplementedException ();
886 return GetProcesses ();
889 public static Process[] GetProcessesByName(string processName)
891 int [] pids = GetProcesses_internal ();
893 return new Process [0];
895 ArrayList proclist = new ArrayList (pids.Length);
896 for (int i = 0; i < pids.Length; i++) {
898 Process p = GetProcessById (pids [i]);
899 if (String.Compare (processName, p.ProcessName, true) == 0)
901 } catch (SystemException) {
902 /* The process might exit
904 * GetProcesses_internal and
910 return ((Process []) proclist.ToArray (typeof (Process)));
914 public static Process[] GetProcessesByName(string processName, string machineName) {
915 throw new NotImplementedException();
924 public static void LeaveDebugMode() {
927 public void Refresh ()
929 // FIXME: should refresh any cached data we might have about
930 // the process (currently we have none).
933 [MethodImplAttribute(MethodImplOptions.InternalCall)]
934 private extern static bool ShellExecuteEx_internal(ProcessStartInfo startInfo,
935 ref ProcInfo proc_info);
937 [MethodImplAttribute(MethodImplOptions.InternalCall)]
938 private extern static bool CreateProcess_internal(ProcessStartInfo startInfo,
942 ref ProcInfo proc_info);
944 private static bool Start_shell (ProcessStartInfo startInfo,
947 ProcInfo proc_info=new ProcInfo();
950 if (startInfo.RedirectStandardInput ||
951 startInfo.RedirectStandardOutput ||
952 startInfo.RedirectStandardError) {
953 throw new InvalidOperationException ("UseShellExecute must be false when redirecting I/O.");
956 if (startInfo.HaveEnvVars)
957 throw new InvalidOperationException ("UseShellExecute must be false in order to use environment variables.");
959 FillUserInfo (startInfo, ref proc_info);
961 ret = ShellExecuteEx_internal (startInfo,
964 if (proc_info.Password != IntPtr.Zero)
965 Marshal.FreeBSTR (proc_info.Password);
966 proc_info.Password = IntPtr.Zero;
969 throw new Win32Exception (-proc_info.pid);
972 process.process_handle = proc_info.process_handle;
973 process.pid = proc_info.pid;
975 process.StartExitCallbackIfNeeded ();
980 private static bool Start_noshell (ProcessStartInfo startInfo,
983 ProcInfo proc_info=new ProcInfo();
984 IntPtr stdin_rd = IntPtr.Zero, stdin_wr = IntPtr.Zero;
990 if (startInfo.HaveEnvVars) {
991 string [] strs = new string [startInfo.EnvironmentVariables.Count];
992 startInfo.EnvironmentVariables.Keys.CopyTo (strs, 0);
993 proc_info.envKeys = strs;
995 strs = new string [startInfo.EnvironmentVariables.Count];
996 startInfo.EnvironmentVariables.Values.CopyTo (strs, 0);
997 proc_info.envValues = strs;
1000 if (startInfo.RedirectStandardInput == true) {
1002 int DUPLICATE_SAME_ACCESS = 0x00000002;
1003 IntPtr stdin_wr_tmp;
1005 ret = MonoIO.CreatePipe (out stdin_rd,
1008 ret = MonoIO.DuplicateHandle (Process.GetCurrentProcess ().Handle, stdin_wr_tmp,
1009 Process.GetCurrentProcess ().Handle, out stdin_wr, 0, 0, DUPLICATE_SAME_ACCESS);
1010 MonoIO.Close (stdin_wr_tmp, out error);
1015 ret = MonoIO.CreatePipe (out stdin_rd,
1019 throw new IOException ("Error creating standard input pipe");
1022 stdin_rd = MonoIO.ConsoleInput;
1023 /* This is required to stop the
1024 * &$*£ing stupid compiler moaning
1025 * that stdin_wr is unassigned, below.
1027 stdin_wr = (IntPtr)0;
1030 if (startInfo.RedirectStandardOutput == true) {
1031 IntPtr out_rd = IntPtr.Zero;
1034 int DUPLICATE_SAME_ACCESS = 0x00000002;
1036 ret = MonoIO.CreatePipe (out out_rd_tmp,
1039 MonoIO.DuplicateHandle (Process.GetCurrentProcess ().Handle, out_rd_tmp,
1040 Process.GetCurrentProcess ().Handle, out out_rd, 0, 0, DUPLICATE_SAME_ACCESS);
1041 MonoIO.Close (out_rd_tmp, out error);
1045 ret = MonoIO.CreatePipe (out out_rd,
1049 process.stdout_rd = out_rd;
1051 if (startInfo.RedirectStandardInput == true) {
1052 MonoIO.Close (stdin_rd, out error);
1053 MonoIO.Close (stdin_wr, out error);
1056 throw new IOException ("Error creating standard output pipe");
1059 process.stdout_rd = (IntPtr)0;
1060 stdout_wr = MonoIO.ConsoleOutput;
1063 if (startInfo.RedirectStandardError == true) {
1064 IntPtr err_rd = IntPtr.Zero;
1067 int DUPLICATE_SAME_ACCESS = 0x00000002;
1069 ret = MonoIO.CreatePipe (out err_rd_tmp,
1072 MonoIO.DuplicateHandle (Process.GetCurrentProcess ().Handle, err_rd_tmp,
1073 Process.GetCurrentProcess ().Handle, out err_rd, 0, 0, DUPLICATE_SAME_ACCESS);
1074 MonoIO.Close (err_rd_tmp, out error);
1078 ret = MonoIO.CreatePipe (out err_rd,
1082 process.stderr_rd = err_rd;
1084 if (startInfo.RedirectStandardInput == true) {
1085 MonoIO.Close (stdin_rd, out error);
1086 MonoIO.Close (stdin_wr, out error);
1088 if (startInfo.RedirectStandardOutput == true) {
1089 MonoIO.Close (process.stdout_rd, out error);
1090 MonoIO.Close (stdout_wr, out error);
1093 throw new IOException ("Error creating standard error pipe");
1096 process.stderr_rd = (IntPtr)0;
1097 stderr_wr = MonoIO.ConsoleError;
1100 FillUserInfo (startInfo, ref proc_info);
1102 ret = CreateProcess_internal (startInfo,
1103 stdin_rd, stdout_wr, stderr_wr,
1106 if (proc_info.Password != IntPtr.Zero)
1107 Marshal.FreeBSTR (proc_info.Password);
1108 proc_info.Password = IntPtr.Zero;
1111 if (startInfo.RedirectStandardInput == true) {
1112 MonoIO.Close (stdin_rd, out error);
1113 MonoIO.Close (stdin_wr, out error);
1116 if (startInfo.RedirectStandardOutput == true) {
1117 MonoIO.Close (process.stdout_rd, out error);
1118 MonoIO.Close (stdout_wr, out error);
1121 if (startInfo.RedirectStandardError == true) {
1122 MonoIO.Close (process.stderr_rd, out error);
1123 MonoIO.Close (stderr_wr, out error);
1126 throw new Win32Exception (-proc_info.pid,
1127 "ApplicationName='" + startInfo.FileName +
1128 "', CommandLine='" + startInfo.Arguments +
1129 "', CurrentDirectory='" + startInfo.WorkingDirectory + "'");
1132 process.process_handle = proc_info.process_handle;
1133 process.pid = proc_info.pid;
1135 if (startInfo.RedirectStandardInput == true) {
1136 MonoIO.Close (stdin_rd, out error);
1137 process.input_stream = new StreamWriter (new MonoSyncFileStream (stdin_wr, FileAccess.Write, true, 8192), Console.Out.Encoding);
1138 process.input_stream.AutoFlush = true;
1142 Encoding stdoutEncoding = startInfo.StandardOutputEncoding ?? Console.Out.Encoding;
1143 Encoding stderrEncoding = startInfo.StandardErrorEncoding ?? Console.Out.Encoding;
1145 Encoding stdoutEncoding = Console.Out.Encoding;
1146 Encoding stderrEncoding = stdoutEncoding;
1149 if (startInfo.RedirectStandardOutput == true) {
1150 MonoIO.Close (stdout_wr, out error);
1151 process.output_stream = new StreamReader (new MonoSyncFileStream (process.stdout_rd, FileAccess.Read, true, 8192), stdoutEncoding, true, 8192);
1154 if (startInfo.RedirectStandardError == true) {
1155 MonoIO.Close (stderr_wr, out error);
1156 process.error_stream = new StreamReader (new MonoSyncFileStream (process.stderr_rd, FileAccess.Read, true, 8192), stderrEncoding, true, 8192);
1159 process.StartExitCallbackIfNeeded ();
1164 // Note that ProcInfo.Password must be freed.
1165 private static void FillUserInfo (ProcessStartInfo startInfo, ref ProcInfo proc_info)
1168 if (startInfo.UserName != null) {
1169 proc_info.UserName = startInfo.UserName;
1170 proc_info.Domain = startInfo.Domain;
1171 if (startInfo.Password != null)
1172 proc_info.Password = Marshal.SecureStringToBSTR (startInfo.Password);
1174 proc_info.Password = IntPtr.Zero;
1175 proc_info.LoadUserProfile = startInfo.LoadUserProfile;
1180 private static bool Start_common (ProcessStartInfo startInfo,
1183 if (startInfo.FileName == null || startInfo.FileName.Length == 0)
1184 throw new InvalidOperationException("File name has not been set");
1187 if (startInfo.StandardErrorEncoding != null && !startInfo.RedirectStandardError)
1188 throw new InvalidOperationException ("StandardErrorEncoding is only supported when standard error is redirected");
1189 if (startInfo.StandardOutputEncoding != null && !startInfo.RedirectStandardOutput)
1190 throw new InvalidOperationException ("StandardOutputEncoding is only supported when standard output is redirected");
1193 if (startInfo.UseShellExecute) {
1195 if (!String.IsNullOrEmpty (startInfo.UserName))
1196 throw new InvalidOperationException ("UserShellExecute must be false if an explicit UserName is specified when starting a process");
1198 return (Start_shell (startInfo, process));
1200 return (Start_noshell (startInfo, process));
1204 public bool Start ()
1206 if (process_handle != IntPtr.Zero) {
1207 Process_free_internal (process_handle);
1208 process_handle = IntPtr.Zero;
1210 return Start_common(start_info, this);
1213 public static Process Start (ProcessStartInfo startInfo)
1215 if (startInfo == null)
1216 throw new ArgumentNullException ("startInfo");
1218 Process process=new Process();
1219 process.StartInfo = startInfo;
1220 if (Start_common(startInfo, process))
1225 public static Process Start (string fileName)
1227 return Start (new ProcessStartInfo (fileName));
1230 public static Process Start(string fileName, string arguments)
1232 return Start (new ProcessStartInfo (fileName, arguments));
1236 public static Process Start(string fileName, string username, SecureString password, string domain) {
1237 return Start(fileName, null, username, password, domain);
1240 public static Process Start(string fileName, string arguments, string username, SecureString password, string domain) {
1241 ProcessStartInfo psi = new ProcessStartInfo(fileName, arguments);
1242 psi.UserName = username;
1243 psi.Password = password;
1244 psi.Domain = domain;
1245 psi.UseShellExecute = false;
1250 public override string ToString()
1252 return(base.ToString() + " (" + this.ProcessName + ")");
1255 /* Waits up to ms milliseconds for process 'handle' to
1256 * exit. ms can be <0 to mean wait forever.
1258 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1259 private extern bool WaitForExit_internal(IntPtr handle, int ms);
1261 public void WaitForExit ()
1266 public bool WaitForExit(int milliseconds) {
1267 int ms = milliseconds;
1268 if (ms == int.MaxValue)
1272 DateTime start = DateTime.UtcNow;
1273 if (async_output != null && !async_output.IsCompleted) {
1274 if (false == async_output.WaitHandle.WaitOne (ms, false))
1275 return false; // Timed out
1278 DateTime now = DateTime.UtcNow;
1279 ms -= (int) (now - start).TotalMilliseconds;
1286 if (async_error != null && !async_error.IsCompleted) {
1287 if (false == async_error.WaitHandle.WaitOne (ms, false))
1288 return false; // Timed out
1291 ms -= (int) (DateTime.UtcNow - start).TotalMilliseconds;
1297 return WaitForExit_internal (process_handle, ms);
1300 /* Waits up to ms milliseconds for process 'handle' to
1301 * wait for input. ms can be <0 to mean wait forever.
1303 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1304 private extern bool WaitForInputIdle_internal(IntPtr handle, int ms);
1306 // The internal call is only implemented properly on Windows.
1308 public bool WaitForInputIdle() {
1309 return WaitForInputIdle (-1);
1312 // The internal call is only implemented properly on Windows.
1314 public bool WaitForInputIdle(int milliseconds) {
1315 return WaitForInputIdle_internal (process_handle, milliseconds);
1318 private static bool IsLocalMachine (string machineName)
1320 if (machineName == "." || machineName.Length == 0)
1323 return (string.Compare (machineName, Environment.MachineName, true) == 0);
1328 [MonitoringDescription ("Raised when it receives output data")]
1329 public event DataReceivedEventHandler OutputDataReceived;
1331 [MonitoringDescription ("Raised when it receives error data")]
1332 public event DataReceivedEventHandler ErrorDataReceived;
1334 void OnOutputDataReceived (string str)
1336 if (OutputDataReceived != null)
1337 OutputDataReceived (this, new DataReceivedEventArgs (str));
1340 void OnErrorDataReceived (string str)
1342 if (ErrorDataReceived != null)
1343 ErrorDataReceived (this, new DataReceivedEventArgs (str));
1351 AsyncOutput = 1 << 2,
1355 [StructLayout (LayoutKind.Sequential)]
1356 sealed class ProcessAsyncReader
1359 The following fields match those of SocketAsyncResult.
1360 This is so that changes needed in the runtime to handle
1361 asynchronous reads are trivial
1362 Keep this in sync with SocketAsyncResult in
1363 ./System.Net.Sockets/Socket.cs and MonoSocketAsyncResult
1364 in metadata/socket-io.h.
1366 /* DON'T shuffle fields around. DON'T remove fields */
1368 public IntPtr handle;
1369 public object state;
1370 public AsyncCallback callback;
1371 public ManualResetEvent wait_handle;
1373 public Exception delayedException;
1375 public object EndPoint;
1376 byte [] buffer = new byte [4196];
1379 public int SockFlags;
1381 public object AcceptSocket;
1382 public object[] Addresses;
1384 public object Buffers; // Reserve this slot in older profiles
1385 public bool ReuseSocket; // Disconnect
1386 public object acc_socket;
1388 public bool completed_sync;
1390 bool err_out; // true -> stdout, false -> stderr
1392 public int operation = 8; // MAGIC NUMBER: see Socket.cs:AsyncOperation
1394 public int EndCalled;
1396 // These fields are not in SocketAsyncResult
1399 StringBuilder sb = new StringBuilder ();
1400 public AsyncReadHandler ReadHandler;
1402 public ProcessAsyncReader (Process process, IntPtr handle, bool err_out)
1404 this.process = process;
1405 this.handle = handle;
1406 stream = new FileStream (handle, FileAccess.Read, false);
1407 this.ReadHandler = new AsyncReadHandler (AddInput);
1408 this.err_out = err_out;
1411 public void AddInput ()
1414 int nread = stream.Read (buffer, 0, buffer.Length);
1417 if (wait_handle != null)
1424 sb.Append (Encoding.Default.GetString (buffer, 0, nread));
1426 // Just in case the encoding fails...
1427 for (int i = 0; i < nread; i++) {
1428 sb.Append ((char) buffer [i]);
1433 ReadHandler.BeginInvoke (null, this);
1441 process.OnOutputDataReceived (null);
1443 process.OnErrorDataReceived (null);
1447 void Flush (bool last)
1449 if (sb.Length == 0 ||
1450 (err_out && process.output_canceled) ||
1451 (!err_out && process.error_canceled))
1454 string total = sb.ToString ();
1456 string [] strs = total.Split ('\n');
1457 int len = strs.Length;
1461 for (int i = 0; i < len - 1; i++) {
1463 process.OnOutputDataReceived (strs [i]);
1465 process.OnErrorDataReceived (strs [i]);
1468 string end = strs [len - 1];
1469 if (last || (len == 1 && end == "")) {
1471 process.OnOutputDataReceived (end);
1473 process.OnErrorDataReceived (end);
1480 public bool IsCompleted {
1481 get { return completed; }
1484 public WaitHandle WaitHandle {
1487 if (wait_handle == null)
1488 wait_handle = new ManualResetEvent (completed);
1494 public void Close () {
1499 AsyncModes async_mode;
1500 bool output_canceled;
1501 bool error_canceled;
1502 ProcessAsyncReader async_output;
1503 ProcessAsyncReader async_error;
1504 delegate void AsyncReadHandler ();
1506 [ComVisibleAttribute(false)]
1507 public void BeginOutputReadLine ()
1509 if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1510 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1512 if ((async_mode & AsyncModes.SyncOutput) != 0)
1513 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
1515 async_mode |= AsyncModes.AsyncOutput;
1516 output_canceled = false;
1517 if (async_output == null) {
1518 async_output = new ProcessAsyncReader (this, stdout_rd, true);
1519 async_output.ReadHandler.BeginInvoke (null, async_output);
1523 [ComVisibleAttribute(false)]
1524 public void CancelOutputRead ()
1526 if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1527 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1529 if ((async_mode & AsyncModes.SyncOutput) != 0)
1530 throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
1532 if (async_output == null)
1533 throw new InvalidOperationException ("No async operation in progress.");
1535 output_canceled = true;
1538 [ComVisibleAttribute(false)]
1539 public void BeginErrorReadLine ()
1541 if (process_handle == IntPtr.Zero || error_stream == null || StartInfo.RedirectStandardError == false)
1542 throw new InvalidOperationException ("Standard error has not been redirected or process has not been started.");
1544 if ((async_mode & AsyncModes.SyncError) != 0)
1545 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
1547 async_mode |= AsyncModes.AsyncError;
1548 error_canceled = false;
1549 if (async_error == null) {
1550 async_error = new ProcessAsyncReader (this, stderr_rd, false);
1551 async_error.ReadHandler.BeginInvoke (null, async_error);
1555 [ComVisibleAttribute(false)]
1556 public void CancelErrorRead ()
1558 if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1559 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1561 if ((async_mode & AsyncModes.SyncOutput) != 0)
1562 throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
1564 if (async_error == null)
1565 throw new InvalidOperationException ("No async operation in progress.");
1567 error_canceled = true;
1571 [Category ("Behavior")]
1572 [MonitoringDescription ("Raised when this process exits.")]
1573 public event EventHandler Exited {
1575 if (process_handle != IntPtr.Zero && HasExited) {
1576 value.BeginInvoke (null, null, null, null);
1578 exited_event = (EventHandler) Delegate.Combine (exited_event, value);
1579 if (exited_event != null)
1580 StartExitCallbackIfNeeded ();
1584 exited_event = (EventHandler) Delegate.Remove (exited_event, value);
1588 // Closes the system process handle
1589 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1590 private extern void Process_free_internal(IntPtr handle);
1592 private bool disposed = false;
1594 protected override void Dispose(bool disposing) {
1595 // Check to see if Dispose has already been called.
1596 if(this.disposed == false) {
1598 // If this is a call to Dispose,
1599 // dispose all managed resources.
1603 /* These have open FileStreams on the pipes we are about to close */
1604 if (async_output != null)
1605 async_output.Close ();
1606 if (async_error != null)
1607 async_error.Close ();
1611 // Release unmanaged resources
1614 if(process_handle!=IntPtr.Zero) {
1615 Process_free_internal(process_handle);
1616 process_handle=IntPtr.Zero;
1619 if (input_stream != null) {
1620 input_stream.Close();
1621 input_stream = null;
1624 if (output_stream != null) {
1625 output_stream.Close();
1626 output_stream = null;
1629 if (error_stream != null) {
1630 error_stream.Close();
1631 error_stream = null;
1635 base.Dispose (disposing);
1643 static void CBOnExit (object state, bool unused)
1645 Process p = (Process) state;
1646 p.already_waiting = false;
1650 protected void OnExited()
1652 if (exited_event == null)
1655 if (synchronizingObject == null) {
1656 foreach (EventHandler d in exited_event.GetInvocationList ()) {
1658 d (this, EventArgs.Empty);
1664 object [] args = new object [] {this, EventArgs.Empty};
1665 synchronizingObject.BeginInvoke (exited_event, args);
1668 static bool IsWindows
1672 PlatformID platform = Environment.OSVersion.Platform;
1673 if (platform == PlatformID.Win32S ||
1674 platform == PlatformID.Win32Windows ||
1675 platform == PlatformID.Win32NT ||
1676 platform == PlatformID.WinCE) {
1683 class ProcessWaitHandle : WaitHandle
1685 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1686 private extern static IntPtr ProcessHandle_duplicate (IntPtr handle);
1688 public ProcessWaitHandle (IntPtr handle)
1690 // Need to keep a reference to this handle,
1691 // in case the Process object is collected
1692 Handle = ProcessHandle_duplicate (handle);
1694 // When the wait handle is disposed, the duplicated handle will be
1695 // closed, so no need to override dispose (bug #464628).