Merge pull request #228 from QuickJack/3e163743eda89cc8c239779a75dd245be12aee3c
[mono.git] / mcs / class / System / System.Diagnostics / Process.cs
1 //
2 // System.Diagnostics.Process.cs
3 //
4 // Authors:
5 //      Dick Porter (dick@ximian.com)
6 //      Andreas Nahr (ClassDevelopment@A-SoftTech.com)
7 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 //
9 // (C) 2002 Ximian, Inc.
10 // (C) 2003 Andreas Nahr
11 // (c) 2004,2005,2006 Novell, Inc. (http://www.novell.com)
12 //
13
14 //
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:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
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.
33 //
34
35 using System.IO;
36 using System.Text;
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;
45
46 namespace System.Diagnostics {
47
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)]
52 #if NET_2_0
53         [MonitoringDescription ("Represents a system process")]
54 #endif
55         public class Process : Component 
56         {
57                 [StructLayout(LayoutKind.Sequential)]
58                 private struct ProcInfo 
59                 {
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
65                          */
66                         public IntPtr thread_handle;
67                         public int pid; // Contains -GetLastError () on failure.
68                         public int tid;
69                         public string [] envKeys;
70                         public string [] envValues;
71                         public string UserName;
72                         public string Domain;
73                         public IntPtr Password;
74                         public bool LoadUserProfile;
75                 };
76                 
77                 IntPtr process_handle;
78                 int pid;
79                 bool enableRaisingEvents;
80                 bool already_waiting;
81                 ISynchronizeInvoke synchronizingObject;
82                 EventHandler exited_event;
83                 IntPtr stdout_rd;
84                 IntPtr stderr_rd;
85                 
86                 /* Private constructor called from other methods */
87                 private Process(IntPtr handle, int id) {
88                         process_handle=handle;
89                         pid=id;
90                 }
91                 
92                 public Process ()
93                 {
94                 }
95
96                 [MonoTODO]
97                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
98                 [MonitoringDescription ("Base process priority.")]
99                 public int BasePriority {
100                         get {
101                                 return(0);
102                         }
103                 }
104
105                 void StartExitCallbackIfNeeded ()
106                 {
107 #if !NET_2_1
108                         bool start = (!already_waiting && enableRaisingEvents && exited_event != null);
109                         if (start && process_handle != IntPtr.Zero) {
110                                 WaitOrTimerCallback cb = new WaitOrTimerCallback (CBOnExit);
111                                 ProcessWaitHandle h = new ProcessWaitHandle (process_handle);
112                                 ThreadPool.RegisterWaitForSingleObject (h, cb, this, -1, true);
113                                 already_waiting = true;
114                         }
115 #endif
116                 }
117
118                 [DefaultValue (false), Browsable (false)]
119                 [MonitoringDescription ("Check for exiting of the process to raise the apropriate event.")]
120                 public bool EnableRaisingEvents {
121                         get {
122                                 return enableRaisingEvents;
123                         }
124                         set { 
125                                 bool prev = enableRaisingEvents;
126                                 enableRaisingEvents = value;
127                                 if (enableRaisingEvents && !prev)
128                                         StartExitCallbackIfNeeded ();
129                         }
130
131                 }
132
133                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
134                 private extern static int ExitCode_internal(IntPtr handle);
135
136                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
137                 [MonitoringDescription ("The exit code of the process.")]
138                 public int ExitCode {
139                         get {
140                                 if (process_handle == IntPtr.Zero)
141                                         throw new InvalidOperationException ("Process has not been started.");
142
143                                 int code = ExitCode_internal (process_handle);
144                                 if (code == 259)
145                                         throw new InvalidOperationException ("The process must exit before " +
146                                                                         "getting the requested information.");
147
148                                 return code;
149                         }
150                 }
151
152                 /* Returns the process start time in Windows file
153                  * times (ticks from DateTime(1/1/1601 00:00 GMT))
154                  */
155                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
156                 private extern static long ExitTime_internal(IntPtr handle);
157                 
158                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
159                 [MonitoringDescription ("The exit time of the process.")]
160                 public DateTime ExitTime {
161                         get {
162                                 if (process_handle == IntPtr.Zero)
163                                         throw new InvalidOperationException ("Process has not been started.");
164
165                                 if (!HasExited)
166                                         throw new InvalidOperationException ("The process must exit before " +
167                                                                         "getting the requested information.");
168
169                                 return(DateTime.FromFileTime(ExitTime_internal(process_handle)));
170                         }
171                 }
172
173                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
174                 [MonitoringDescription ("Handle for this process.")]
175                 public IntPtr Handle {
176                         get {
177                                 return(process_handle);
178                         }
179                 }
180
181                 [MonoTODO]
182                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
183                 [MonitoringDescription ("Handles for this process.")]
184                 public int HandleCount {
185                         get {
186                                 return(0);
187                         }
188                 }
189
190                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
191                 [MonitoringDescription ("Determines if the process is still running.")]
192                 public bool HasExited {
193                         get {
194                                 if (process_handle == IntPtr.Zero)
195                                         throw new InvalidOperationException ("Process has not been started.");
196                                         
197                                 int exitcode = ExitCode_internal (process_handle);
198
199                                 if(exitcode==259) {
200                                         /* STILL_ACTIVE */
201                                         return(false);
202                                 } else {
203                                         return(true);
204                                 }
205                         }
206                 }
207
208                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
209                 [MonitoringDescription ("Process identifier.")]
210                 public int Id {
211                         get {
212                                 if (pid == 0)
213                                         throw new InvalidOperationException ("Process ID has not been set.");
214
215                                 return(pid);
216                         }
217                 }
218
219                 [MonoTODO]
220                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
221                 [MonitoringDescription ("The name of the computer running the process.")]
222                 public string MachineName {
223                         get {
224                                 return("localhost");
225                         }
226                 }
227
228                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
229                 [MonitoringDescription ("The main module of the process.")]
230                 public ProcessModule MainModule {
231                         get {
232                                 return(this.Modules[0]);
233                         }
234                 }
235
236                 [MonoTODO]
237                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
238                 [MonitoringDescription ("The handle of the main window of the process.")]
239                 public IntPtr MainWindowHandle {
240                         get {
241                                 return((IntPtr)0);
242                         }
243                 }
244
245                 [MonoTODO]
246                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
247                 [MonitoringDescription ("The title of the main window of the process.")]
248                 public string MainWindowTitle {
249                         get {
250                                 return("null");
251                         }
252                 }
253
254                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
255                 private extern static bool GetWorkingSet_internal(IntPtr handle, out int min, out int max);
256                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
257                 private extern static bool SetWorkingSet_internal(IntPtr handle, int min, int max, bool use_min);
258
259                 /* LAMESPEC: why is this an IntPtr not a plain int? */
260                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
261                 [MonitoringDescription ("The maximum working set for this process.")]
262                 public IntPtr MaxWorkingSet {
263                         get {
264                                 if(HasExited)
265                                         throw new InvalidOperationException(
266                                                 "The process " + ProcessName +
267                                                 " (ID " + Id + ") has exited");
268                                 
269                                 int min;
270                                 int max;
271                                 bool ok=GetWorkingSet_internal(process_handle, out min, out max);
272                                 if(ok==false) {
273                                         throw new Win32Exception();
274                                 }
275                                 
276                                 return((IntPtr)max);
277                         }
278                         set {
279                                 if(HasExited) {
280                                         throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
281                                 }
282                                 
283                                 bool ok=SetWorkingSet_internal(process_handle, 0, value.ToInt32(), false);
284                                 if(ok==false) {
285                                         throw new Win32Exception();
286                                 }
287                         }
288                 }
289
290                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
291                 [MonitoringDescription ("The minimum working set for this process.")]
292                 public IntPtr MinWorkingSet {
293                         get {
294                                 if(HasExited)
295                                         throw new InvalidOperationException(
296                                                 "The process " + ProcessName +
297                                                 " (ID " + Id + ") has exited");
298                                 
299                                 int min;
300                                 int max;
301                                 bool ok= GetWorkingSet_internal (process_handle, out min, out max);
302                                 if(!ok)
303                                         throw new Win32Exception();
304                                 return ((IntPtr) min);
305                         }
306                         set {
307                                 if(HasExited)
308                                         throw new InvalidOperationException(
309                                                 "The process " + ProcessName +
310                                                 " (ID " + Id + ") has exited");
311                                 
312                                 bool ok = SetWorkingSet_internal (process_handle, value.ToInt32(), 0, true);
313                                 if (!ok)
314                                         throw new Win32Exception();
315                         }
316                 }
317
318                 /* Returns the list of process modules.  The main module is
319                  * element 0.
320                  */
321                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
322                 private extern ProcessModule[] GetModules_internal(IntPtr handle);
323
324                 private ProcessModuleCollection module_collection;
325                 
326                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
327                 [MonitoringDescription ("The modules that are loaded as part of this process.")]
328                 public ProcessModuleCollection Modules {
329                         get {
330                                 if (module_collection == null)
331                                         module_collection = new ProcessModuleCollection(
332                                                 GetModules_internal (process_handle));
333                                 return(module_collection);
334                         }
335                 }
336
337                 /* data type is from the MonoProcessData enum in mono-proclib.h in the runtime */
338                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
339                 private extern static long GetProcessData (int pid, int data_type, out int error);
340
341                 [MonoTODO]
342 #if NET_2_0
343                 [Obsolete ("Use NonpagedSystemMemorySize64")]
344 #endif
345                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
346                 [MonitoringDescription ("The number of bytes that are not pageable.")]
347                 public int NonpagedSystemMemorySize {
348                         get {
349                                 return(0);
350                         }
351                 }
352
353                 [MonoTODO]
354 #if NET_2_0
355                 [Obsolete ("Use PagedMemorySize64")]
356 #endif
357                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
358                 [MonitoringDescription ("The number of bytes that are paged.")]
359                 public int PagedMemorySize {
360                         get {
361                                 return(0);
362                         }
363                 }
364
365                 [MonoTODO]
366 #if NET_2_0
367                 [Obsolete ("Use PagedSystemMemorySize64")]
368 #endif
369                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
370                 [MonitoringDescription ("The amount of paged system memory in bytes.")]
371                 public int PagedSystemMemorySize {
372                         get {
373                                 return(0);
374                         }
375                 }
376
377                 [MonoTODO]
378 #if NET_2_0
379                 [Obsolete ("Use PeakPagedMemorySize64")]
380 #endif
381                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
382                 [MonitoringDescription ("The maximum amount of paged memory used by this process.")]
383                 public int PeakPagedMemorySize {
384                         get {
385                                 return(0);
386                         }
387                 }
388
389 #if NET_2_0
390                 [Obsolete ("Use PeakVirtualMemorySize64")]
391 #endif
392                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
393                 [MonitoringDescription ("The maximum amount of virtual memory used by this process.")]
394                 public int PeakVirtualMemorySize {
395                         get {
396                                 int error;
397                                 return (int)GetProcessData (pid, 8, out error);
398                         }
399                 }
400
401 #if NET_2_0
402                 [Obsolete ("Use PeakWorkingSet64")]
403 #endif
404                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
405                 [MonitoringDescription ("The maximum amount of system memory used by this process.")]
406                 public int PeakWorkingSet {
407                         get {
408                                 int error;
409                                 return (int)GetProcessData (pid, 5, out error);
410                         }
411                 }
412
413 #if NET_2_0
414                 [MonoTODO]
415                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
416                 [MonitoringDescription ("The number of bytes that are not pageable.")]
417                 [ComVisible (false)]
418                 public long NonpagedSystemMemorySize64 {
419                         get {
420                                 return(0);
421                         }
422                 }
423
424                 [MonoTODO]
425                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
426                 [MonitoringDescription ("The number of bytes that are paged.")]
427                 [ComVisible (false)]
428                 public long PagedMemorySize64 {
429                         get {
430                                 return(0);
431                         }
432                 }
433
434                 [MonoTODO]
435                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
436                 [MonitoringDescription ("The amount of paged system memory in bytes.")]
437                 [ComVisible (false)]
438                 public long PagedSystemMemorySize64 {
439                         get {
440                                 return(0);
441                         }
442                 }
443
444                 [MonoTODO]
445                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
446                 [MonitoringDescription ("The maximum amount of paged memory used by this process.")]
447                 [ComVisible (false)]
448                 public long PeakPagedMemorySize64 {
449                         get {
450                                 return(0);
451                         }
452                 }
453
454                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
455                 [MonitoringDescription ("The maximum amount of virtual memory used by this process.")]
456                 [ComVisible (false)]
457                 public long PeakVirtualMemorySize64 {
458                         get {
459                                 int error;
460                                 return GetProcessData (pid, 8, out error);
461                         }
462                 }
463
464                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
465                 [MonitoringDescription ("The maximum amount of system memory used by this process.")]
466                 [ComVisible (false)]
467                 public long PeakWorkingSet64 {
468                         get {
469                                 int error;
470                                 return GetProcessData (pid, 5, out error);
471                         }
472                 }
473 #endif
474
475                 [MonoTODO]
476                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
477                 [MonitoringDescription ("Process will be of higher priority while it is actively used.")]
478                 public bool PriorityBoostEnabled {
479                         get {
480                                 return(false);
481                         }
482                         set {
483                         }
484                 }
485
486                 [MonoLimitation ("Under Unix, only root is allowed to raise the priority.")]
487                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
488                 [MonitoringDescription ("The relative process priority.")]
489                 public ProcessPriorityClass PriorityClass {
490                         get {
491                                 if (process_handle == IntPtr.Zero)
492                                         throw new InvalidOperationException ("Process has not been started.");
493                                 
494                                 int error;
495                                 int prio = GetPriorityClass (process_handle, out error);
496                                 if (prio == 0)
497                                         throw new Win32Exception (error);
498                                 return (ProcessPriorityClass) prio;
499                         }
500                         set {
501                                 if (!Enum.IsDefined (typeof (ProcessPriorityClass), value))
502                                         throw new InvalidEnumArgumentException (
503                                                 "value", (int) value,
504                                                 typeof (ProcessPriorityClass));
505
506                                 if (process_handle == IntPtr.Zero)
507                                         throw new InvalidOperationException ("Process has not been started.");
508                                 
509                                 int error;
510                                 if (!SetPriorityClass (process_handle, (int) value, out error)) {
511                                         CheckExited ();
512                                         throw new Win32Exception (error);
513                                 }
514                         }
515                 }
516
517                 void CheckExited () {
518                         if (HasExited)
519                                 throw new InvalidOperationException (String.Format ("Cannot process request because the process ({0}) has exited.", Id));
520                 }
521
522                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
523                 static extern int GetPriorityClass (IntPtr handle, out int error);
524
525                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
526                 static extern bool SetPriorityClass (IntPtr handle, int priority, out int error);
527
528                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
529                 [MonitoringDescription ("The amount of memory exclusively used by this process.")]
530 #if NET_2_0
531                 [Obsolete ("Use PrivateMemorySize64")]
532 #endif
533                 public int PrivateMemorySize {
534                         get {
535                                 int error;
536                                 return (int)GetProcessData (pid, 6, out error);
537                         }
538                 }
539
540 #if NET_2_0
541                 [MonoNotSupported ("")]
542                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
543                 [MonitoringDescription ("The session ID for this process.")]
544                 public int SessionId {
545                         get { throw new NotImplementedException (); }
546                 }
547 #endif
548
549                 /* the meaning of type is as follows: 0: user, 1: system, 2: total */
550                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
551                 private extern static long Times (IntPtr handle, int type);
552
553                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
554                 [MonitoringDescription ("The amount of processing time spent in the OS core for this process.")]
555                 public TimeSpan PrivilegedProcessorTime {
556                         get {
557                                 return new TimeSpan (Times (process_handle, 1));
558                         }
559                 }
560
561                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
562                 private extern static string ProcessName_internal(IntPtr handle);
563                 
564                 private string process_name=null;
565                 
566                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
567                 [MonitoringDescription ("The name of this process.")]
568                 public string ProcessName {
569                         get {
570                                 if(process_name==null) {
571                                         
572                                         if (process_handle == IntPtr.Zero)
573                                                 throw new InvalidOperationException ("No process is associated with this object.");
574                                         
575                                         process_name=ProcessName_internal(process_handle);
576                                         /* If process_name is _still_
577                                          * null, assume the process
578                                          * has exited
579                                          */
580                                         if (process_name == null)
581                                                 throw new InvalidOperationException ("Process has exited, so the requested information is not available.");
582                                         
583                                         /* Strip the suffix (if it
584                                          * exists) simplistically
585                                          * instead of removing any
586                                          * trailing \.???, so we dont
587                                          * get stupid results on sane
588                                          * systems
589                                          */
590                                         if(process_name.EndsWith(".exe") ||
591                                            process_name.EndsWith(".bat") ||
592                                            process_name.EndsWith(".com")) {
593                                                 process_name=process_name.Substring(0, process_name.Length-4);
594                                         }
595                                 }
596                                 return(process_name);
597                         }
598                 }
599
600                 [MonoTODO]
601                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
602                 [MonitoringDescription ("Allowed processor that can be used by this process.")]
603                 public IntPtr ProcessorAffinity {
604                         get {
605                                 return((IntPtr)0);
606                         }
607                         set {
608                         }
609                 }
610
611                 [MonoTODO]
612                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
613                 [MonitoringDescription ("Is this process responsive.")]
614                 public bool Responding {
615                         get {
616                                 return(false);
617                         }
618                 }
619
620                 private StreamReader error_stream=null;
621                 
622                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
623                 [MonitoringDescription ("The standard error stream of this process.")]
624                 public StreamReader StandardError {
625                         get {
626                                 if (error_stream == null)
627                                         throw new InvalidOperationException("Standard error has not been redirected");
628
629 #if NET_2_0
630                                 if ((async_mode & AsyncModes.AsyncError) != 0)
631                                         throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
632
633                                 async_mode |= AsyncModes.SyncError;
634 #endif
635
636                                 return(error_stream);
637                         }
638                 }
639
640                 private StreamWriter input_stream=null;
641                 
642                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
643                 [MonitoringDescription ("The standard input stream of this process.")]
644                 public StreamWriter StandardInput {
645                         get {
646                                 if (input_stream == null)
647                                         throw new InvalidOperationException("Standard input has not been redirected");
648
649                                 return(input_stream);
650                         }
651                 }
652
653                 private StreamReader output_stream=null;
654                 
655                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
656                 [MonitoringDescription ("The standard output stream of this process.")]
657                 public StreamReader StandardOutput {
658                         get {
659                                 if (output_stream == null)
660                                         throw new InvalidOperationException("Standard output has not been redirected");
661
662 #if NET_2_0
663                                 if ((async_mode & AsyncModes.AsyncOutput) != 0)
664                                         throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
665
666                                 async_mode |= AsyncModes.SyncOutput;
667 #endif
668
669                                 return(output_stream);
670                         }
671                 }
672
673                 private ProcessStartInfo start_info=null;
674                 
675                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content), Browsable (false)]
676                 [MonitoringDescription ("Information for the start of this process.")]
677                 public ProcessStartInfo StartInfo {
678                         get {
679                                 if (start_info == null)
680                                         start_info = new ProcessStartInfo();
681                                 return start_info;
682                         }
683                         set {
684                                 if (value == null)
685                                         throw new ArgumentNullException("value");
686                                 start_info = value;
687                         }
688                 }
689
690                 /* Returns the process start time in Windows file
691                  * times (ticks from DateTime(1/1/1601 00:00 GMT))
692                  */
693                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
694                 private extern static long StartTime_internal(IntPtr handle);
695                 
696                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
697                 [MonitoringDescription ("The time this process started.")]
698                 public DateTime StartTime {
699                         get {
700                                 return(DateTime.FromFileTime(StartTime_internal(process_handle)));
701                         }
702                 }
703
704                 [DefaultValue (null), Browsable (false)]
705                 [MonitoringDescription ("The object that is used to synchronize event handler calls for this process.")]
706                 public ISynchronizeInvoke SynchronizingObject {
707                         get { return synchronizingObject; }
708                         set { synchronizingObject = value; }
709                 }
710
711                 [MonoTODO]
712                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
713                 [MonitoringDescription ("The number of threads of this process.")]
714                 public ProcessThreadCollection Threads {
715                         get {
716                                 return ProcessThreadCollection.GetEmpty ();
717                         }
718                 }
719
720                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
721                 [MonitoringDescription ("The total CPU time spent for this process.")]
722                 public TimeSpan TotalProcessorTime {
723                         get {
724                                 return new TimeSpan (Times (process_handle, 2));
725                         }
726                 }
727
728                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
729                 [MonitoringDescription ("The CPU time spent for this process in user mode.")]
730                 public TimeSpan UserProcessorTime {
731                         get {
732                                 return new TimeSpan (Times (process_handle, 0));
733                         }
734                 }
735
736 #if NET_2_0
737                 [Obsolete ("Use VirtualMemorySize64")]
738 #endif
739                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
740                 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
741                 public int VirtualMemorySize {
742                         get {
743                                 int error;
744                                 return (int)GetProcessData (pid, 7, out error);
745                         }
746                 }
747
748 #if NET_2_0
749                 [Obsolete ("Use WorkingSet64")]
750 #endif
751                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
752                 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
753                 public int WorkingSet {
754                         get {
755                                 int error;
756                                 return (int)GetProcessData (pid, 4, out error);
757                         }
758                 }
759
760 #if NET_2_0
761                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
762                 [MonitoringDescription ("The amount of memory exclusively used by this process.")]
763                 [ComVisible (false)]
764                 public long PrivateMemorySize64 {
765                         get {
766                                 int error;
767                                 return GetProcessData (pid, 6, out error);
768                         }
769                 }
770
771                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
772                 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
773                 [ComVisible (false)]
774                 public long VirtualMemorySize64 {
775                         get {
776                                 int error;
777                                 return GetProcessData (pid, 7, out error);
778                         }
779                 }
780
781                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
782                 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
783                 [ComVisible (false)]
784                 public long WorkingSet64 {
785                         get {
786                                 int error;
787                                 return GetProcessData (pid, 4, out error);
788                         }
789                 }
790 #endif
791
792                 public void Close()
793                 {
794                         Dispose (true);
795                 }
796
797                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
798                 extern static bool Kill_internal (IntPtr handle, int signo);
799
800                 /* int kill -> 1 KILL, 2 CloseMainWindow */
801                 bool Close (int signo)
802                 {
803                         if (process_handle == IntPtr.Zero)
804                                 throw new SystemException ("No process to kill.");
805
806                         int exitcode = ExitCode_internal (process_handle);
807                         if (exitcode != 259)
808                                 throw new InvalidOperationException ("The process already finished.");
809
810                         return Kill_internal (process_handle, signo);
811                 }
812
813                 public bool CloseMainWindow ()
814                 {
815                         return Close (2);
816                 }
817
818                 [MonoTODO]
819                 public static void EnterDebugMode() {
820                 }
821
822                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
823                 private extern static IntPtr GetProcess_internal(int pid);
824                 
825                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
826                 private extern static int GetPid_internal();
827
828                 public static Process GetCurrentProcess()
829                 {
830                         int pid = GetPid_internal();
831                         IntPtr proc = GetProcess_internal(pid);
832                         
833                         if (proc == IntPtr.Zero)
834                                 throw new SystemException("Can't find current process");
835
836                         return (new Process (proc, pid));
837                 }
838
839                 public static Process GetProcessById(int processId)
840                 {
841                         IntPtr proc = GetProcess_internal(processId);
842                         
843                         if (proc == IntPtr.Zero)
844                                 throw new ArgumentException ("Can't find process with ID " + processId.ToString ());
845
846                         return (new Process (proc, processId));
847                 }
848
849                 [MonoTODO ("There is no support for retrieving process information from a remote machine")]
850                 public static Process GetProcessById(int processId, string machineName) {
851                         if (machineName == null)
852                                 throw new ArgumentNullException ("machineName");
853
854                         if (!IsLocalMachine (machineName))
855                                 throw new NotImplementedException ();
856
857                         return GetProcessById (processId);
858                 }
859
860                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
861                 private extern static int[] GetProcesses_internal();
862
863                 public static Process[] GetProcesses()
864                 {
865                         int [] pids = GetProcesses_internal ();
866                         if (pids == null)
867                                 return new Process [0];
868
869                         ArrayList proclist = new ArrayList (pids.Length);
870                         for (int i = 0; i < pids.Length; i++) {
871                                 try {
872                                         proclist.Add (GetProcessById (pids [i]));
873                                 } catch (SystemException) {
874                                         /* The process might exit
875                                          * between
876                                          * GetProcesses_internal and
877                                          * GetProcessById
878                                          */
879                                 }
880                         }
881
882                         return ((Process []) proclist.ToArray (typeof (Process)));
883                 }
884
885                 [MonoTODO ("There is no support for retrieving process information from a remote machine")]
886                 public static Process[] GetProcesses(string machineName) {
887                         if (machineName == null)
888                                 throw new ArgumentNullException ("machineName");
889
890                         if (!IsLocalMachine (machineName))
891                                 throw new NotImplementedException ();
892
893                         return GetProcesses ();
894                 }
895
896                 public static Process[] GetProcessesByName(string processName)
897                 {
898                         int [] pids = GetProcesses_internal ();
899                         if (pids == null)
900                                 return new Process [0];
901                         
902                         ArrayList proclist = new ArrayList (pids.Length);
903                         for (int i = 0; i < pids.Length; i++) {
904                                 try {
905                                         Process p = GetProcessById (pids [i]);
906                                         if (String.Compare (processName, p.ProcessName, true) == 0)
907                                                 proclist.Add (p);
908                                 } catch (SystemException) {
909                                         /* The process might exit
910                                          * between
911                                          * GetProcesses_internal and
912                                          * GetProcessById
913                                          */
914                                 }
915                         }
916
917                         return ((Process []) proclist.ToArray (typeof (Process)));
918                 }
919
920                 [MonoTODO]
921                 public static Process[] GetProcessesByName(string processName, string machineName) {
922                         throw new NotImplementedException();
923                 }
924
925                 public void Kill ()
926                 {
927                         Close (1);
928                 }
929
930                 [MonoTODO]
931                 public static void LeaveDebugMode() {
932                 }
933
934                 public void Refresh ()
935                 {
936                         // FIXME: should refresh any cached data we might have about
937                         // the process (currently we have none).
938                 }
939
940                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
941                 private extern static bool ShellExecuteEx_internal(ProcessStartInfo startInfo,
942                                                                    ref ProcInfo proc_info);
943
944                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
945                 private extern static bool CreateProcess_internal(ProcessStartInfo startInfo,
946                                                                   IntPtr stdin,
947                                                                   IntPtr stdout,
948                                                                   IntPtr stderr,
949                                                                   ref ProcInfo proc_info);
950
951                 private static bool Start_shell (ProcessStartInfo startInfo,
952                                                  Process process)
953                 {
954                         ProcInfo proc_info=new ProcInfo();
955                         bool ret;
956
957                         if (startInfo.RedirectStandardInput ||
958                             startInfo.RedirectStandardOutput ||
959                             startInfo.RedirectStandardError) {
960                                 throw new InvalidOperationException ("UseShellExecute must be false when redirecting I/O.");
961                         }
962
963                         if (startInfo.HaveEnvVars)
964                                 throw new InvalidOperationException ("UseShellExecute must be false in order to use environment variables.");
965
966                         FillUserInfo (startInfo, ref proc_info);
967                         try {
968                                 ret = ShellExecuteEx_internal (startInfo,
969                                                                ref proc_info);
970                         } finally {
971                                 if (proc_info.Password != IntPtr.Zero)
972                                         Marshal.FreeBSTR (proc_info.Password);
973                                 proc_info.Password = IntPtr.Zero;
974                         }
975                         if (!ret) {
976                                 throw new Win32Exception (-proc_info.pid);
977                         }
978
979                         process.process_handle = proc_info.process_handle;
980                         process.pid = proc_info.pid;
981
982                         process.StartExitCallbackIfNeeded ();
983
984                         return(ret);
985                 }
986
987                 private static bool Start_noshell (ProcessStartInfo startInfo,
988                                                    Process process)
989                 {
990                         ProcInfo proc_info=new ProcInfo();
991                         IntPtr stdin_rd = IntPtr.Zero, stdin_wr = IntPtr.Zero;
992                         IntPtr stdout_wr;
993                         IntPtr stderr_wr;
994                         bool ret;
995                         MonoIOError error;
996
997                         if (startInfo.HaveEnvVars) {
998                                 string [] strs = new string [startInfo.EnvironmentVariables.Count];
999                                 startInfo.EnvironmentVariables.Keys.CopyTo (strs, 0);
1000                                 proc_info.envKeys = strs;
1001
1002                                 strs = new string [startInfo.EnvironmentVariables.Count];
1003                                 startInfo.EnvironmentVariables.Values.CopyTo (strs, 0);
1004                                 proc_info.envValues = strs;
1005                         }
1006
1007                         if (startInfo.RedirectStandardInput == true) {
1008                                 if (IsWindows) {
1009                                         int DUPLICATE_SAME_ACCESS = 0x00000002;
1010                                         IntPtr stdin_wr_tmp;
1011
1012                                         ret = MonoIO.CreatePipe (out stdin_rd,
1013                                                                          out stdin_wr_tmp);
1014                                         if (ret) {
1015                                                 ret = MonoIO.DuplicateHandle (Process.GetCurrentProcess ().Handle, stdin_wr_tmp,
1016                                                 Process.GetCurrentProcess ().Handle, out stdin_wr, 0, 0, DUPLICATE_SAME_ACCESS);
1017                                                 MonoIO.Close (stdin_wr_tmp, out error);
1018                                         }
1019                                 }
1020                                 else
1021                                 {
1022                                         ret = MonoIO.CreatePipe (out stdin_rd,
1023                                                                          out stdin_wr);
1024                                 }
1025                                 if (ret == false) {
1026                                         throw new IOException ("Error creating standard input pipe");
1027                                 }
1028                         } else {
1029                                 stdin_rd = MonoIO.ConsoleInput;
1030                                 /* This is required to stop the
1031                                  * &$*£ing stupid compiler moaning
1032                                  * that stdin_wr is unassigned, below.
1033                                  */
1034                                 stdin_wr = (IntPtr)0;
1035                         }
1036
1037                         if (startInfo.RedirectStandardOutput == true) {
1038                                 IntPtr out_rd = IntPtr.Zero;
1039                                 if (IsWindows) {
1040                                         IntPtr out_rd_tmp;
1041                                         int DUPLICATE_SAME_ACCESS = 0x00000002;
1042
1043                                         ret = MonoIO.CreatePipe (out out_rd_tmp,
1044                                                                          out stdout_wr);
1045                                         if (ret) {
1046                                                 MonoIO.DuplicateHandle (Process.GetCurrentProcess ().Handle, out_rd_tmp,
1047                                                 Process.GetCurrentProcess ().Handle, out out_rd, 0, 0, DUPLICATE_SAME_ACCESS);
1048                                                 MonoIO.Close (out_rd_tmp, out error);
1049                                         }
1050                                 }
1051                                 else {
1052                                         ret = MonoIO.CreatePipe (out out_rd,
1053                                                                          out stdout_wr);
1054                                 }
1055
1056                                 process.stdout_rd = out_rd;
1057                                 if (ret == false) {
1058                                         if (startInfo.RedirectStandardInput == true) {
1059                                                 MonoIO.Close (stdin_rd, out error);
1060                                                 MonoIO.Close (stdin_wr, out error);
1061                                         }
1062
1063                                         throw new IOException ("Error creating standard output pipe");
1064                                 }
1065                         } else {
1066                                 process.stdout_rd = (IntPtr)0;
1067                                 stdout_wr = MonoIO.ConsoleOutput;
1068                         }
1069
1070                         if (startInfo.RedirectStandardError == true) {
1071                                 IntPtr err_rd = IntPtr.Zero;
1072                                 if (IsWindows) {
1073                                         IntPtr err_rd_tmp;
1074                                         int DUPLICATE_SAME_ACCESS = 0x00000002;
1075
1076                                         ret = MonoIO.CreatePipe (out err_rd_tmp,
1077                                                                          out stderr_wr);
1078                                         if (ret) {
1079                                                 MonoIO.DuplicateHandle (Process.GetCurrentProcess ().Handle, err_rd_tmp,
1080                                                 Process.GetCurrentProcess ().Handle, out err_rd, 0, 0, DUPLICATE_SAME_ACCESS);
1081                                                 MonoIO.Close (err_rd_tmp, out error);
1082                                         }
1083                                 }
1084                                 else {
1085                                         ret = MonoIO.CreatePipe (out err_rd,
1086                                                                          out stderr_wr);
1087                                 }
1088
1089                                 process.stderr_rd = err_rd;
1090                                 if (ret == false) {
1091                                         if (startInfo.RedirectStandardInput == true) {
1092                                                 MonoIO.Close (stdin_rd, out error);
1093                                                 MonoIO.Close (stdin_wr, out error);
1094                                         }
1095                                         if (startInfo.RedirectStandardOutput == true) {
1096                                                 MonoIO.Close (process.stdout_rd, out error);
1097                                                 MonoIO.Close (stdout_wr, out error);
1098                                         }
1099                                         
1100                                         throw new IOException ("Error creating standard error pipe");
1101                                 }
1102                         } else {
1103                                 process.stderr_rd = (IntPtr)0;
1104                                 stderr_wr = MonoIO.ConsoleError;
1105                         }
1106
1107                         FillUserInfo (startInfo, ref proc_info);
1108                         try {
1109                                 ret = CreateProcess_internal (startInfo,
1110                                                               stdin_rd, stdout_wr, stderr_wr,
1111                                                               ref proc_info);
1112                         } finally {
1113                                 if (proc_info.Password != IntPtr.Zero)
1114                                         Marshal.FreeBSTR (proc_info.Password);
1115                                 proc_info.Password = IntPtr.Zero;
1116                         }
1117                         if (!ret) {
1118                                 if (startInfo.RedirectStandardInput == true) {
1119                                         MonoIO.Close (stdin_rd, out error);
1120                                         MonoIO.Close (stdin_wr, out error);
1121                                 }
1122
1123                                 if (startInfo.RedirectStandardOutput == true) {
1124                                         MonoIO.Close (process.stdout_rd, out error);
1125                                         MonoIO.Close (stdout_wr, out error);
1126                                 }
1127
1128                                 if (startInfo.RedirectStandardError == true) {
1129                                         MonoIO.Close (process.stderr_rd, out error);
1130                                         MonoIO.Close (stderr_wr, out error);
1131                                 }
1132
1133                                 throw new Win32Exception (-proc_info.pid,
1134                                         "ApplicationName='" + startInfo.FileName +
1135                                         "', CommandLine='" + startInfo.Arguments +
1136                                         "', CurrentDirectory='" + startInfo.WorkingDirectory +
1137                                         "', Native error= " + Win32Exception.W32ErrorMessage (-proc_info.pid));
1138                         }
1139
1140                         process.process_handle = proc_info.process_handle;
1141                         process.pid = proc_info.pid;
1142                         
1143                         if (startInfo.RedirectStandardInput == true) {
1144                                 MonoIO.Close (stdin_rd, out error);
1145                                 process.input_stream = new StreamWriter (new MonoSyncFileStream (stdin_wr, FileAccess.Write, true, 8192), Console.Out.Encoding);
1146                                 process.input_stream.AutoFlush = true;
1147                         }
1148
1149 #if NET_2_0
1150                         Encoding stdoutEncoding = startInfo.StandardOutputEncoding ?? Console.Out.Encoding;
1151                         Encoding stderrEncoding = startInfo.StandardErrorEncoding ?? Console.Out.Encoding;
1152 #else
1153                         Encoding stdoutEncoding = Console.Out.Encoding;
1154                         Encoding stderrEncoding = stdoutEncoding;
1155 #endif
1156
1157                         if (startInfo.RedirectStandardOutput == true) {
1158                                 MonoIO.Close (stdout_wr, out error);
1159                                 process.output_stream = new StreamReader (new MonoSyncFileStream (process.stdout_rd, FileAccess.Read, true, 8192), stdoutEncoding, true, 8192);
1160                         }
1161
1162                         if (startInfo.RedirectStandardError == true) {
1163                                 MonoIO.Close (stderr_wr, out error);
1164                                 process.error_stream = new StreamReader (new MonoSyncFileStream (process.stderr_rd, FileAccess.Read, true, 8192), stderrEncoding, true, 8192);
1165                         }
1166
1167                         process.StartExitCallbackIfNeeded ();
1168
1169                         return(ret);
1170                 }
1171
1172                 // Note that ProcInfo.Password must be freed.
1173                 private static void FillUserInfo (ProcessStartInfo startInfo, ref ProcInfo proc_info)
1174                 {
1175 #if NET_2_0
1176                         if (startInfo.UserName != null) {
1177                                 proc_info.UserName = startInfo.UserName;
1178                                 proc_info.Domain = startInfo.Domain;
1179                                 if (startInfo.Password != null)
1180                                         proc_info.Password = Marshal.SecureStringToBSTR (startInfo.Password);
1181                                 else
1182                                         proc_info.Password = IntPtr.Zero;
1183                                 proc_info.LoadUserProfile = startInfo.LoadUserProfile;
1184                         }
1185 #endif
1186                 }
1187
1188                 private static bool Start_common (ProcessStartInfo startInfo,
1189                                                   Process process)
1190                 {
1191                         if (startInfo.FileName == null || startInfo.FileName.Length == 0)
1192                                 throw new InvalidOperationException("File name has not been set");
1193                         
1194 #if NET_2_0
1195                         if (startInfo.StandardErrorEncoding != null && !startInfo.RedirectStandardError)
1196                                 throw new InvalidOperationException ("StandardErrorEncoding is only supported when standard error is redirected");
1197                         if (startInfo.StandardOutputEncoding != null && !startInfo.RedirectStandardOutput)
1198                                 throw new InvalidOperationException ("StandardOutputEncoding is only supported when standard output is redirected");
1199 #endif
1200                         
1201                         if (startInfo.UseShellExecute) {
1202 #if NET_2_0
1203                                 if (!String.IsNullOrEmpty (startInfo.UserName))
1204                                         throw new InvalidOperationException ("UserShellExecute must be false if an explicit UserName is specified when starting a process");
1205 #endif
1206                                 return (Start_shell (startInfo, process));
1207                         } else {
1208                                 return (Start_noshell (startInfo, process));
1209                         }
1210                 }
1211                 
1212                 public bool Start ()
1213                 {
1214                         if (process_handle != IntPtr.Zero) {
1215                                 Process_free_internal (process_handle);
1216                                 process_handle = IntPtr.Zero;
1217                         }
1218                         return Start_common(start_info, this);
1219                 }
1220
1221                 public static Process Start (ProcessStartInfo startInfo)
1222                 {
1223                         if (startInfo == null)
1224                                 throw new ArgumentNullException ("startInfo");
1225
1226                         Process process=new Process();
1227                         process.StartInfo = startInfo;
1228                         if (Start_common(startInfo, process))
1229                                 return process;
1230                         return null;
1231                 }
1232
1233                 public static Process Start (string fileName)
1234                 {
1235                         return Start (new ProcessStartInfo (fileName));
1236                 }
1237
1238                 public static Process Start(string fileName, string arguments)
1239                 {
1240                         return Start (new ProcessStartInfo (fileName, arguments));
1241                 }
1242
1243 #if NET_2_0
1244                 public static Process Start(string fileName, string username, SecureString password, string domain) {
1245                         return Start(fileName, null, username, password, domain);
1246                 }
1247
1248                 public static Process Start(string fileName, string arguments, string username, SecureString password, string domain) {
1249                         ProcessStartInfo psi = new ProcessStartInfo(fileName, arguments);
1250                         psi.UserName = username;
1251                         psi.Password = password;
1252                         psi.Domain = domain;
1253                         psi.UseShellExecute = false;
1254                         return Start(psi);
1255                 }
1256 #endif
1257
1258                 public override string ToString()
1259                 {
1260                         return(base.ToString() + " (" + this.ProcessName + ")");
1261                 }
1262
1263                 /* Waits up to ms milliseconds for process 'handle' to
1264                  * exit.  ms can be <0 to mean wait forever.
1265                  */
1266                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1267                 private extern bool WaitForExit_internal(IntPtr handle, int ms);
1268
1269                 public void WaitForExit ()
1270                 {
1271                         WaitForExit (-1);
1272                 }
1273
1274                 public bool WaitForExit(int milliseconds) {
1275                         int ms = milliseconds;
1276                         if (ms == int.MaxValue)
1277                                 ms = -1;
1278
1279 #if NET_2_0
1280                         DateTime start = DateTime.UtcNow;
1281                         if (async_output != null && !async_output.IsCompleted) {
1282                                 if (false == async_output.WaitHandle.WaitOne (ms, false))
1283                                         return false; // Timed out
1284
1285                                 if (ms >= 0) {
1286                                         DateTime now = DateTime.UtcNow;
1287                                         ms -= (int) (now - start).TotalMilliseconds;
1288                                         if (ms <= 0)
1289                                                 return false;
1290                                         start = now;
1291                                 }
1292                         }
1293
1294                         if (async_error != null && !async_error.IsCompleted) {
1295                                 if (false == async_error.WaitHandle.WaitOne (ms, false))
1296                                         return false; // Timed out
1297
1298                                 if (ms >= 0) {
1299                                         ms -= (int) (DateTime.UtcNow - start).TotalMilliseconds;
1300                                         if (ms <= 0)
1301                                                 return false;
1302                                 }
1303                         }
1304 #endif
1305                         return WaitForExit_internal (process_handle, ms);
1306                 }
1307
1308                 /* Waits up to ms milliseconds for process 'handle' to 
1309                  * wait for input.  ms can be <0 to mean wait forever.
1310                  */
1311                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1312                 private extern bool WaitForInputIdle_internal(IntPtr handle, int ms);
1313
1314                 // The internal call is only implemented properly on Windows.
1315                 [MonoTODO]
1316                 public bool WaitForInputIdle() {
1317                         return WaitForInputIdle (-1);
1318                 }
1319
1320                 // The internal call is only implemented properly on Windows.
1321                 [MonoTODO]
1322                 public bool WaitForInputIdle(int milliseconds) {
1323                         return WaitForInputIdle_internal (process_handle, milliseconds);
1324                 }
1325
1326                 private static bool IsLocalMachine (string machineName)
1327                 {
1328                         if (machineName == "." || machineName.Length == 0)
1329                                 return true;
1330
1331                         return (string.Compare (machineName, Environment.MachineName, true) == 0);
1332                 }
1333
1334 #if NET_2_0
1335                 [Browsable (true)]
1336                 [MonitoringDescription ("Raised when it receives output data")]
1337                 public event DataReceivedEventHandler OutputDataReceived;
1338                 [Browsable (true)]
1339                 [MonitoringDescription ("Raised when it receives error data")]
1340                 public event DataReceivedEventHandler ErrorDataReceived;
1341
1342                 void OnOutputDataReceived (string str)
1343                 {
1344                         if (OutputDataReceived != null)
1345                                 OutputDataReceived (this, new DataReceivedEventArgs (str));
1346                 }
1347
1348                 void OnErrorDataReceived (string str)
1349                 {
1350                         if (ErrorDataReceived != null)
1351                                 ErrorDataReceived (this, new DataReceivedEventArgs (str));
1352                 }
1353
1354                 [Flags]
1355                 enum AsyncModes {
1356                         NoneYet = 0,
1357                         SyncOutput = 1,
1358                         SyncError = 1 << 1,
1359                         AsyncOutput = 1 << 2,
1360                         AsyncError = 1 << 3
1361                 }
1362
1363                 [StructLayout (LayoutKind.Sequential)]
1364                 sealed class ProcessAsyncReader
1365                 {
1366                         /*
1367                            The following fields match those of SocketAsyncResult.
1368                            This is so that changes needed in the runtime to handle
1369                            asynchronous reads are trivial
1370                            Keep this in sync with SocketAsyncResult in 
1371                            ./System.Net.Sockets/Socket.cs and MonoSocketAsyncResult
1372                            in metadata/socket-io.h.
1373                         */
1374                         /* DON'T shuffle fields around. DON'T remove fields */
1375                         public object Sock;
1376                         public IntPtr handle;
1377                         public object state;
1378                         public AsyncCallback callback;
1379                         public ManualResetEvent wait_handle;
1380
1381                         public Exception delayedException;
1382
1383                         public object EndPoint;
1384                         byte [] buffer = new byte [4196];
1385                         public int Offset;
1386                         public int Size;
1387                         public int SockFlags;
1388
1389                         public object AcceptSocket;
1390                         public object[] Addresses;
1391                         public int port;
1392                         public object Buffers;          // Reserve this slot in older profiles
1393                         public bool ReuseSocket;        // Disconnect
1394                         public object acc_socket;
1395                         public int total;
1396                         public bool completed_sync;
1397                         bool completed;
1398                         bool err_out; // true -> stdout, false -> stderr
1399                         internal int error;
1400                         public int operation = 8; // MAGIC NUMBER: see Socket.cs:AsyncOperation
1401                         public object ares;
1402                         public int EndCalled;
1403
1404                         // These fields are not in SocketAsyncResult
1405                         Process process;
1406                         Stream stream;
1407                         StringBuilder sb = new StringBuilder ();
1408                         public AsyncReadHandler ReadHandler;
1409
1410                         public ProcessAsyncReader (Process process, IntPtr handle, bool err_out)
1411                         {
1412                                 this.process = process;
1413                                 this.handle = handle;
1414                                 stream = new FileStream (handle, FileAccess.Read, false);
1415                                 this.ReadHandler = new AsyncReadHandler (AddInput);
1416                                 this.err_out = err_out;
1417                         }
1418
1419                         public void AddInput ()
1420                         {
1421                                 lock (this) {
1422                                         int nread = stream.Read (buffer, 0, buffer.Length);
1423                                         if (nread == 0) {
1424                                                 completed = true;
1425                                                 if (wait_handle != null)
1426                                                         wait_handle.Set ();
1427                                                 FlushLast ();
1428                                                 return;
1429                                         }
1430
1431                                         try {
1432                                                 sb.Append (Encoding.Default.GetString (buffer, 0, nread));
1433                                         } catch {
1434                                                 // Just in case the encoding fails...
1435                                                 for (int i = 0; i < nread; i++) {
1436                                                         sb.Append ((char) buffer [i]);
1437                                                 }
1438                                         }
1439
1440                                         Flush (false);
1441                                         ReadHandler.BeginInvoke (null, this);
1442                                 }
1443                         }
1444
1445                         void FlushLast ()
1446                         {
1447                                 Flush (true);
1448                                 if (err_out) {
1449                                         process.OnOutputDataReceived (null);
1450                                 } else {
1451                                         process.OnErrorDataReceived (null);
1452                                 }
1453                         }
1454                         
1455                         void Flush (bool last)
1456                         {
1457                                 if (sb.Length == 0 ||
1458                                     (err_out && process.output_canceled) ||
1459                                     (!err_out && process.error_canceled))
1460                                         return;
1461
1462                                 string total = sb.ToString ();
1463                                 sb.Length = 0;
1464                                 string [] strs = total.Split ('\n');
1465                                 int len = strs.Length;
1466                                 if (len == 0)
1467                                         return;
1468
1469                                 for (int i = 0; i < len - 1; i++) {
1470                                         if (err_out)
1471                                                 process.OnOutputDataReceived (strs [i]);
1472                                         else
1473                                                 process.OnErrorDataReceived (strs [i]);
1474                                 }
1475
1476                                 string end = strs [len - 1];
1477                                 if (last || (len == 1 && end == "")) {
1478                                         if (err_out) {
1479                                                 process.OnOutputDataReceived (end);
1480                                         } else {
1481                                                 process.OnErrorDataReceived (end);
1482                                         }
1483                                 } else {
1484                                         sb.Append (end);
1485                                 }
1486                         }
1487
1488                         public bool IsCompleted {
1489                                 get { return completed; }
1490                         }
1491
1492                         public WaitHandle WaitHandle {
1493                                 get {
1494                                         lock (this) {
1495                                                 if (wait_handle == null)
1496                                                         wait_handle = new ManualResetEvent (completed);
1497                                                 return wait_handle;
1498                                         }
1499                                 }
1500                         }
1501
1502                         public void Close () {
1503                                 stream.Close ();
1504                         }
1505                 }
1506
1507                 AsyncModes async_mode;
1508                 bool output_canceled;
1509                 bool error_canceled;
1510                 ProcessAsyncReader async_output;
1511                 ProcessAsyncReader async_error;
1512                 delegate void AsyncReadHandler ();
1513
1514                 [ComVisibleAttribute(false)] 
1515                 public void BeginOutputReadLine ()
1516                 {
1517                         if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1518                                 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1519
1520                         if ((async_mode & AsyncModes.SyncOutput) != 0)
1521                                 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
1522
1523                         async_mode |= AsyncModes.AsyncOutput;
1524                         output_canceled = false;
1525                         if (async_output == null) {
1526                                 async_output = new ProcessAsyncReader (this, stdout_rd, true);
1527                                 async_output.ReadHandler.BeginInvoke (null, async_output);
1528                         }
1529                 }
1530
1531                 [ComVisibleAttribute(false)] 
1532                 public void CancelOutputRead ()
1533                 {
1534                         if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1535                                 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1536
1537                         if ((async_mode & AsyncModes.SyncOutput) != 0)
1538                                 throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
1539
1540                         if (async_output == null)
1541                                 throw new InvalidOperationException ("No async operation in progress.");
1542
1543                         output_canceled = true;
1544                 }
1545
1546                 [ComVisibleAttribute(false)] 
1547                 public void BeginErrorReadLine ()
1548                 {
1549                         if (process_handle == IntPtr.Zero || error_stream == null || StartInfo.RedirectStandardError == false)
1550                                 throw new InvalidOperationException ("Standard error has not been redirected or process has not been started.");
1551
1552                         if ((async_mode & AsyncModes.SyncError) != 0)
1553                                 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
1554
1555                         async_mode |= AsyncModes.AsyncError;
1556                         error_canceled = false;
1557                         if (async_error == null) {
1558                                 async_error = new ProcessAsyncReader (this, stderr_rd, false);
1559                                 async_error.ReadHandler.BeginInvoke (null, async_error);
1560                         }
1561                 }
1562
1563                 [ComVisibleAttribute(false)] 
1564                 public void CancelErrorRead ()
1565                 {
1566                         if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1567                                 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1568
1569                         if ((async_mode & AsyncModes.SyncOutput) != 0)
1570                                 throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
1571
1572                         if (async_error == null)
1573                                 throw new InvalidOperationException ("No async operation in progress.");
1574
1575                         error_canceled = true;
1576                 }
1577 #endif
1578
1579                 [Category ("Behavior")]
1580                 [MonitoringDescription ("Raised when this process exits.")]
1581                 public event EventHandler Exited {
1582                         add {
1583                                 if (process_handle != IntPtr.Zero && HasExited) {
1584                                         value.BeginInvoke (null, null, null, null);
1585                                 } else {
1586                                         exited_event = (EventHandler) Delegate.Combine (exited_event, value);
1587                                         if (exited_event != null)
1588                                                 StartExitCallbackIfNeeded ();
1589                                 }
1590                         }
1591                         remove {
1592                                 exited_event = (EventHandler) Delegate.Remove (exited_event, value);
1593                         }
1594                 }
1595
1596                 // Closes the system process handle
1597                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1598                 private extern void Process_free_internal(IntPtr handle);
1599                 
1600                 private bool disposed = false;
1601                 
1602                 protected override void Dispose(bool disposing) {
1603                         // Check to see if Dispose has already been called.
1604                         if(this.disposed == false) {
1605                                 this.disposed=true;
1606                                 // If this is a call to Dispose,
1607                                 // dispose all managed resources.
1608                                 if(disposing) {
1609                                         // Do stuff here
1610                                         lock (this) {
1611                                                 /* These have open FileStreams on the pipes we are about to close */
1612                                                 if (async_output != null)
1613                                                         async_output.Close ();
1614                                                 if (async_error != null)
1615                                                         async_error.Close ();
1616                                         }
1617                                 }
1618                                 
1619                                 // Release unmanaged resources
1620
1621                                 lock(this) {
1622                                         if(process_handle!=IntPtr.Zero) {
1623                                                 Process_free_internal(process_handle);
1624                                                 process_handle=IntPtr.Zero;
1625                                         }
1626
1627                                         if (input_stream != null) {
1628                                                 input_stream.Close();
1629                                                 input_stream = null;
1630                                         }
1631
1632                                         if (output_stream != null) {
1633                                                 output_stream.Close();
1634                                                 output_stream = null;
1635                                         }
1636
1637                                         if (error_stream != null) {
1638                                                 error_stream.Close();
1639                                                 error_stream = null;
1640                                         }
1641                                 }
1642                         }
1643                         base.Dispose (disposing);
1644                 }
1645
1646                 ~Process ()
1647                 {
1648                         Dispose (false);
1649                 }
1650
1651                 static void CBOnExit (object state, bool unused)
1652                 {
1653                         Process p = (Process) state;
1654                         p.already_waiting = false;
1655                         p.OnExited ();
1656                 }
1657
1658                 protected void OnExited() 
1659                 {
1660                         if (exited_event == null)
1661                                 return;
1662
1663                         if (synchronizingObject == null) {
1664                                 foreach (EventHandler d in exited_event.GetInvocationList ()) {
1665                                         try {
1666                                                 d (this, EventArgs.Empty);
1667                                         } catch {}
1668                                 }
1669                                 return;
1670                         }
1671                         
1672                         object [] args = new object [] {this, EventArgs.Empty};
1673                         synchronizingObject.BeginInvoke (exited_event, args);
1674                 }
1675
1676                 static bool IsWindows
1677                 {
1678                         get
1679                         {
1680                                 PlatformID platform = Environment.OSVersion.Platform;
1681                                 if (platform == PlatformID.Win32S ||
1682                                         platform == PlatformID.Win32Windows ||
1683                                         platform == PlatformID.Win32NT ||
1684                                         platform == PlatformID.WinCE) {
1685                                         return true;
1686                                 }
1687                                 return false;
1688                         }
1689                 }
1690
1691                 class ProcessWaitHandle : WaitHandle
1692                 {
1693                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
1694                         private extern static IntPtr ProcessHandle_duplicate (IntPtr handle);
1695                         
1696                         public ProcessWaitHandle (IntPtr handle)
1697                         {
1698                                 // Need to keep a reference to this handle,
1699                                 // in case the Process object is collected
1700                                 Handle = ProcessHandle_duplicate (handle);
1701
1702                                 // When the wait handle is disposed, the duplicated handle will be
1703                                 // closed, so no need to override dispose (bug #464628).
1704                         }
1705                 }
1706         }
1707 }
1708