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 {
525 [MonoNotSupported ("")]
526 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
527 [MonitoringDescription ("The session ID for this process.")]
528 public int SessionId {
529 get { throw new NotImplementedException (); }
533 /* the meaning of type is as follows: 0: user, 1: system, 2: total */
534 [MethodImplAttribute(MethodImplOptions.InternalCall)]
535 private extern static long Times (IntPtr handle, int type);
537 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
538 [MonitoringDescription ("The amount of processing time spent in the OS core for this process.")]
539 public TimeSpan PrivilegedProcessorTime {
541 return new TimeSpan (Times (process_handle, 1));
545 [MethodImplAttribute(MethodImplOptions.InternalCall)]
546 private extern static string ProcessName_internal(IntPtr handle);
548 private string process_name=null;
550 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
551 [MonitoringDescription ("The name of this process.")]
552 public string ProcessName {
554 if(process_name==null) {
555 process_name=ProcessName_internal(process_handle);
556 /* If process_name is _still_
557 * null, assume the process
560 if(process_name==null) {
561 throw new SystemException("The process has exited");
564 /* Strip the suffix (if it
565 * exists) simplistically
566 * instead of removing any
567 * trailing \.???, so we dont
568 * get stupid results on sane
571 if(process_name.EndsWith(".exe") ||
572 process_name.EndsWith(".bat") ||
573 process_name.EndsWith(".com")) {
574 process_name=process_name.Substring(0, process_name.Length-4);
577 return(process_name);
582 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
583 [MonitoringDescription ("Allowed processor that can be used by this process.")]
584 public IntPtr ProcessorAffinity {
593 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
594 [MonitoringDescription ("Is this process responsive.")]
595 public bool Responding {
601 private StreamReader error_stream=null;
603 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
604 [MonitoringDescription ("The standard error stream of this process.")]
605 public StreamReader StandardError {
607 if (error_stream == null) {
608 throw new InvalidOperationException("Standard error has not been redirected");
611 if ((async_mode & AsyncModes.AsyncError) != 0)
612 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
614 async_mode |= AsyncModes.SyncError;
617 return(error_stream);
621 private StreamWriter input_stream=null;
623 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
624 [MonitoringDescription ("The standard input stream of this process.")]
625 public StreamWriter StandardInput {
627 if (input_stream == null) {
628 throw new InvalidOperationException("Standard input has not been redirected");
631 return(input_stream);
635 private StreamReader output_stream=null;
637 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
638 [MonitoringDescription ("The standard output stream of this process.")]
639 public StreamReader StandardOutput {
641 if (output_stream == null) {
642 throw new InvalidOperationException("Standard output has not been redirected");
645 if ((async_mode & AsyncModes.AsyncOutput) != 0)
646 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
648 async_mode |= AsyncModes.SyncOutput;
651 return(output_stream);
655 private ProcessStartInfo start_info=null;
657 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content), Browsable (false)]
658 [MonitoringDescription ("Information for the start of this process.")]
659 public ProcessStartInfo StartInfo {
661 if(start_info==null) {
662 start_info=new ProcessStartInfo();
669 throw new ArgumentException("value is null");
676 /* Returns the process start time in Windows file
677 * times (ticks from DateTime(1/1/1601 00:00 GMT))
679 [MethodImplAttribute(MethodImplOptions.InternalCall)]
680 private extern static long StartTime_internal(IntPtr handle);
682 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
683 [MonitoringDescription ("The time this process started.")]
684 public DateTime StartTime {
686 return(DateTime.FromFileTime(StartTime_internal(process_handle)));
690 [DefaultValue (null), Browsable (false)]
691 [MonitoringDescription ("The object that is used to synchronize event handler calls for this process.")]
692 public ISynchronizeInvoke SynchronizingObject {
693 get { return synchronizingObject; }
694 set { synchronizingObject = value; }
698 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
699 [MonitoringDescription ("The number of threads of this process.")]
700 public ProcessThreadCollection Threads {
702 return ProcessThreadCollection.GetEmpty ();
706 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
707 [MonitoringDescription ("The total CPU time spent for this process.")]
708 public TimeSpan TotalProcessorTime {
710 return new TimeSpan (Times (process_handle, 2));
714 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
715 [MonitoringDescription ("The CPU time spent for this process in user mode.")]
716 public TimeSpan UserProcessorTime {
718 return new TimeSpan (Times (process_handle, 0));
724 [Obsolete ("Use VirtualMemorySize64")]
726 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
727 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
728 public int VirtualMemorySize {
736 [Obsolete ("Use WorkingSet64")]
738 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
739 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
740 public int WorkingSet {
748 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
749 [MonitoringDescription ("The amount of memory exclusively used by this process.")]
751 public long PrivateMemorySize64 {
758 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
759 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
761 public long VirtualMemorySize64 {
768 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
769 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
771 public long WorkingSet64 {
783 [MethodImplAttribute(MethodImplOptions.InternalCall)]
784 extern static bool Kill_internal (IntPtr handle, int signo);
786 /* int kill -> 1 KILL, 2 CloseMainWindow */
787 bool Close (int signo)
789 if (process_handle == IntPtr.Zero)
790 throw new SystemException ("No process to kill.");
792 int exitcode = ExitCode_internal (process_handle);
794 throw new InvalidOperationException ("The process already finished.");
796 return Kill_internal (process_handle, signo);
799 public bool CloseMainWindow ()
805 public static void EnterDebugMode() {
808 [MethodImplAttribute(MethodImplOptions.InternalCall)]
809 private extern static IntPtr GetProcess_internal(int pid);
811 [MethodImplAttribute(MethodImplOptions.InternalCall)]
812 private extern static int GetPid_internal();
814 public static Process GetCurrentProcess()
816 int pid = GetPid_internal();
817 IntPtr proc = GetProcess_internal(pid);
819 if (proc == IntPtr.Zero) {
820 throw new SystemException("Can't find current process");
823 return (new Process (proc, pid));
826 public static Process GetProcessById(int processId)
828 IntPtr proc = GetProcess_internal(processId);
830 if (proc == IntPtr.Zero) {
831 throw new ArgumentException ("Can't find process with ID " + processId.ToString ());
834 return (new Process (proc, processId));
837 [MonoTODO ("There is no support for retrieving process information from a remote machine")]
838 public static Process GetProcessById(int processId, string machineName) {
839 if (machineName == null)
840 throw new ArgumentNullException ("machineName");
842 if (!IsLocalMachine (machineName))
843 throw new NotImplementedException ();
845 return GetProcessById (processId);
848 [MethodImplAttribute(MethodImplOptions.InternalCall)]
849 private extern static int[] GetProcesses_internal();
851 public static Process[] GetProcesses()
853 int [] pids = GetProcesses_internal ();
854 ArrayList proclist = new ArrayList ();
856 for (int i = 0; i < pids.Length; i++) {
858 proclist.Add (GetProcessById (pids [i]));
859 } catch (SystemException) {
860 /* The process might exit
862 * GetProcesses_internal and
868 return ((Process []) proclist.ToArray (typeof (Process)));
871 [MonoTODO ("There is no support for retrieving process information from a remote machine")]
872 public static Process[] GetProcesses(string machineName) {
873 if (machineName == null)
874 throw new ArgumentNullException ("machineName");
876 if (!IsLocalMachine (machineName))
877 throw new NotImplementedException ();
879 return GetProcesses ();
882 public static Process[] GetProcessesByName(string processName)
884 Process [] procs = GetProcesses();
885 ArrayList proclist = new ArrayList();
887 for (int i = 0; i < procs.Length; i++) {
889 if (String.Compare (processName,
890 procs [i].ProcessName,
892 proclist.Add (procs [i]);
896 return ((Process[]) proclist.ToArray (typeof(Process)));
900 public static Process[] GetProcessesByName(string processName, string machineName) {
901 throw new NotImplementedException();
910 public static void LeaveDebugMode() {
913 public void Refresh ()
915 // FIXME: should refresh any cached data we might have about
916 // the process (currently we have none).
919 [MethodImplAttribute(MethodImplOptions.InternalCall)]
920 private extern static bool ShellExecuteEx_internal(ProcessStartInfo startInfo,
921 ref ProcInfo proc_info);
923 [MethodImplAttribute(MethodImplOptions.InternalCall)]
924 private extern static bool CreateProcess_internal(ProcessStartInfo startInfo,
928 ref ProcInfo proc_info);
930 private static bool Start_shell (ProcessStartInfo startInfo,
933 ProcInfo proc_info=new ProcInfo();
936 if (startInfo.RedirectStandardInput ||
937 startInfo.RedirectStandardOutput ||
938 startInfo.RedirectStandardError) {
939 throw new InvalidOperationException ("UseShellExecute must be false when redirecting I/O.");
942 if (startInfo.HaveEnvVars) {
943 throw new InvalidOperationException ("UseShellExecute must be false in order to use environment variables.");
946 FillUserInfo (startInfo, ref proc_info);
948 ret = ShellExecuteEx_internal (startInfo,
951 if (proc_info.Password != IntPtr.Zero)
952 Marshal.FreeBSTR (proc_info.Password);
953 proc_info.Password = IntPtr.Zero;
956 throw new Win32Exception (-proc_info.pid);
959 process.process_handle = proc_info.process_handle;
960 process.pid = proc_info.pid;
962 process.StartExitCallbackIfNeeded ();
967 private static bool Start_noshell (ProcessStartInfo startInfo,
970 if (Path.IsPathRooted (startInfo.FileName) && !File.Exists (startInfo.FileName))
971 throw new FileNotFoundException ("Executable not found: " + startInfo.FileName);
972 ProcInfo proc_info=new ProcInfo();
973 IntPtr stdin_rd, stdin_wr;
979 if (startInfo.HaveEnvVars) {
980 string [] strs = new string [startInfo.EnvironmentVariables.Count];
981 startInfo.EnvironmentVariables.Keys.CopyTo (strs, 0);
982 proc_info.envKeys = strs;
984 strs = new string [startInfo.EnvironmentVariables.Count];
985 startInfo.EnvironmentVariables.Values.CopyTo (strs, 0);
986 proc_info.envValues = strs;
989 if (startInfo.RedirectStandardInput == true) {
990 ret = MonoIO.CreatePipe (out stdin_rd,
993 throw new IOException ("Error creating standard input pipe");
996 stdin_rd = MonoIO.ConsoleInput;
997 /* This is required to stop the
998 * &$*£ing stupid compiler moaning
999 * that stdin_wr is unassigned, below.
1001 stdin_wr = (IntPtr)0;
1004 if (startInfo.RedirectStandardOutput == true) {
1006 ret = MonoIO.CreatePipe (out out_rd,
1009 process.stdout_rd = out_rd;
1011 if (startInfo.RedirectStandardInput == true) {
1012 MonoIO.Close (stdin_rd, out error);
1013 MonoIO.Close (stdin_wr, out error);
1016 throw new IOException ("Error creating standard output pipe");
1019 process.stdout_rd = (IntPtr)0;
1020 stdout_wr = MonoIO.ConsoleOutput;
1023 if (startInfo.RedirectStandardError == true) {
1025 ret = MonoIO.CreatePipe (out err_rd,
1028 process.stderr_rd = err_rd;
1030 if (startInfo.RedirectStandardInput == true) {
1031 MonoIO.Close (stdin_rd, out error);
1032 MonoIO.Close (stdin_wr, out error);
1034 if (startInfo.RedirectStandardOutput == true) {
1035 MonoIO.Close (process.stdout_rd, out error);
1036 MonoIO.Close (stdout_wr, out error);
1039 throw new IOException ("Error creating standard error pipe");
1042 process.stderr_rd = (IntPtr)0;
1043 stderr_wr = MonoIO.ConsoleError;
1046 FillUserInfo (startInfo, ref proc_info);
1048 ret = CreateProcess_internal (startInfo,
1049 stdin_rd, stdout_wr, stderr_wr,
1052 if (proc_info.Password != IntPtr.Zero)
1053 Marshal.FreeBSTR (proc_info.Password);
1054 proc_info.Password = IntPtr.Zero;
1057 if (startInfo.RedirectStandardInput == true) {
1058 MonoIO.Close (stdin_rd, out error);
1059 MonoIO.Close (stdin_wr, out error);
1062 if (startInfo.RedirectStandardOutput == true) {
1063 MonoIO.Close (process.stdout_rd, out error);
1064 MonoIO.Close (stdout_wr, out error);
1067 if (startInfo.RedirectStandardError == true) {
1068 MonoIO.Close (process.stderr_rd, out error);
1069 MonoIO.Close (stderr_wr, out error);
1072 throw new Win32Exception (-proc_info.pid,
1073 "ApplicationName='"+startInfo.FileName+
1074 "', CommandLine='"+startInfo.Arguments+
1075 "', CurrentDirectory='"+startInfo.WorkingDirectory+
1076 "', PATH='"+startInfo.EnvironmentVariables["PATH"]+"'");
1079 process.process_handle = proc_info.process_handle;
1080 process.pid = proc_info.pid;
1082 if (startInfo.RedirectStandardInput == true) {
1083 MonoIO.Close (stdin_rd, out error);
1084 process.input_stream = new StreamWriter (new MonoSyncFileStream (stdin_wr, FileAccess.Write, true, 8192), Console.Out.Encoding);
1085 process.input_stream.AutoFlush = true;
1089 Encoding stdoutEncoding = startInfo.StandardOutputEncoding ?? Console.Out.Encoding;
1090 Encoding stderrEncoding = startInfo.StandardErrorEncoding ?? Console.Out.Encoding;
1092 Encoding stdoutEncoding = Console.Out.Encoding;
1093 Encoding stderrEncoding = stdoutEncoding;
1096 if (startInfo.RedirectStandardOutput == true) {
1097 MonoIO.Close (stdout_wr, out error);
1098 process.output_stream = new StreamReader (new MonoSyncFileStream (process.stdout_rd, FileAccess.Read, true, 8192), stdoutEncoding);
1101 if (startInfo.RedirectStandardError == true) {
1102 MonoIO.Close (stderr_wr, out error);
1103 process.error_stream = new StreamReader (new MonoSyncFileStream (process.stderr_rd, FileAccess.Read, true, 8192), stderrEncoding);
1106 process.StartExitCallbackIfNeeded ();
1111 // Note that ProcInfo.Password must be freed.
1112 private static void FillUserInfo (ProcessStartInfo startInfo, ref ProcInfo proc_info)
1115 if (startInfo.UserName != null) {
1116 proc_info.UserName = startInfo.UserName;
1117 proc_info.Domain = startInfo.Domain;
1118 if (startInfo.Password != null)
1119 proc_info.Password = Marshal.SecureStringToBSTR (startInfo.Password);
1121 proc_info.Password = IntPtr.Zero;
1122 proc_info.LoadUserProfile = startInfo.LoadUserProfile;
1127 private static bool Start_common (ProcessStartInfo startInfo,
1130 if(startInfo.FileName == null ||
1131 startInfo.FileName == "") {
1132 throw new InvalidOperationException("File name has not been set");
1136 if (startInfo.StandardErrorEncoding != null && !startInfo.RedirectStandardError)
1137 throw new InvalidOperationException ("StandardErrorEncoding is only supported when standard error is redirected");
1138 if (startInfo.StandardOutputEncoding != null && !startInfo.RedirectStandardOutput)
1139 throw new InvalidOperationException ("StandardOutputEncoding is only supported when standard output is redirected");
1142 if (startInfo.UseShellExecute) {
1144 if (startInfo.UserName != null)
1145 throw new InvalidOperationException ("UserShellExecute must be false if an explicit UserName is specified when starting a process");
1147 return (Start_shell (startInfo, process));
1149 return (Start_noshell (startInfo, process));
1153 public bool Start() {
1156 if (process_handle != IntPtr.Zero) {
1157 Process_free_internal (process_handle);
1158 process_handle = IntPtr.Zero;
1160 ret=Start_common(start_info, this);
1165 public static Process Start(ProcessStartInfo startInfo) {
1167 Process process=new Process();
1170 process.StartInfo = startInfo;
1171 ret=Start_common(startInfo, process);
1180 public static Process Start(string fileName) {
1181 return Start(new ProcessStartInfo(fileName));
1184 public static Process Start(string fileName,
1186 return Start(new ProcessStartInfo(fileName, arguments));
1190 public static Process Start(string fileName, string username, SecureString password, string domain) {
1191 return Start(fileName, null, username, password, domain);
1194 public static Process Start(string fileName, string arguments, string username, SecureString password, string domain) {
1195 ProcessStartInfo psi = new ProcessStartInfo(fileName, arguments);
1196 psi.UserName = username;
1197 psi.Password = password;
1198 psi.Domain = domain;
1199 psi.UseShellExecute = false;
1204 public override string ToString() {
1205 return(base.ToString() +
1206 " (" + this.ProcessName + ")");
1209 /* Waits up to ms milliseconds for process 'handle' to
1210 * exit. ms can be <0 to mean wait forever.
1212 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1213 private extern bool WaitForExit_internal(IntPtr handle, int ms);
1215 public void WaitForExit ()
1220 public bool WaitForExit(int milliseconds) {
1221 int ms = milliseconds;
1222 if (ms == int.MaxValue)
1226 DateTime start = DateTime.UtcNow;
1227 if (async_output != null && !async_output.IsCompleted) {
1228 if (false == async_output.WaitHandle.WaitOne (ms, false))
1229 return false; // Timed out
1232 DateTime now = DateTime.UtcNow;
1233 ms -= (int) (now - start).TotalMilliseconds;
1240 if (async_error != null && !async_error.IsCompleted) {
1241 if (false == async_error.WaitHandle.WaitOne (ms, false))
1242 return false; // Timed out
1245 ms -= (int) (DateTime.UtcNow - start).TotalMilliseconds;
1251 return WaitForExit_internal (process_handle, ms);
1255 public bool WaitForInputIdle() {
1260 public bool WaitForInputIdle(int milliseconds) {
1264 private static bool IsLocalMachine (string machineName)
1266 if (machineName == "." || machineName.Length == 0)
1269 return (string.Compare (machineName, Environment.MachineName, true) == 0);
1274 [MonitoringDescription ("Raised when it receives output data")]
1275 public event DataReceivedEventHandler OutputDataReceived;
1277 [MonitoringDescription ("Raised when it receives error data")]
1278 public event DataReceivedEventHandler ErrorDataReceived;
1280 void OnOutputDataReceived (string str)
1282 if (OutputDataReceived != null)
1283 OutputDataReceived (this, new DataReceivedEventArgs (str));
1286 void OnErrorDataReceived (string str)
1288 if (ErrorDataReceived != null)
1289 ErrorDataReceived (this, new DataReceivedEventArgs (str));
1297 AsyncOutput = 1 << 2,
1301 [StructLayout (LayoutKind.Sequential)]
1302 sealed class ProcessAsyncReader
1305 The following fields match those of SocketAsyncResult.
1306 This is so that changes needed in the runtime to handle
1307 asynchronous reads are trivial
1309 /* DON'T shuffle fields around. DON'T remove fields */
1311 public IntPtr handle;
1312 public object state;
1313 public AsyncCallback callback;
1314 public ManualResetEvent wait_handle;
1316 public Exception delayedException;
1318 public object EndPoint;
1319 byte [] buffer = new byte [4196];
1322 public int SockFlags;
1324 public object acc_socket;
1326 public bool completed_sync;
1328 bool err_out; // true -> stdout, false -> stderr
1330 public int operation = 8; // MAGIC NUMBER: see Socket.cs:AsyncOperation
1334 // These fields are not in SocketAsyncResult
1337 StringBuilder sb = new StringBuilder ();
1338 public AsyncReadHandler ReadHandler;
1340 public ProcessAsyncReader (Process process, IntPtr handle, bool err_out)
1342 this.process = process;
1343 this.handle = handle;
1344 stream = new FileStream (handle, FileAccess.Read, false);
1345 this.ReadHandler = new AsyncReadHandler (AddInput);
1346 this.err_out = err_out;
1349 public void AddInput ()
1352 int nread = stream.Read (buffer, 0, buffer.Length);
1355 if (wait_handle != null)
1362 sb.Append (Encoding.Default.GetString (buffer, 0, nread));
1364 // Just in case the encoding fails...
1365 for (int i = 0; i < nread; i++) {
1366 sb.Append ((char) buffer [i]);
1371 ReadHandler.BeginInvoke (null, this);
1375 void Flush (bool last)
1377 if (sb.Length == 0 ||
1378 (err_out && process.output_canceled) ||
1379 (!err_out && process.error_canceled))
1382 string total = sb.ToString ();
1384 string [] strs = total.Split ('\n');
1385 int len = strs.Length;
1389 for (int i = 0; i < len - 1; i++) {
1391 process.OnOutputDataReceived (strs [i]);
1393 process.OnErrorDataReceived (strs [i]);
1396 string end = strs [len - 1];
1397 if (last || (len == 1 && end == "")) {
1399 process.OnOutputDataReceived (end);
1401 process.OnErrorDataReceived (end);
1408 public bool IsCompleted {
1409 get { return completed; }
1412 public WaitHandle WaitHandle {
1415 if (wait_handle == null)
1416 wait_handle = new ManualResetEvent (completed);
1423 AsyncModes async_mode;
1424 bool output_canceled;
1425 bool error_canceled;
1426 ProcessAsyncReader async_output;
1427 ProcessAsyncReader async_error;
1428 delegate void AsyncReadHandler ();
1430 [ComVisibleAttribute(false)]
1431 public void BeginOutputReadLine ()
1433 if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1434 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1436 if ((async_mode & AsyncModes.SyncOutput) != 0)
1437 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
1439 async_mode |= AsyncModes.AsyncOutput;
1440 output_canceled = false;
1441 if (async_output == null) {
1442 async_output = new ProcessAsyncReader (this, stdout_rd, true);
1443 async_output.ReadHandler.BeginInvoke (null, async_output);
1447 [ComVisibleAttribute(false)]
1448 public void CancelOutputRead ()
1450 if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1451 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1453 if ((async_mode & AsyncModes.SyncOutput) != 0)
1454 throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
1456 if (async_output == null)
1457 throw new InvalidOperationException ("No async operation in progress.");
1459 output_canceled = true;
1462 [ComVisibleAttribute(false)]
1463 public void BeginErrorReadLine ()
1465 if (process_handle == IntPtr.Zero || error_stream == null || StartInfo.RedirectStandardError == false)
1466 throw new InvalidOperationException ("Standard error has not been redirected or process has not been started.");
1468 if ((async_mode & AsyncModes.SyncError) != 0)
1469 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
1471 async_mode |= AsyncModes.AsyncError;
1472 error_canceled = false;
1473 if (async_error == null) {
1474 async_error = new ProcessAsyncReader (this, stderr_rd, false);
1475 async_error.ReadHandler.BeginInvoke (null, async_error);
1479 [ComVisibleAttribute(false)]
1480 public void CancelErrorRead ()
1482 if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1483 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1485 if ((async_mode & AsyncModes.SyncOutput) != 0)
1486 throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
1488 if (async_error == null)
1489 throw new InvalidOperationException ("No async operation in progress.");
1491 error_canceled = true;
1495 [Category ("Behavior")]
1496 [MonitoringDescription ("Raised when this process exits.")]
1497 public event EventHandler Exited {
1499 if (process_handle != IntPtr.Zero && HasExited) {
1500 value.BeginInvoke (null, null, null, null);
1502 exited_event = (EventHandler) Delegate.Combine (exited_event, value);
1503 if (exited_event != null)
1504 StartExitCallbackIfNeeded ();
1508 exited_event = (EventHandler) Delegate.Remove (exited_event, value);
1512 // Closes the system process handle
1513 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1514 private extern void Process_free_internal(IntPtr handle);
1516 private bool disposed = false;
1518 protected override void Dispose(bool disposing) {
1519 // Check to see if Dispose has already been called.
1520 if(this.disposed == false) {
1522 // If this is a call to Dispose,
1523 // dispose all managed resources.
1528 // Release unmanaged resources
1531 if(process_handle!=IntPtr.Zero) {
1533 Process_free_internal(process_handle);
1534 process_handle=IntPtr.Zero;
1537 if (input_stream != null) {
1538 input_stream.Close();
1539 input_stream = null;
1542 if (output_stream != null) {
1543 output_stream.Close();
1544 output_stream = null;
1547 if (error_stream != null) {
1548 error_stream.Close();
1549 error_stream = null;
1553 base.Dispose (disposing);
1561 static void CBOnExit (object state, bool unused)
1563 Process p = (Process) state;
1567 protected void OnExited()
1569 if (exited_event == null)
1572 if (synchronizingObject == null) {
1573 foreach (EventHandler d in exited_event.GetInvocationList ()) {
1575 d (this, EventArgs.Empty);
1581 object [] args = new object [] {this, EventArgs.Empty};
1582 synchronizingObject.BeginInvoke (exited_event, args);
1585 class ProcessWaitHandle : WaitHandle
1587 public ProcessWaitHandle (IntPtr handle)
1592 protected override void Dispose (bool explicitDisposing)
1594 // Do nothing, we don't own the handle and we won't close it.