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 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.Collections;
42 using System.Threading;
44 namespace System.Diagnostics {
45 [DefaultEvent ("Exited"), DefaultProperty ("StartInfo")]
46 [Designer ("System.Diagnostics.Design.ProcessDesigner, " + Consts.AssemblySystem_Design, typeof (IDesigner))]
47 public class Process : Component
49 [StructLayout(LayoutKind.Sequential)]
50 private struct ProcInfo
52 public IntPtr process_handle;
53 /* If thread_handle is ever needed for
54 * something, take out the CloseHandle() in
55 * the Start_internal icall in
56 * mono/metadata/process.c
58 public IntPtr thread_handle;
59 public int pid; // Contains -GetLastError () on failure.
61 public string [] envKeys;
62 public string [] envValues;
63 public bool useShellExecute;
66 IntPtr process_handle;
68 bool enableRaisingEvents;
69 ISynchronizeInvoke synchronizingObject;
71 /* Private constructor called from other methods */
72 private Process(IntPtr handle, int id) {
73 process_handle=handle;
82 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
83 [MonitoringDescription ("Base process priority.")]
84 public int BasePriority {
90 [DefaultValue (false), Browsable (false)]
91 [MonitoringDescription ("Check for exiting of the process to raise the apropriate event.")]
92 public bool EnableRaisingEvents {
94 return enableRaisingEvents;
97 if (process_handle != IntPtr.Zero)
98 throw new InvalidOperationException ("The process is already started.");
100 enableRaisingEvents = value;
105 [MethodImplAttribute(MethodImplOptions.InternalCall)]
106 private extern static int ExitCode_internal(IntPtr handle);
108 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
109 [MonitoringDescription ("The exit code of the process.")]
110 public int ExitCode {
112 if (process_handle == IntPtr.Zero)
113 throw new InvalidOperationException ("Process has not been started.");
115 int code = ExitCode_internal (process_handle);
117 throw new InvalidOperationException ("The process must exit before " +
118 "getting the requested information.");
124 /* Returns the process start time in Windows file
125 * times (ticks from DateTime(1/1/1601 00:00 GMT))
127 [MethodImplAttribute(MethodImplOptions.InternalCall)]
128 private extern static long ExitTime_internal(IntPtr handle);
130 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
131 [MonitoringDescription ("The exit time of the process.")]
132 public DateTime ExitTime {
134 if (process_handle == IntPtr.Zero)
135 throw new InvalidOperationException ("Process has not been started.");
138 throw new InvalidOperationException ("The process must exit before " +
139 "getting the requested information.");
141 return(DateTime.FromFileTime(ExitTime_internal(process_handle)));
145 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
146 [MonitoringDescription ("Handle for this process.")]
147 public IntPtr Handle {
149 return(process_handle);
154 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
155 [MonitoringDescription ("Handles for this process.")]
156 public int HandleCount {
162 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
163 [MonitoringDescription ("Determines if the process is still running.")]
164 public bool HasExited {
166 if (process_handle == IntPtr.Zero)
167 throw new InvalidOperationException ("Process has not been started.");
169 int exitcode = ExitCode_internal (process_handle);
180 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
181 [MonitoringDescription ("Process identifier.")]
189 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
190 [MonitoringDescription ("The name of the computer running the process.")]
191 public string MachineName {
197 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
198 [MonitoringDescription ("The main module of the process.")]
199 public ProcessModule MainModule {
201 return(this.Modules[0]);
206 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
207 [MonitoringDescription ("The handle of the main window of the process.")]
208 public IntPtr MainWindowHandle {
215 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
216 [MonitoringDescription ("The title of the main window of the process.")]
217 public string MainWindowTitle {
223 [MethodImplAttribute(MethodImplOptions.InternalCall)]
224 private extern static bool GetWorkingSet_internal(IntPtr handle, out int min, out int max);
225 [MethodImplAttribute(MethodImplOptions.InternalCall)]
226 private extern static bool SetWorkingSet_internal(IntPtr handle, int min, int max, bool use_min);
228 /* LAMESPEC: why is this an IntPtr not a plain int? */
229 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
230 [MonitoringDescription ("The maximum working set for this process.")]
231 public IntPtr MaxWorkingSet {
234 throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
239 bool ok=GetWorkingSet_internal(process_handle, out min, out max);
241 throw new Win32Exception();
248 throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
251 bool ok=SetWorkingSet_internal(process_handle, 0, value.ToInt32(), false);
253 throw new Win32Exception();
258 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
259 [MonitoringDescription ("The minimum working set for this process.")]
260 public IntPtr MinWorkingSet {
263 throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
268 bool ok=GetWorkingSet_internal(process_handle, out min, out max);
270 throw new Win32Exception();
277 throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
280 bool ok=SetWorkingSet_internal(process_handle, value.ToInt32(), 0, true);
282 throw new Win32Exception();
287 /* Returns the list of process modules. The main module is
290 [MethodImplAttribute(MethodImplOptions.InternalCall)]
291 private extern ProcessModule[] GetModules_internal();
293 private ProcessModuleCollection module_collection;
295 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
296 [MonitoringDescription ("The modules that are loaded as part of this process.")]
297 public ProcessModuleCollection Modules {
299 if(module_collection==null) {
300 module_collection=new ProcessModuleCollection(GetModules_internal());
303 return(module_collection);
308 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
309 [MonitoringDescription ("The number of bytes that are not pageable.")]
310 public int NonpagedSystemMemorySize {
317 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
318 [MonitoringDescription ("The number of bytes that are paged.")]
319 public int PagedMemorySize {
326 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
327 [MonitoringDescription ("The amount of paged system memory in bytes.")]
328 public int PagedSystemMemorySize {
335 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
336 [MonitoringDescription ("The maximum amount of paged memory used by this process.")]
337 public int PeakPagedMemorySize {
344 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
345 [MonitoringDescription ("The maximum amount of virtual memory used by this process.")]
346 public int PeakVirtualMemorySize {
353 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
354 [MonitoringDescription ("The maximum amount of system memory used by this process.")]
355 public int PeakWorkingSet {
362 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
363 [MonitoringDescription ("Process will be of higher priority while it is actively used.")]
364 public bool PriorityBoostEnabled {
373 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
374 [MonitoringDescription ("The relative process priority.")]
375 public ProcessPriorityClass PriorityClass {
377 return(ProcessPriorityClass.Normal);
384 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
385 [MonitoringDescription ("The amount of memory exclusively used by this process.")]
386 public int PrivateMemorySize {
393 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
394 [MonitoringDescription ("The amount of processing time spent in the OS core for this process.")]
395 public TimeSpan PrivilegedProcessorTime {
397 return(new TimeSpan(0));
401 [MethodImplAttribute(MethodImplOptions.InternalCall)]
402 private extern static string ProcessName_internal(IntPtr handle);
404 private string process_name=null;
406 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
407 [MonitoringDescription ("The name of this process.")]
408 public string ProcessName {
410 if(process_name==null) {
411 process_name=ProcessName_internal(process_handle);
412 /* If process_name is _still_
413 * null, assume the process
416 if(process_name==null) {
417 throw new SystemException("The process has exited");
420 /* Strip the suffix (if it
421 * exists) simplistically
422 * instead of removing any
423 * trailing \.???, so we dont
424 * get stupid results on sane
427 if(process_name.EndsWith(".exe") ||
428 process_name.EndsWith(".bat") ||
429 process_name.EndsWith(".com")) {
430 process_name=process_name.Substring(0, process_name.Length-4);
433 return(process_name);
438 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
439 [MonitoringDescription ("Allowed processor that can be used by this process.")]
440 public IntPtr ProcessorAffinity {
449 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
450 [MonitoringDescription ("Is this process responsive.")]
451 public bool Responding {
457 private StreamReader error_stream=null;
459 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
460 [MonitoringDescription ("The standard error stream of this process.")]
461 public StreamReader StandardError {
463 return(error_stream);
467 private StreamWriter input_stream=null;
469 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
470 [MonitoringDescription ("The standard input stream of this process.")]
471 public StreamWriter StandardInput {
473 return(input_stream);
477 private StreamReader output_stream=null;
479 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
480 [MonitoringDescription ("The standard output stream of this process.")]
481 public StreamReader StandardOutput {
483 return(output_stream);
487 private ProcessStartInfo start_info=null;
489 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
490 [MonitoringDescription ("Information for the start of this process.")]
491 public ProcessStartInfo StartInfo {
493 if(start_info==null) {
494 start_info=new ProcessStartInfo();
501 throw new ArgumentException("value is null");
508 /* Returns the process start time in Windows file
509 * times (ticks from DateTime(1/1/1601 00:00 GMT))
511 [MethodImplAttribute(MethodImplOptions.InternalCall)]
512 private extern static long StartTime_internal(IntPtr handle);
514 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
515 [MonitoringDescription ("The time this process started.")]
516 public DateTime StartTime {
518 return(DateTime.FromFileTime(StartTime_internal(process_handle)));
522 [DefaultValue (null), Browsable (false)]
523 [MonitoringDescription ("The object that is used to synchronize event handler calls for this process.")]
524 public ISynchronizeInvoke SynchronizingObject {
525 get { return synchronizingObject; }
526 set { synchronizingObject = value; }
530 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
531 [MonitoringDescription ("The number of threads of this process.")]
532 public ProcessThreadCollection Threads {
539 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
540 [MonitoringDescription ("The total CPU time spent for this process.")]
541 public TimeSpan TotalProcessorTime {
543 return(new TimeSpan(0));
548 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
549 [MonitoringDescription ("The CPU time spent for this process in user mode.")]
550 public TimeSpan UserProcessorTime {
552 return(new TimeSpan(0));
557 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
558 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
559 public int VirtualMemorySize {
566 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
567 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
568 public int WorkingSet {
579 [MethodImplAttribute(MethodImplOptions.InternalCall)]
580 extern static bool Kill_internal (IntPtr handle, int signo);
582 /* int kill -> 1 KILL, 2 CloseMainWindow */
583 bool Close (int signo)
585 if (process_handle == IntPtr.Zero)
586 throw new SystemException ("No process to kill.");
588 int exitcode = ExitCode_internal (process_handle);
590 throw new InvalidOperationException ("The process already finished.");
592 return Kill_internal (process_handle, signo);
595 public bool CloseMainWindow ()
601 public static void EnterDebugMode() {
604 [MethodImplAttribute(MethodImplOptions.InternalCall)]
605 private extern static IntPtr GetProcess_internal(int pid);
607 [MethodImplAttribute(MethodImplOptions.InternalCall)]
608 private extern static int GetPid_internal();
610 public static Process GetCurrentProcess() {
611 int pid=GetPid_internal();
612 IntPtr proc=GetProcess_internal(pid);
614 if(proc==IntPtr.Zero) {
615 throw new SystemException("Can't find current process");
618 return(new Process(proc, pid));
621 public static Process GetProcessById(int processId) {
622 IntPtr proc=GetProcess_internal(processId);
624 if(proc==IntPtr.Zero) {
625 throw new ArgumentException("Can't find process with ID " + processId.ToString());
628 return(new Process(proc, processId));
632 public static Process GetProcessById(int processId, string machineName) {
633 throw new NotImplementedException();
636 [MethodImplAttribute(MethodImplOptions.InternalCall)]
637 private extern static int[] GetProcesses_internal();
639 public static Process[] GetProcesses() {
640 int[] pids=GetProcesses_internal();
641 ArrayList proclist=new ArrayList();
643 for(int i=0; i<pids.Length; i++) {
645 proclist.Add(GetProcessById(pids[i]));
646 } catch (SystemException) {
647 /* The process might exit
649 * GetProcesses_internal and
655 return((Process[])proclist.ToArray(typeof(Process)));
659 public static Process[] GetProcesses(string machineName) {
660 throw new NotImplementedException();
663 public static Process[] GetProcessesByName(string processName) {
664 Process[] procs=GetProcesses();
665 ArrayList proclist=new ArrayList();
667 for(int i=0; i<procs.Length; i++) {
669 if(String.Compare(processName,
670 procs[i].ProcessName,
672 proclist.Add(procs[i]);
676 return((Process[])proclist.ToArray(typeof(Process)));
680 public static Process[] GetProcessesByName(string processName, string machineName) {
681 throw new NotImplementedException();
690 public static void LeaveDebugMode() {
694 public void Refresh() {
697 [MethodImplAttribute(MethodImplOptions.InternalCall)]
698 private extern static bool Start_internal(string appname,
704 ref ProcInfo proc_info);
706 private static bool Start_common(ProcessStartInfo startInfo,
708 ProcInfo proc_info=new ProcInfo();
709 IntPtr stdin_rd, stdin_wr;
710 IntPtr stdout_rd, stdout_wr;
711 IntPtr stderr_rd, stderr_wr;
714 if(startInfo.FileName == null || startInfo.FileName == "") {
715 throw new InvalidOperationException("File name has not been set");
718 proc_info.useShellExecute = startInfo.UseShellExecute;
719 if (proc_info.useShellExecute && (startInfo.RedirectStandardInput ||
720 startInfo.RedirectStandardOutput || startInfo.RedirectStandardError)) {
721 throw new InvalidOperationException ("UseShellExecute must be false when " +
725 if (startInfo.HaveEnvVars) {
726 if (startInfo.UseShellExecute)
727 throw new InvalidOperationException ("UseShellExecute must be false in order " +
728 "to use environment variables.");
730 string [] strs = new string [startInfo.EnvironmentVariables.Count];
731 startInfo.EnvironmentVariables.Keys.CopyTo (strs, 0);
732 proc_info.envKeys = strs;
734 strs = new string [startInfo.EnvironmentVariables.Count];
735 startInfo.EnvironmentVariables.Values.CopyTo (strs, 0);
736 proc_info.envValues = strs;
739 if(startInfo.RedirectStandardInput==true) {
740 ret=MonoIO.CreatePipe(out stdin_rd,
743 stdin_rd=MonoIO.ConsoleInput;
744 /* This is required to stop the
745 * &$*£ing stupid compiler moaning
746 * that stdin_wr is unassigned, below.
751 if(startInfo.RedirectStandardOutput==true) {
752 ret=MonoIO.CreatePipe(out stdout_rd,
756 stdout_wr=MonoIO.ConsoleOutput;
759 if(startInfo.RedirectStandardError==true) {
760 ret=MonoIO.CreatePipe(out stderr_rd,
764 stderr_wr=MonoIO.ConsoleError;
769 if (startInfo.UseShellExecute) {
771 string args = startInfo.Arguments;
772 if (args == null || args.Trim () == "")
773 cmdline = startInfo.FileName;
775 cmdline = startInfo.FileName + " " + startInfo.Arguments.Trim ();
777 appname = startInfo.FileName;
778 // FIXME: There seems something wrong in process.c. We should not require extraneous command line
779 if (Path.DirectorySeparatorChar == '\\')
780 cmdline = appname + " " + startInfo.Arguments.Trim ();
782 cmdline = startInfo.Arguments.Trim ();
785 ret=Start_internal(appname,
787 startInfo.WorkingDirectory,
788 stdin_rd, stdout_wr, stderr_wr,
794 if (startInfo.RedirectStandardInput == true)
795 MonoIO.Close (stdin_rd, out error);
797 if (startInfo.RedirectStandardOutput == true)
798 MonoIO.Close (stdout_wr, out error);
800 if (startInfo.RedirectStandardError == true)
801 MonoIO.Close (stderr_wr, out error);
803 throw new Win32Exception (-proc_info.pid);
806 if (process.enableRaisingEvents && process.Exited != null) {
807 WaitOrTimerCallback cb = new WaitOrTimerCallback (CBOnExit);
808 ProcessWaitHandle h = new ProcessWaitHandle (proc_info.process_handle);
809 ThreadPool.RegisterWaitForSingleObject (h, cb, process, -1, true);
812 process.process_handle=proc_info.process_handle;
813 process.pid=proc_info.pid;
815 if(startInfo.RedirectStandardInput==true) {
816 MonoIO.Close(stdin_rd, out error);
817 process.input_stream=new StreamWriter(new FileStream(stdin_wr, FileAccess.Write, true));
818 process.input_stream.AutoFlush=true;
821 if(startInfo.RedirectStandardOutput==true) {
822 MonoIO.Close(stdout_wr, out error);
823 process.output_stream=new StreamReader(new FileStream(stdout_rd, FileAccess.Read, true));
826 if(startInfo.RedirectStandardError==true) {
827 MonoIO.Close(stderr_wr, out error);
828 process.error_stream=new StreamReader(new FileStream(stderr_rd, FileAccess.Read, true));
834 public bool Start() {
837 ret=Start_common(start_info, this);
842 public static Process Start(ProcessStartInfo startInfo) {
843 Process process=new Process();
846 ret=Start_common(startInfo, process);
855 public static Process Start(string fileName) {
856 return Start(new ProcessStartInfo(fileName));
859 public static Process Start(string fileName,
861 return Start(new ProcessStartInfo(fileName, arguments));
864 public override string ToString() {
865 return(base.ToString() +
866 " (" + this.ProcessName + ")");
869 /* Waits up to ms milliseconds for process 'handle' to
870 * exit. ms can be <0 to mean wait forever.
872 [MethodImplAttribute(MethodImplOptions.InternalCall)]
873 private extern bool WaitForExit_internal(IntPtr handle,
876 public void WaitForExit() {
877 WaitForExit_internal(process_handle, -1);
880 public bool WaitForExit(int milliseconds) {
881 if (milliseconds == int.MaxValue)
884 return WaitForExit_internal (process_handle, milliseconds);
888 public bool WaitForInputIdle() {
893 public bool WaitForInputIdle(int milliseconds) {
898 [MonitoringDescription ("Raised when this process exits.")]
899 public event EventHandler Exited;
901 // Closes the system process handle
902 [MethodImplAttribute(MethodImplOptions.InternalCall)]
903 private extern void Process_free_internal(IntPtr handle);
905 private bool disposed = false;
907 protected override void Dispose(bool disposing) {
908 // Check to see if Dispose has already been called.
909 if(this.disposed == false) {
911 // If this is a call to Dispose,
912 // dispose all managed resources.
917 // Release unmanaged resources
920 if(process_handle!=IntPtr.Zero) {
922 Process_free_internal(process_handle);
923 process_handle=IntPtr.Zero;
927 base.Dispose (disposing);
930 static void CBOnExit (object state, bool unused)
932 Process p = (Process) state;
936 protected void OnExited()
941 if (synchronizingObject == null) {
942 Exited (this, EventArgs.Empty);
946 object [] args = new object [] {this, EventArgs.Empty};
947 synchronizingObject.BeginInvoke (Exited, args);
950 class ProcessWaitHandle : WaitHandle
952 public ProcessWaitHandle (IntPtr handle)
957 protected override void Dispose (bool explicitDisposing)
959 // Do nothing, we don't own the handle and we won't close it.