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.Threading;
45 namespace System.Diagnostics {
47 [DefaultEvent ("Exited"), DefaultProperty ("StartInfo")]
48 [Designer ("System.Diagnostics.Design.ProcessDesigner, " + Consts.AssemblySystem_Design)]
49 [PermissionSet (SecurityAction.LinkDemand, Unrestricted = true)]
50 [PermissionSet (SecurityAction.InheritanceDemand, Unrestricted = true)]
51 public class Process : Component
53 [StructLayout(LayoutKind.Sequential)]
54 private struct ProcInfo
56 public IntPtr process_handle;
57 /* If thread_handle is ever needed for
58 * something, take out the CloseHandle() in
59 * the Start_internal icall in
60 * mono/metadata/process.c
62 public IntPtr thread_handle;
63 public int pid; // Contains -GetLastError () on failure.
65 public string [] envKeys;
66 public string [] envValues;
69 IntPtr process_handle;
71 bool enableRaisingEvents;
73 ISynchronizeInvoke synchronizingObject;
74 EventHandler exited_event;
78 /* Private constructor called from other methods */
79 private Process(IntPtr handle, int id) {
80 process_handle=handle;
89 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
90 [MonitoringDescription ("Base process priority.")]
91 public int BasePriority {
97 void StartExitCallbackIfNeeded ()
99 bool start = (!already_waiting && enableRaisingEvents && exited_event != null);
100 if (start && process_handle != IntPtr.Zero && !HasExited) {
101 WaitOrTimerCallback cb = new WaitOrTimerCallback (CBOnExit);
102 ProcessWaitHandle h = new ProcessWaitHandle (process_handle);
103 ThreadPool.RegisterWaitForSingleObject (h, cb, this, -1, true);
104 already_waiting = true;
108 [DefaultValue (false), Browsable (false)]
109 [MonitoringDescription ("Check for exiting of the process to raise the apropriate event.")]
110 public bool EnableRaisingEvents {
112 return enableRaisingEvents;
115 bool prev = enableRaisingEvents;
116 enableRaisingEvents = value;
117 if (enableRaisingEvents && !prev)
118 StartExitCallbackIfNeeded ();
123 [MethodImplAttribute(MethodImplOptions.InternalCall)]
124 private extern static int ExitCode_internal(IntPtr handle);
126 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
127 [MonitoringDescription ("The exit code of the process.")]
128 public int ExitCode {
130 if (process_handle == IntPtr.Zero)
131 throw new InvalidOperationException ("Process has not been started.");
133 int code = ExitCode_internal (process_handle);
135 throw new InvalidOperationException ("The process must exit before " +
136 "getting the requested information.");
142 /* Returns the process start time in Windows file
143 * times (ticks from DateTime(1/1/1601 00:00 GMT))
145 [MethodImplAttribute(MethodImplOptions.InternalCall)]
146 private extern static long ExitTime_internal(IntPtr handle);
148 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
149 [MonitoringDescription ("The exit time of the process.")]
150 public DateTime ExitTime {
152 if (process_handle == IntPtr.Zero)
153 throw new InvalidOperationException ("Process has not been started.");
156 throw new InvalidOperationException ("The process must exit before " +
157 "getting the requested information.");
159 return(DateTime.FromFileTime(ExitTime_internal(process_handle)));
163 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
164 [MonitoringDescription ("Handle for this process.")]
165 public IntPtr Handle {
167 return(process_handle);
172 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
173 [MonitoringDescription ("Handles for this process.")]
174 public int HandleCount {
180 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
181 [MonitoringDescription ("Determines if the process is still running.")]
182 public bool HasExited {
184 if (process_handle == IntPtr.Zero)
185 throw new InvalidOperationException ("Process has not been started.");
187 int exitcode = ExitCode_internal (process_handle);
198 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
199 [MonitoringDescription ("Process identifier.")]
203 throw new InvalidOperationException ("Process ID has not been set.");
211 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
212 [MonitoringDescription ("The name of the computer running the process.")]
213 public string MachineName {
219 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
220 [MonitoringDescription ("The main module of the process.")]
221 public ProcessModule MainModule {
223 return(this.Modules[0]);
228 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
229 [MonitoringDescription ("The handle of the main window of the process.")]
230 public IntPtr MainWindowHandle {
237 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
238 [MonitoringDescription ("The title of the main window of the process.")]
239 public string MainWindowTitle {
245 [MethodImplAttribute(MethodImplOptions.InternalCall)]
246 private extern static bool GetWorkingSet_internal(IntPtr handle, out int min, out int max);
247 [MethodImplAttribute(MethodImplOptions.InternalCall)]
248 private extern static bool SetWorkingSet_internal(IntPtr handle, int min, int max, bool use_min);
250 /* LAMESPEC: why is this an IntPtr not a plain int? */
251 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
252 [MonitoringDescription ("The maximum working set for this process.")]
253 public IntPtr MaxWorkingSet {
256 throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
261 bool ok=GetWorkingSet_internal(process_handle, out min, out max);
263 throw new Win32Exception();
270 throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
273 bool ok=SetWorkingSet_internal(process_handle, 0, value.ToInt32(), false);
275 throw new Win32Exception();
280 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
281 [MonitoringDescription ("The minimum working set for this process.")]
282 public IntPtr MinWorkingSet {
285 throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
290 bool ok=GetWorkingSet_internal(process_handle, out min, out max);
292 throw new Win32Exception();
299 throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
302 bool ok=SetWorkingSet_internal(process_handle, value.ToInt32(), 0, true);
304 throw new Win32Exception();
309 /* Returns the list of process modules. The main module is
312 [MethodImplAttribute(MethodImplOptions.InternalCall)]
313 private extern ProcessModule[] GetModules_internal();
315 private ProcessModuleCollection module_collection;
317 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
318 [MonitoringDescription ("The modules that are loaded as part of this process.")]
319 public ProcessModuleCollection Modules {
321 if(module_collection==null) {
322 module_collection=new ProcessModuleCollection(GetModules_internal());
325 return(module_collection);
331 [Obsolete ("Use NonpagedSystemMemorySize64")]
333 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
334 [MonitoringDescription ("The number of bytes that are not pageable.")]
335 public int NonpagedSystemMemorySize {
343 [Obsolete ("Use PagedMemorySize64")]
345 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
346 [MonitoringDescription ("The number of bytes that are paged.")]
347 public int PagedMemorySize {
355 [Obsolete ("Use PagedSystemMemorySize64")]
357 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
358 [MonitoringDescription ("The amount of paged system memory in bytes.")]
359 public int PagedSystemMemorySize {
367 [Obsolete ("Use PeakPagedMemorySize64")]
369 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
370 [MonitoringDescription ("The maximum amount of paged memory used by this process.")]
371 public int PeakPagedMemorySize {
379 [Obsolete ("Use PeakVirtualMemorySize64")]
381 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
382 [MonitoringDescription ("The maximum amount of virtual memory used by this process.")]
383 public int PeakVirtualMemorySize {
391 [Obsolete ("Use PeakWorkingSet64")]
393 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
394 [MonitoringDescription ("The maximum amount of system memory used by this process.")]
395 public int PeakWorkingSet {
403 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
404 [MonitoringDescription ("The number of bytes that are not pageable.")]
405 public long NonpagedSystemMemorySize64 {
412 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
413 [MonitoringDescription ("The number of bytes that are paged.")]
414 public long PagedMemorySize64 {
421 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
422 [MonitoringDescription ("The amount of paged system memory in bytes.")]
423 public long PagedSystemMemorySize64 {
430 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
431 [MonitoringDescription ("The maximum amount of paged memory used by this process.")]
432 public long PeakPagedMemorySize64 {
439 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
440 [MonitoringDescription ("The maximum amount of virtual memory used by this process.")]
441 public long PeakVirtualMemorySize64 {
448 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
449 [MonitoringDescription ("The maximum amount of system memory used by this process.")]
450 public long PeakWorkingSet64 {
458 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
459 [MonitoringDescription ("Process will be of higher priority while it is actively used.")]
460 public bool PriorityBoostEnabled {
469 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
470 [MonitoringDescription ("The relative process priority.")]
471 public ProcessPriorityClass PriorityClass {
473 return(ProcessPriorityClass.Normal);
480 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
481 [MonitoringDescription ("The amount of memory exclusively used by this process.")]
482 public int PrivateMemorySize {
489 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
490 [MonitoringDescription ("The amount of processing time spent in the OS core for this process.")]
491 public TimeSpan PrivilegedProcessorTime {
493 return(new TimeSpan(0));
497 [MethodImplAttribute(MethodImplOptions.InternalCall)]
498 private extern static string ProcessName_internal(IntPtr handle);
500 private string process_name=null;
502 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
503 [MonitoringDescription ("The name of this process.")]
504 public string ProcessName {
506 if(process_name==null) {
507 process_name=ProcessName_internal(process_handle);
508 /* If process_name is _still_
509 * null, assume the process
512 if(process_name==null) {
513 throw new SystemException("The process has exited");
516 /* Strip the suffix (if it
517 * exists) simplistically
518 * instead of removing any
519 * trailing \.???, so we dont
520 * get stupid results on sane
523 if(process_name.EndsWith(".exe") ||
524 process_name.EndsWith(".bat") ||
525 process_name.EndsWith(".com")) {
526 process_name=process_name.Substring(0, process_name.Length-4);
529 return(process_name);
534 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
535 [MonitoringDescription ("Allowed processor that can be used by this process.")]
536 public IntPtr ProcessorAffinity {
545 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
546 [MonitoringDescription ("Is this process responsive.")]
547 public bool Responding {
553 private StreamReader error_stream=null;
555 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
556 [MonitoringDescription ("The standard error stream of this process.")]
557 public StreamReader StandardError {
559 if (error_stream == null) {
560 throw new InvalidOperationException("Standard error has not been redirected");
563 if ((async_mode & AsyncModes.AsyncError) != 0)
564 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
566 async_mode |= AsyncModes.SyncError;
569 return(error_stream);
573 private StreamWriter input_stream=null;
575 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
576 [MonitoringDescription ("The standard input stream of this process.")]
577 public StreamWriter StandardInput {
579 if (input_stream == null) {
580 throw new InvalidOperationException("Standard input has not been redirected");
583 return(input_stream);
587 private StreamReader output_stream=null;
589 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
590 [MonitoringDescription ("The standard output stream of this process.")]
591 public StreamReader StandardOutput {
593 if (output_stream == null) {
594 throw new InvalidOperationException("Standard output has not been redirected");
597 if ((async_mode & AsyncModes.AsyncOutput) != 0)
598 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
600 async_mode |= AsyncModes.SyncOutput;
603 return(output_stream);
607 private ProcessStartInfo start_info=null;
609 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content), Browsable (false)]
610 [MonitoringDescription ("Information for the start of this process.")]
611 public ProcessStartInfo StartInfo {
613 if(start_info==null) {
614 start_info=new ProcessStartInfo();
621 throw new ArgumentException("value is null");
628 /* Returns the process start time in Windows file
629 * times (ticks from DateTime(1/1/1601 00:00 GMT))
631 [MethodImplAttribute(MethodImplOptions.InternalCall)]
632 private extern static long StartTime_internal(IntPtr handle);
634 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
635 [MonitoringDescription ("The time this process started.")]
636 public DateTime StartTime {
638 return(DateTime.FromFileTime(StartTime_internal(process_handle)));
642 [DefaultValue (null), Browsable (false)]
643 [MonitoringDescription ("The object that is used to synchronize event handler calls for this process.")]
644 public ISynchronizeInvoke SynchronizingObject {
645 get { return synchronizingObject; }
646 set { synchronizingObject = value; }
650 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
651 [MonitoringDescription ("The number of threads of this process.")]
652 public ProcessThreadCollection Threads {
659 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
660 [MonitoringDescription ("The total CPU time spent for this process.")]
661 public TimeSpan TotalProcessorTime {
663 return(new TimeSpan(0));
668 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
669 [MonitoringDescription ("The CPU time spent for this process in user mode.")]
670 public TimeSpan UserProcessorTime {
672 return(new TimeSpan(0));
678 [Obsolete ("Use VirtualMemorySize64")]
680 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
681 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
682 public int VirtualMemorySize {
690 [Obsolete ("Use WorkingSet64")]
692 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
693 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
694 public int WorkingSet {
702 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
703 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
704 public long VirtualMemorySize64 {
711 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
712 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
713 public long WorkingSet64 {
725 [MethodImplAttribute(MethodImplOptions.InternalCall)]
726 extern static bool Kill_internal (IntPtr handle, int signo);
728 /* int kill -> 1 KILL, 2 CloseMainWindow */
729 bool Close (int signo)
731 if (process_handle == IntPtr.Zero)
732 throw new SystemException ("No process to kill.");
734 int exitcode = ExitCode_internal (process_handle);
736 throw new InvalidOperationException ("The process already finished.");
738 return Kill_internal (process_handle, signo);
741 public bool CloseMainWindow ()
747 public static void EnterDebugMode() {
750 [MethodImplAttribute(MethodImplOptions.InternalCall)]
751 private extern static IntPtr GetProcess_internal(int pid);
753 [MethodImplAttribute(MethodImplOptions.InternalCall)]
754 private extern static int GetPid_internal();
756 public static Process GetCurrentProcess()
758 int pid = GetPid_internal();
759 IntPtr proc = GetProcess_internal(pid);
761 if (proc == IntPtr.Zero) {
762 throw new SystemException("Can't find current process");
765 return (new Process (proc, pid));
768 public static Process GetProcessById(int processId)
770 IntPtr proc = GetProcess_internal(processId);
772 if (proc == IntPtr.Zero) {
773 throw new ArgumentException ("Can't find process with ID " + processId.ToString ());
776 return (new Process (proc, processId));
780 public static Process GetProcessById(int processId, string machineName) {
781 throw new NotImplementedException();
784 [MethodImplAttribute(MethodImplOptions.InternalCall)]
785 private extern static int[] GetProcesses_internal();
787 public static Process[] GetProcesses()
789 int [] pids = GetProcesses_internal ();
790 ArrayList proclist = new ArrayList ();
792 for (int i = 0; i < pids.Length; i++) {
794 proclist.Add (GetProcessById (pids [i]));
795 } catch (SystemException) {
796 /* The process might exit
798 * GetProcesses_internal and
804 return ((Process []) proclist.ToArray (typeof (Process)));
807 [MonoTODO ("There is no support for retrieving process information from a remote machine")]
808 public static Process[] GetProcesses(string machineName) {
809 throw new NotImplementedException();
812 public static Process[] GetProcessesByName(string processName)
814 Process [] procs = GetProcesses();
815 ArrayList proclist = new ArrayList();
817 for (int i = 0; i < procs.Length; i++) {
819 if (String.Compare (processName,
820 procs [i].ProcessName,
822 proclist.Add (procs [i]);
826 return ((Process[]) proclist.ToArray (typeof(Process)));
830 public static Process[] GetProcessesByName(string processName, string machineName) {
831 throw new NotImplementedException();
840 public static void LeaveDebugMode() {
843 public void Refresh ()
845 // FIXME: should refresh any cached data we might have about
846 // the process (currently we have none).
849 [MethodImplAttribute(MethodImplOptions.InternalCall)]
850 private extern static bool ShellExecuteEx_internal(ProcessStartInfo startInfo,
851 ref ProcInfo proc_info);
853 [MethodImplAttribute(MethodImplOptions.InternalCall)]
854 private extern static bool CreateProcess_internal(ProcessStartInfo startInfo,
858 ref ProcInfo proc_info);
860 private static bool Start_shell (ProcessStartInfo startInfo,
863 ProcInfo proc_info=new ProcInfo();
866 if (startInfo.RedirectStandardInput ||
867 startInfo.RedirectStandardOutput ||
868 startInfo.RedirectStandardError) {
869 throw new InvalidOperationException ("UseShellExecute must be false when redirecting I/O.");
872 if (startInfo.HaveEnvVars) {
873 throw new InvalidOperationException ("UseShellExecute must be false in order to use environment variables.");
876 ret = ShellExecuteEx_internal (startInfo,
879 throw new Win32Exception (-proc_info.pid);
882 process.process_handle = proc_info.process_handle;
883 process.pid = proc_info.pid;
885 process.StartExitCallbackIfNeeded ();
890 private static bool Start_noshell (ProcessStartInfo startInfo,
893 if (Path.IsPathRooted (startInfo.FileName) && !File.Exists (startInfo.FileName))
894 throw new FileNotFoundException ("Executable not found: " + startInfo.FileName);
895 ProcInfo proc_info=new ProcInfo();
896 IntPtr stdin_rd, stdin_wr;
902 if (startInfo.HaveEnvVars) {
903 string [] strs = new string [startInfo.EnvironmentVariables.Count];
904 startInfo.EnvironmentVariables.Keys.CopyTo (strs, 0);
905 proc_info.envKeys = strs;
907 strs = new string [startInfo.EnvironmentVariables.Count];
908 startInfo.EnvironmentVariables.Values.CopyTo (strs, 0);
909 proc_info.envValues = strs;
912 if (startInfo.RedirectStandardInput == true) {
913 ret = MonoIO.CreatePipe (out stdin_rd,
916 throw new IOException ("Error creating standard input pipe");
919 stdin_rd = MonoIO.ConsoleInput;
920 /* This is required to stop the
921 * &$*£ing stupid compiler moaning
922 * that stdin_wr is unassigned, below.
924 stdin_wr = (IntPtr)0;
927 if (startInfo.RedirectStandardOutput == true) {
929 ret = MonoIO.CreatePipe (out out_rd,
932 process.stdout_rd = out_rd;
934 if (startInfo.RedirectStandardInput == true) {
935 MonoIO.Close (stdin_rd, out error);
936 MonoIO.Close (stdin_wr, out error);
939 throw new IOException ("Error creating standard output pipe");
942 process.stdout_rd = (IntPtr)0;
943 stdout_wr = MonoIO.ConsoleOutput;
946 if (startInfo.RedirectStandardError == true) {
948 ret = MonoIO.CreatePipe (out err_rd,
951 process.stderr_rd = err_rd;
953 if (startInfo.RedirectStandardInput == true) {
954 MonoIO.Close (stdin_rd, out error);
955 MonoIO.Close (stdin_wr, out error);
957 if (startInfo.RedirectStandardOutput == true) {
958 MonoIO.Close (process.stdout_rd, out error);
959 MonoIO.Close (stdout_wr, out error);
962 throw new IOException ("Error creating standard error pipe");
965 process.stderr_rd = (IntPtr)0;
966 stderr_wr = MonoIO.ConsoleError;
969 ret = CreateProcess_internal (startInfo,
970 stdin_rd, stdout_wr, stderr_wr,
973 if (startInfo.RedirectStandardInput == true) {
974 MonoIO.Close (stdin_rd, out error);
975 MonoIO.Close (stdin_wr, out error);
978 if (startInfo.RedirectStandardOutput == true) {
979 MonoIO.Close (process.stdout_rd, out error);
980 MonoIO.Close (stdout_wr, out error);
983 if (startInfo.RedirectStandardError == true) {
984 MonoIO.Close (process.stderr_rd, out error);
985 MonoIO.Close (stderr_wr, out error);
988 throw new Win32Exception (-proc_info.pid,
989 "ApplicationName='"+startInfo.FileName+
990 "', CommandLine='"+startInfo.Arguments+
991 "', CurrentDirectory='"+startInfo.WorkingDirectory+
992 "', PATH='"+startInfo.EnvironmentVariables["PATH"]+"'");
995 process.process_handle = proc_info.process_handle;
996 process.pid = proc_info.pid;
998 if (startInfo.RedirectStandardInput == true) {
999 MonoIO.Close (stdin_rd, out error);
1000 process.input_stream = new StreamWriter (new FileStream (stdin_wr, FileAccess.Write, true));
1001 process.input_stream.AutoFlush = true;
1004 if (startInfo.RedirectStandardOutput == true) {
1005 MonoIO.Close (stdout_wr, out error);
1006 process.output_stream = new StreamReader (new FileStream (process.stdout_rd, FileAccess.Read, true));
1009 if (startInfo.RedirectStandardError == true) {
1010 MonoIO.Close (stderr_wr, out error);
1011 process.error_stream = new StreamReader (new FileStream (process.stderr_rd, FileAccess.Read, true));
1014 process.StartExitCallbackIfNeeded ();
1019 private static bool Start_common (ProcessStartInfo startInfo,
1022 if(startInfo.FileName == null ||
1023 startInfo.FileName == "") {
1024 throw new InvalidOperationException("File name has not been set");
1027 if (startInfo.UseShellExecute) {
1028 return (Start_shell (startInfo, process));
1030 return (Start_noshell (startInfo, process));
1034 public bool Start() {
1037 ret=Start_common(start_info, this);
1042 public static Process Start(ProcessStartInfo startInfo) {
1043 Process process=new Process();
1046 process.StartInfo = startInfo;
1047 ret=Start_common(startInfo, process);
1056 public static Process Start(string fileName) {
1057 return Start(new ProcessStartInfo(fileName));
1060 public static Process Start(string fileName,
1062 return Start(new ProcessStartInfo(fileName, arguments));
1065 public override string ToString() {
1066 return(base.ToString() +
1067 " (" + this.ProcessName + ")");
1070 /* Waits up to ms milliseconds for process 'handle' to
1071 * exit. ms can be <0 to mean wait forever.
1073 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1074 private extern bool WaitForExit_internal(IntPtr handle, int ms);
1076 public void WaitForExit ()
1081 public bool WaitForExit(int milliseconds) {
1082 int ms = milliseconds;
1083 if (ms == int.MaxValue)
1087 DateTime start = DateTime.UtcNow;
1088 if (async_output != null && !async_output.IsCompleted) {
1089 if (false == async_output.WaitHandle.WaitOne (ms, false))
1090 return false; // Timed out
1093 DateTime now = DateTime.UtcNow;
1094 ms -= (int) (now - start).TotalMilliseconds;
1101 if (async_error != null && !async_error.IsCompleted) {
1102 if (false == async_error.WaitHandle.WaitOne (ms, false))
1103 return false; // Timed out
1106 ms -= (int) (DateTime.UtcNow - start).TotalMilliseconds;
1112 return WaitForExit_internal (process_handle, ms);
1116 public bool WaitForInputIdle() {
1121 public bool WaitForInputIdle(int milliseconds) {
1127 public event DataReceivedEventHandler OutputDataReceived;
1128 public event DataReceivedEventHandler ErrorDataReceived;
1130 void OnOutputDataReceived (string str)
1132 if (OutputDataReceived != null)
1133 OutputDataReceived (this, new DataReceivedEventArgs (str));
1136 void OnErrorDataReceived (string str)
1138 if (ErrorDataReceived != null)
1139 ErrorDataReceived (this, new DataReceivedEventArgs (str));
1147 AsyncOutput = 1 << 2,
1151 [StructLayout (LayoutKind.Sequential)]
1152 sealed class ProcessAsyncReader
1155 The following fields match those of SocketAsyncResult.
1156 This is so that changes needed in the runtime to handle
1157 asynchronous reads are trivial
1159 /* DON'T shuffle fields around. DON'T remove fields */
1161 public IntPtr handle;
1162 public object state;
1163 public AsyncCallback callback;
1164 public ManualResetEvent wait_handle;
1166 public Exception delayedException;
1168 public object EndPoint;
1169 byte [] buffer = new byte [4196];
1172 public int SockFlags;
1174 public object acc_socket;
1176 public bool completed_sync;
1178 bool err_out; // true -> stdout, false -> stderr
1180 public int operation = 8; // MAGIC NUMBER: see Socket.cs:AsyncOperation
1184 // These fields are not in SocketAsyncResult
1187 StringBuilder sb = new StringBuilder ();
1188 public AsyncReadHandler ReadHandler;
1190 public ProcessAsyncReader (Process process, IntPtr handle, bool err_out)
1192 this.process = process;
1193 this.handle = handle;
1194 stream = new FileStream (handle, FileAccess.Read, false);
1195 this.ReadHandler = new AsyncReadHandler (AddInput);
1196 this.err_out = err_out;
1199 public void AddInput ()
1202 int nread = stream.Read (buffer, 0, buffer.Length);
1205 if (wait_handle != null)
1212 sb.Append (Encoding.Default.GetString (buffer, 0, nread));
1214 // Just in case the encoding fails...
1215 for (int i = 0; i < nread; i++) {
1216 sb.Append ((char) buffer [i]);
1221 ReadHandler.BeginInvoke (null, this);
1225 void Flush (bool last)
1227 if (sb.Length == 0 ||
1228 (err_out && process.output_canceled) ||
1229 (!err_out && process.error_canceled))
1232 string total = sb.ToString ();
1234 string [] strs = total.Split ('\n');
1235 int len = strs.Length;
1239 for (int i = 0; i < len - 1; i++) {
1241 process.OnOutputDataReceived (strs [i]);
1243 process.OnErrorDataReceived (strs [i]);
1246 string end = strs [len - 1];
1247 if (last || end == "") {
1249 process.OnOutputDataReceived (end);
1251 process.OnErrorDataReceived (end);
1257 public bool IsCompleted {
1258 get { return completed; }
1261 public WaitHandle WaitHandle {
1264 if (wait_handle == null)
1265 wait_handle = new ManualResetEvent (completed);
1272 AsyncModes async_mode;
1273 bool output_canceled;
1274 bool error_canceled;
1275 ProcessAsyncReader async_output;
1276 ProcessAsyncReader async_error;
1277 delegate void AsyncReadHandler ();
1279 [ComVisibleAttribute(false)]
1280 public void BeginOutputReadLine ()
1282 if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1283 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1285 if ((async_mode & AsyncModes.SyncOutput) != 0)
1286 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
1288 async_mode |= AsyncModes.AsyncOutput;
1289 output_canceled = false;
1290 if (async_output == null) {
1291 async_output = new ProcessAsyncReader (this, stdout_rd, true);
1292 async_output.ReadHandler.BeginInvoke (null, async_output);
1296 [ComVisibleAttribute(false)]
1297 public void CancelOutputRead ()
1299 if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1300 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1302 if ((async_mode & AsyncModes.SyncOutput) != 0)
1303 throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
1305 if (async_output == null)
1306 throw new InvalidOperationException ("No async operation in progress.");
1308 output_canceled = true;
1311 [ComVisibleAttribute(false)]
1312 public void BeginErrorReadLine ()
1314 if (process_handle == IntPtr.Zero || error_stream == null || StartInfo.RedirectStandardError == false)
1315 throw new InvalidOperationException ("Standard error has not been redirected or process has not been started.");
1317 if ((async_mode & AsyncModes.SyncError) != 0)
1318 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
1320 async_mode |= AsyncModes.AsyncError;
1321 error_canceled = false;
1322 if (async_error == null) {
1323 async_error = new ProcessAsyncReader (this, stderr_rd, false);
1324 async_error.ReadHandler.BeginInvoke (null, async_error);
1328 [ComVisibleAttribute(false)]
1329 public void CancelErrorRead ()
1331 if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1332 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1334 if ((async_mode & AsyncModes.SyncOutput) != 0)
1335 throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
1337 if (async_error == null)
1338 throw new InvalidOperationException ("No async operation in progress.");
1340 error_canceled = true;
1344 [Category ("Behavior")]
1345 [MonitoringDescription ("Raised when this process exits.")]
1346 public event EventHandler Exited {
1348 if (process_handle != IntPtr.Zero && HasExited) {
1349 value.BeginInvoke (null, null, null, null);
1351 exited_event = (EventHandler) Delegate.Combine (exited_event, value);
1352 if (exited_event != null)
1353 StartExitCallbackIfNeeded ();
1357 exited_event = (EventHandler) Delegate.Remove (exited_event, value);
1361 // Closes the system process handle
1362 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1363 private extern void Process_free_internal(IntPtr handle);
1365 private bool disposed = false;
1367 protected override void Dispose(bool disposing) {
1368 // Check to see if Dispose has already been called.
1369 if(this.disposed == false) {
1371 // If this is a call to Dispose,
1372 // dispose all managed resources.
1377 // Release unmanaged resources
1380 if(process_handle!=IntPtr.Zero) {
1382 Process_free_internal(process_handle);
1383 process_handle=IntPtr.Zero;
1386 if (input_stream != null) {
1387 input_stream.Close();
1388 input_stream = null;
1391 if (output_stream != null) {
1392 output_stream.Close();
1393 output_stream = null;
1396 if (error_stream != null) {
1397 error_stream.Close();
1398 error_stream = null;
1402 base.Dispose (disposing);
1410 static void CBOnExit (object state, bool unused)
1412 Process p = (Process) state;
1416 protected void OnExited()
1418 if (exited_event == null)
1421 if (synchronizingObject == null) {
1422 foreach (EventHandler d in exited_event.GetInvocationList ()) {
1424 d (this, EventArgs.Empty);
1430 object [] args = new object [] {this, EventArgs.Empty};
1431 synchronizingObject.BeginInvoke (exited_event, args);
1434 class ProcessWaitHandle : WaitHandle
1436 public ProcessWaitHandle (IntPtr handle)
1441 protected override void Dispose (bool explicitDisposing)
1443 // Do nothing, we don't own the handle and we won't close it.