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 if (error_stream == null) {
464 throw new InvalidOperationException("Standard error has not been redirected");
467 return(error_stream);
471 private StreamWriter input_stream=null;
473 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
474 [MonitoringDescription ("The standard input stream of this process.")]
475 public StreamWriter StandardInput {
477 if (input_stream == null) {
478 throw new InvalidOperationException("Standard input has not been redirected");
481 return(input_stream);
485 private StreamReader output_stream=null;
487 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
488 [MonitoringDescription ("The standard output stream of this process.")]
489 public StreamReader StandardOutput {
491 if (output_stream == null) {
492 throw new InvalidOperationException("Standard output has not been redirected");
495 return(output_stream);
499 private ProcessStartInfo start_info=null;
501 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
502 [MonitoringDescription ("Information for the start of this process.")]
503 public ProcessStartInfo StartInfo {
505 if(start_info==null) {
506 start_info=new ProcessStartInfo();
513 throw new ArgumentException("value is null");
520 /* Returns the process start time in Windows file
521 * times (ticks from DateTime(1/1/1601 00:00 GMT))
523 [MethodImplAttribute(MethodImplOptions.InternalCall)]
524 private extern static long StartTime_internal(IntPtr handle);
526 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
527 [MonitoringDescription ("The time this process started.")]
528 public DateTime StartTime {
530 return(DateTime.FromFileTime(StartTime_internal(process_handle)));
534 [DefaultValue (null), Browsable (false)]
535 [MonitoringDescription ("The object that is used to synchronize event handler calls for this process.")]
536 public ISynchronizeInvoke SynchronizingObject {
537 get { return synchronizingObject; }
538 set { synchronizingObject = value; }
542 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
543 [MonitoringDescription ("The number of threads of this process.")]
544 public ProcessThreadCollection Threads {
551 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
552 [MonitoringDescription ("The total CPU time spent for this process.")]
553 public TimeSpan TotalProcessorTime {
555 return(new TimeSpan(0));
560 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
561 [MonitoringDescription ("The CPU time spent for this process in user mode.")]
562 public TimeSpan UserProcessorTime {
564 return(new TimeSpan(0));
569 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
570 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
571 public int VirtualMemorySize {
578 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
579 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
580 public int WorkingSet {
591 [MethodImplAttribute(MethodImplOptions.InternalCall)]
592 extern static bool Kill_internal (IntPtr handle, int signo);
594 /* int kill -> 1 KILL, 2 CloseMainWindow */
595 bool Close (int signo)
597 if (process_handle == IntPtr.Zero)
598 throw new SystemException ("No process to kill.");
600 int exitcode = ExitCode_internal (process_handle);
602 throw new InvalidOperationException ("The process already finished.");
604 return Kill_internal (process_handle, signo);
607 public bool CloseMainWindow ()
613 public static void EnterDebugMode() {
616 [MethodImplAttribute(MethodImplOptions.InternalCall)]
617 private extern static IntPtr GetProcess_internal(int pid);
619 [MethodImplAttribute(MethodImplOptions.InternalCall)]
620 private extern static int GetPid_internal();
622 public static Process GetCurrentProcess() {
623 int pid=GetPid_internal();
624 IntPtr proc=GetProcess_internal(pid);
626 if(proc==IntPtr.Zero) {
627 throw new SystemException("Can't find current process");
630 return(new Process(proc, pid));
633 public static Process GetProcessById(int processId) {
634 IntPtr proc=GetProcess_internal(processId);
636 if(proc==IntPtr.Zero) {
637 throw new ArgumentException("Can't find process with ID " + processId.ToString());
640 return(new Process(proc, processId));
644 public static Process GetProcessById(int processId, string machineName) {
645 throw new NotImplementedException();
648 [MethodImplAttribute(MethodImplOptions.InternalCall)]
649 private extern static int[] GetProcesses_internal();
651 public static Process[] GetProcesses() {
652 int[] pids=GetProcesses_internal();
653 ArrayList proclist=new ArrayList();
655 for(int i=0; i<pids.Length; i++) {
657 proclist.Add(GetProcessById(pids[i]));
658 } catch (SystemException) {
659 /* The process might exit
661 * GetProcesses_internal and
667 return((Process[])proclist.ToArray(typeof(Process)));
671 public static Process[] GetProcesses(string machineName) {
672 throw new NotImplementedException();
675 public static Process[] GetProcessesByName(string processName) {
676 Process[] procs=GetProcesses();
677 ArrayList proclist=new ArrayList();
679 for(int i=0; i<procs.Length; i++) {
681 if(String.Compare(processName,
682 procs[i].ProcessName,
684 proclist.Add(procs[i]);
688 return((Process[])proclist.ToArray(typeof(Process)));
692 public static Process[] GetProcessesByName(string processName, string machineName) {
693 throw new NotImplementedException();
702 public static void LeaveDebugMode() {
706 public void Refresh() {
709 [MethodImplAttribute(MethodImplOptions.InternalCall)]
710 private extern static bool Start_internal(string appname,
716 ref ProcInfo proc_info);
718 private static bool Start_common(ProcessStartInfo startInfo,
720 ProcInfo proc_info=new ProcInfo();
721 IntPtr stdin_rd, stdin_wr;
722 IntPtr stdout_rd, stdout_wr;
723 IntPtr stderr_rd, stderr_wr;
726 if(startInfo.FileName == null || startInfo.FileName == "") {
727 throw new InvalidOperationException("File name has not been set");
730 proc_info.useShellExecute = startInfo.UseShellExecute;
731 if (proc_info.useShellExecute && (startInfo.RedirectStandardInput ||
732 startInfo.RedirectStandardOutput || startInfo.RedirectStandardError)) {
733 throw new InvalidOperationException ("UseShellExecute must be false when " +
737 if (startInfo.HaveEnvVars) {
738 if (startInfo.UseShellExecute)
739 throw new InvalidOperationException ("UseShellExecute must be false in order " +
740 "to use environment variables.");
742 string [] strs = new string [startInfo.EnvironmentVariables.Count];
743 startInfo.EnvironmentVariables.Keys.CopyTo (strs, 0);
744 proc_info.envKeys = strs;
746 strs = new string [startInfo.EnvironmentVariables.Count];
747 startInfo.EnvironmentVariables.Values.CopyTo (strs, 0);
748 proc_info.envValues = strs;
751 if(startInfo.RedirectStandardInput==true) {
752 ret=MonoIO.CreatePipe(out stdin_rd,
755 throw new IOException("Error creating standard input pipe");
758 stdin_rd=MonoIO.ConsoleInput;
759 /* This is required to stop the
760 * &$*£ing stupid compiler moaning
761 * that stdin_wr is unassigned, below.
766 if(startInfo.RedirectStandardOutput==true) {
767 ret=MonoIO.CreatePipe(out stdout_rd,
770 throw new IOException("Error creating standard output pipe");
774 stdout_wr=MonoIO.ConsoleOutput;
777 if(startInfo.RedirectStandardError==true) {
778 ret=MonoIO.CreatePipe(out stderr_rd,
781 throw new IOException("Error creating standard error pipe");
785 stderr_wr=MonoIO.ConsoleError;
790 if (startInfo.UseShellExecute) {
792 string args = startInfo.Arguments;
793 if (args == null || args.Trim () == "")
794 cmdline = startInfo.FileName;
796 cmdline = startInfo.FileName + " " + startInfo.Arguments.Trim ();
798 appname = startInfo.FileName;
799 cmdline = startInfo.Arguments.Trim ();
802 ret=Start_internal(appname,
804 startInfo.WorkingDirectory,
805 stdin_rd, stdout_wr, stderr_wr,
811 if (startInfo.RedirectStandardInput == true)
812 MonoIO.Close (stdin_rd, out error);
814 if (startInfo.RedirectStandardOutput == true)
815 MonoIO.Close (stdout_wr, out error);
817 if (startInfo.RedirectStandardError == true)
818 MonoIO.Close (stderr_wr, out error);
820 throw new Win32Exception (-proc_info.pid);
823 if (process.enableRaisingEvents && process.Exited != null) {
824 WaitOrTimerCallback cb = new WaitOrTimerCallback (CBOnExit);
825 ProcessWaitHandle h = new ProcessWaitHandle (proc_info.process_handle);
826 ThreadPool.RegisterWaitForSingleObject (h, cb, process, -1, true);
829 process.process_handle=proc_info.process_handle;
830 process.pid=proc_info.pid;
832 if(startInfo.RedirectStandardInput==true) {
833 MonoIO.Close(stdin_rd, out error);
834 process.input_stream=new StreamWriter(new FileStream(stdin_wr, FileAccess.Write, true));
835 process.input_stream.AutoFlush=true;
838 if(startInfo.RedirectStandardOutput==true) {
839 MonoIO.Close(stdout_wr, out error);
840 process.output_stream=new StreamReader(new FileStream(stdout_rd, FileAccess.Read, true));
843 if(startInfo.RedirectStandardError==true) {
844 MonoIO.Close(stderr_wr, out error);
845 process.error_stream=new StreamReader(new FileStream(stderr_rd, FileAccess.Read, true));
851 public bool Start() {
854 ret=Start_common(start_info, this);
859 public static Process Start(ProcessStartInfo startInfo) {
860 Process process=new Process();
863 ret=Start_common(startInfo, process);
872 public static Process Start(string fileName) {
873 return Start(new ProcessStartInfo(fileName));
876 public static Process Start(string fileName,
878 return Start(new ProcessStartInfo(fileName, arguments));
881 public override string ToString() {
882 return(base.ToString() +
883 " (" + this.ProcessName + ")");
886 /* Waits up to ms milliseconds for process 'handle' to
887 * exit. ms can be <0 to mean wait forever.
889 [MethodImplAttribute(MethodImplOptions.InternalCall)]
890 private extern bool WaitForExit_internal(IntPtr handle,
893 public void WaitForExit() {
894 WaitForExit_internal(process_handle, -1);
897 public bool WaitForExit(int milliseconds) {
898 if (milliseconds == int.MaxValue)
901 return WaitForExit_internal (process_handle, milliseconds);
905 public bool WaitForInputIdle() {
910 public bool WaitForInputIdle(int milliseconds) {
915 [MonitoringDescription ("Raised when this process exits.")]
916 public event EventHandler Exited;
918 // Closes the system process handle
919 [MethodImplAttribute(MethodImplOptions.InternalCall)]
920 private extern void Process_free_internal(IntPtr handle);
922 private bool disposed = false;
924 protected override void Dispose(bool disposing) {
925 // Check to see if Dispose has already been called.
926 if(this.disposed == false) {
928 // If this is a call to Dispose,
929 // dispose all managed resources.
934 // Release unmanaged resources
937 if(process_handle!=IntPtr.Zero) {
939 Process_free_internal(process_handle);
940 process_handle=IntPtr.Zero;
943 if (input_stream != null) {
944 input_stream.Close();
948 if (output_stream != null) {
949 output_stream.Close();
950 output_stream = null;
953 if (error_stream != null) {
954 error_stream.Close();
959 base.Dispose (disposing);
962 static void CBOnExit (object state, bool unused)
964 Process p = (Process) state;
968 protected void OnExited()
973 if (synchronizingObject == null) {
974 Exited (this, EventArgs.Empty);
978 object [] args = new object [] {this, EventArgs.Empty};
979 synchronizingObject.BeginInvoke (Exited, args);
982 class ProcessWaitHandle : WaitHandle
984 public ProcessWaitHandle (IntPtr handle)
989 protected override void Dispose (bool explicitDisposing)
991 // Do nothing, we don't own the handle and we won't close it.