* Test/System.Diagnostics/ProcessTest.cs: New tests for bug 477943.
[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                                         throw new Win32Exception (error);
512                         }
513                 }
514
515                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
516                 static extern int GetPriorityClass (IntPtr handle, out int error);
517
518                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
519                 static extern bool SetPriorityClass (IntPtr handle, int priority, out int error);
520
521                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
522                 [MonitoringDescription ("The amount of memory exclusively used by this process.")]
523 #if NET_2_0
524                 [Obsolete ("Use PrivateMemorySize64")]
525 #endif
526                 public int PrivateMemorySize {
527                         get {
528                                 int error;
529                                 return (int)GetProcessData (pid, 6, out error);
530                         }
531                 }
532
533 #if NET_2_0
534                 [MonoNotSupported ("")]
535                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
536                 [MonitoringDescription ("The session ID for this process.")]
537                 public int SessionId {
538                         get { throw new NotImplementedException (); }
539                 }
540 #endif
541
542                 /* the meaning of type is as follows: 0: user, 1: system, 2: total */
543                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
544                 private extern static long Times (IntPtr handle, int type);
545
546                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
547                 [MonitoringDescription ("The amount of processing time spent in the OS core for this process.")]
548                 public TimeSpan PrivilegedProcessorTime {
549                         get {
550                                 return new TimeSpan (Times (process_handle, 1));
551                         }
552                 }
553
554                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
555                 private extern static string ProcessName_internal(IntPtr handle);
556                 
557                 private string process_name=null;
558                 
559                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
560                 [MonitoringDescription ("The name of this process.")]
561                 public string ProcessName {
562                         get {
563                                 if(process_name==null) {
564                                         
565                                         if (process_handle == IntPtr.Zero)
566                                                 throw new InvalidOperationException ("No process is associated with this object.");
567                                         
568                                         process_name=ProcessName_internal(process_handle);
569                                         /* If process_name is _still_
570                                          * null, assume the process
571                                          * has exited
572                                          */
573                                         if (process_name == null)
574                                                 throw new InvalidOperationException ("Process has exited, so the requested information is not available.");
575                                         
576                                         /* Strip the suffix (if it
577                                          * exists) simplistically
578                                          * instead of removing any
579                                          * trailing \.???, so we dont
580                                          * get stupid results on sane
581                                          * systems
582                                          */
583                                         if(process_name.EndsWith(".exe") ||
584                                            process_name.EndsWith(".bat") ||
585                                            process_name.EndsWith(".com")) {
586                                                 process_name=process_name.Substring(0, process_name.Length-4);
587                                         }
588                                 }
589                                 return(process_name);
590                         }
591                 }
592
593                 [MonoTODO]
594                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
595                 [MonitoringDescription ("Allowed processor that can be used by this process.")]
596                 public IntPtr ProcessorAffinity {
597                         get {
598                                 return((IntPtr)0);
599                         }
600                         set {
601                         }
602                 }
603
604                 [MonoTODO]
605                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
606                 [MonitoringDescription ("Is this process responsive.")]
607                 public bool Responding {
608                         get {
609                                 return(false);
610                         }
611                 }
612
613                 private StreamReader error_stream=null;
614                 
615                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
616                 [MonitoringDescription ("The standard error stream of this process.")]
617                 public StreamReader StandardError {
618                         get {
619                                 if (error_stream == null)
620                                         throw new InvalidOperationException("Standard error has not been redirected");
621
622 #if NET_2_0
623                                 if ((async_mode & AsyncModes.AsyncError) != 0)
624                                         throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
625
626                                 async_mode |= AsyncModes.SyncError;
627 #endif
628
629                                 return(error_stream);
630                         }
631                 }
632
633                 private StreamWriter input_stream=null;
634                 
635                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
636                 [MonitoringDescription ("The standard input stream of this process.")]
637                 public StreamWriter StandardInput {
638                         get {
639                                 if (input_stream == null)
640                                         throw new InvalidOperationException("Standard input has not been redirected");
641
642                                 return(input_stream);
643                         }
644                 }
645
646                 private StreamReader output_stream=null;
647                 
648                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
649                 [MonitoringDescription ("The standard output stream of this process.")]
650                 public StreamReader StandardOutput {
651                         get {
652                                 if (output_stream == null)
653                                         throw new InvalidOperationException("Standard output has not been redirected");
654
655 #if NET_2_0
656                                 if ((async_mode & AsyncModes.AsyncOutput) != 0)
657                                         throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
658
659                                 async_mode |= AsyncModes.SyncOutput;
660 #endif
661
662                                 return(output_stream);
663                         }
664                 }
665
666                 private ProcessStartInfo start_info=null;
667                 
668                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content), Browsable (false)]
669                 [MonitoringDescription ("Information for the start of this process.")]
670                 public ProcessStartInfo StartInfo {
671                         get {
672                                 if (start_info == null)
673                                         start_info = new ProcessStartInfo();
674                                 return start_info;
675                         }
676                         set {
677                                 if (value == null)
678                                         throw new ArgumentNullException("value");
679                                 start_info = value;
680                         }
681                 }
682
683                 /* Returns the process start time in Windows file
684                  * times (ticks from DateTime(1/1/1601 00:00 GMT))
685                  */
686                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
687                 private extern static long StartTime_internal(IntPtr handle);
688                 
689                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
690                 [MonitoringDescription ("The time this process started.")]
691                 public DateTime StartTime {
692                         get {
693                                 return(DateTime.FromFileTime(StartTime_internal(process_handle)));
694                         }
695                 }
696
697                 [DefaultValue (null), Browsable (false)]
698                 [MonitoringDescription ("The object that is used to synchronize event handler calls for this process.")]
699                 public ISynchronizeInvoke SynchronizingObject {
700                         get { return synchronizingObject; }
701                         set { synchronizingObject = value; }
702                 }
703
704                 [MonoTODO]
705                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
706                 [MonitoringDescription ("The number of threads of this process.")]
707                 public ProcessThreadCollection Threads {
708                         get {
709                                 return ProcessThreadCollection.GetEmpty ();
710                         }
711                 }
712
713                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
714                 [MonitoringDescription ("The total CPU time spent for this process.")]
715                 public TimeSpan TotalProcessorTime {
716                         get {
717                                 return new TimeSpan (Times (process_handle, 2));
718                         }
719                 }
720
721                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
722                 [MonitoringDescription ("The CPU time spent for this process in user mode.")]
723                 public TimeSpan UserProcessorTime {
724                         get {
725                                 return new TimeSpan (Times (process_handle, 0));
726                         }
727                 }
728
729 #if NET_2_0
730                 [Obsolete ("Use VirtualMemorySize64")]
731 #endif
732                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
733                 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
734                 public int VirtualMemorySize {
735                         get {
736                                 int error;
737                                 return (int)GetProcessData (pid, 7, out error);
738                         }
739                 }
740
741 #if NET_2_0
742                 [Obsolete ("Use WorkingSet64")]
743 #endif
744                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
745                 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
746                 public int WorkingSet {
747                         get {
748                                 int error;
749                                 return (int)GetProcessData (pid, 4, out error);
750                         }
751                 }
752
753 #if NET_2_0
754                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
755                 [MonitoringDescription ("The amount of memory exclusively used by this process.")]
756                 [ComVisible (false)]
757                 public long PrivateMemorySize64 {
758                         get {
759                                 int error;
760                                 return GetProcessData (pid, 6, out error);
761                         }
762                 }
763
764                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
765                 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
766                 [ComVisible (false)]
767                 public long VirtualMemorySize64 {
768                         get {
769                                 int error;
770                                 return GetProcessData (pid, 7, out error);
771                         }
772                 }
773
774                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
775                 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
776                 [ComVisible (false)]
777                 public long WorkingSet64 {
778                         get {
779                                 int error;
780                                 return GetProcessData (pid, 4, out error);
781                         }
782                 }
783 #endif
784
785                 public void Close()
786                 {
787                         Dispose (true);
788                 }
789
790                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
791                 extern static bool Kill_internal (IntPtr handle, int signo);
792
793                 /* int kill -> 1 KILL, 2 CloseMainWindow */
794                 bool Close (int signo)
795                 {
796                         if (process_handle == IntPtr.Zero)
797                                 throw new SystemException ("No process to kill.");
798
799                         int exitcode = ExitCode_internal (process_handle);
800                         if (exitcode != 259)
801                                 throw new InvalidOperationException ("The process already finished.");
802
803                         return Kill_internal (process_handle, signo);
804                 }
805
806                 public bool CloseMainWindow ()
807                 {
808                         return Close (2);
809                 }
810
811                 [MonoTODO]
812                 public static void EnterDebugMode() {
813                 }
814
815                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
816                 private extern static IntPtr GetProcess_internal(int pid);
817                 
818                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
819                 private extern static int GetPid_internal();
820
821                 public static Process GetCurrentProcess()
822                 {
823                         int pid = GetPid_internal();
824                         IntPtr proc = GetProcess_internal(pid);
825                         
826                         if (proc == IntPtr.Zero)
827                                 throw new SystemException("Can't find current process");
828
829                         return (new Process (proc, pid));
830                 }
831
832                 public static Process GetProcessById(int processId)
833                 {
834                         IntPtr proc = GetProcess_internal(processId);
835                         
836                         if (proc == IntPtr.Zero)
837                                 throw new ArgumentException ("Can't find process with ID " + processId.ToString ());
838
839                         return (new Process (proc, processId));
840                 }
841
842                 [MonoTODO ("There is no support for retrieving process information from a remote machine")]
843                 public static Process GetProcessById(int processId, string machineName) {
844                         if (machineName == null)
845                                 throw new ArgumentNullException ("machineName");
846
847                         if (!IsLocalMachine (machineName))
848                                 throw new NotImplementedException ();
849
850                         return GetProcessById (processId);
851                 }
852
853                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
854                 private extern static int[] GetProcesses_internal();
855
856                 public static Process[] GetProcesses()
857                 {
858                         int [] pids = GetProcesses_internal ();
859                         ArrayList proclist = new ArrayList ();
860                         
861                         for (int i = 0; i < pids.Length; i++) {
862                                 try {
863                                         proclist.Add (GetProcessById (pids [i]));
864                                 } catch (SystemException) {
865                                         /* The process might exit
866                                          * between
867                                          * GetProcesses_internal and
868                                          * GetProcessById
869                                          */
870                                 }
871                         }
872
873                         return ((Process []) proclist.ToArray (typeof (Process)));
874                 }
875
876                 [MonoTODO ("There is no support for retrieving process information from a remote machine")]
877                 public static Process[] GetProcesses(string machineName) {
878                         if (machineName == null)
879                                 throw new ArgumentNullException ("machineName");
880
881                         if (!IsLocalMachine (machineName))
882                                 throw new NotImplementedException ();
883
884                         return GetProcesses ();
885                 }
886
887                 public static Process[] GetProcessesByName(string processName)
888                 {
889                         Process [] procs = GetProcesses();
890                         ArrayList proclist = new ArrayList();
891                         
892                         for (int i = 0; i < procs.Length; i++) {
893                                 /* Ignore case */
894                                 if (String.Compare (processName,
895                                                     procs [i].ProcessName,
896                                                     true) == 0) {
897                                         proclist.Add (procs [i]);
898                                 }
899                         }
900
901                         return ((Process[]) proclist.ToArray (typeof(Process)));
902                 }
903
904                 [MonoTODO]
905                 public static Process[] GetProcessesByName(string processName, string machineName) {
906                         throw new NotImplementedException();
907                 }
908
909                 public void Kill ()
910                 {
911                         Close (1);
912                 }
913
914                 [MonoTODO]
915                 public static void LeaveDebugMode() {
916                 }
917
918                 public void Refresh ()
919                 {
920                         // FIXME: should refresh any cached data we might have about
921                         // the process (currently we have none).
922                 }
923
924                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
925                 private extern static bool ShellExecuteEx_internal(ProcessStartInfo startInfo,
926                                                                    ref ProcInfo proc_info);
927
928                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
929                 private extern static bool CreateProcess_internal(ProcessStartInfo startInfo,
930                                                                   IntPtr stdin,
931                                                                   IntPtr stdout,
932                                                                   IntPtr stderr,
933                                                                   ref ProcInfo proc_info);
934
935                 private static bool Start_shell (ProcessStartInfo startInfo,
936                                                  Process process)
937                 {
938                         ProcInfo proc_info=new ProcInfo();
939                         bool ret;
940
941                         if (startInfo.RedirectStandardInput ||
942                             startInfo.RedirectStandardOutput ||
943                             startInfo.RedirectStandardError) {
944                                 throw new InvalidOperationException ("UseShellExecute must be false when redirecting I/O.");
945                         }
946
947                         if (startInfo.HaveEnvVars)
948                                 throw new InvalidOperationException ("UseShellExecute must be false in order to use environment variables.");
949
950                         FillUserInfo (startInfo, ref proc_info);
951                         try {
952                                 ret = ShellExecuteEx_internal (startInfo,
953                                                                ref proc_info);
954                         } finally {
955                                 if (proc_info.Password != IntPtr.Zero)
956                                         Marshal.FreeBSTR (proc_info.Password);
957                                 proc_info.Password = IntPtr.Zero;
958                         }
959                         if (!ret) {
960                                 throw new Win32Exception (-proc_info.pid);
961                         }
962
963                         process.process_handle = proc_info.process_handle;
964                         process.pid = proc_info.pid;
965
966                         process.StartExitCallbackIfNeeded ();
967
968                         return(ret);
969                 }
970
971                 private static bool Start_noshell (ProcessStartInfo startInfo,
972                                                    Process process)
973                 {
974                         ProcInfo proc_info=new ProcInfo();
975                         IntPtr stdin_rd = IntPtr.Zero, stdin_wr = IntPtr.Zero;
976                         IntPtr stdout_wr;
977                         IntPtr stderr_wr;
978                         bool ret;
979                         MonoIOError error;
980
981                         if (startInfo.HaveEnvVars) {
982                                 string [] strs = new string [startInfo.EnvironmentVariables.Count];
983                                 startInfo.EnvironmentVariables.Keys.CopyTo (strs, 0);
984                                 proc_info.envKeys = strs;
985
986                                 strs = new string [startInfo.EnvironmentVariables.Count];
987                                 startInfo.EnvironmentVariables.Values.CopyTo (strs, 0);
988                                 proc_info.envValues = strs;
989                         }
990
991                         if (startInfo.RedirectStandardInput == true) {
992                                 if (IsWindows) {
993                                         int DUPLICATE_SAME_ACCESS = 0x00000002;
994                                         IntPtr stdin_wr_tmp;
995
996                                         ret = MonoIO.CreatePipe (out stdin_rd,
997                                                                          out stdin_wr_tmp);
998                                         if (ret) {
999                                                 ret = MonoIO.DuplicateHandle (Process.GetCurrentProcess ().Handle, stdin_wr_tmp,
1000                                                 Process.GetCurrentProcess ().Handle, out stdin_wr, 0, 0, DUPLICATE_SAME_ACCESS);
1001                                                 MonoIO.Close (stdin_wr_tmp, out error);
1002                                         }
1003                                 }
1004                                 else
1005                                 {
1006                                         ret = MonoIO.CreatePipe (out stdin_rd,
1007                                                                          out stdin_wr);
1008                                 }
1009                                 if (ret == false) {
1010                                         throw new IOException ("Error creating standard input pipe");
1011                                 }
1012                         } else {
1013                                 stdin_rd = MonoIO.ConsoleInput;
1014                                 /* This is required to stop the
1015                                  * &$*£ing stupid compiler moaning
1016                                  * that stdin_wr is unassigned, below.
1017                                  */
1018                                 stdin_wr = (IntPtr)0;
1019                         }
1020
1021                         if (startInfo.RedirectStandardOutput == true) {
1022                                 IntPtr out_rd = IntPtr.Zero;
1023                                 if (IsWindows) {
1024                                         IntPtr out_rd_tmp;
1025                                         int DUPLICATE_SAME_ACCESS = 0x00000002;
1026
1027                                         ret = MonoIO.CreatePipe (out out_rd_tmp,
1028                                                                          out stdout_wr);
1029                                         if (ret) {
1030                                                 MonoIO.DuplicateHandle (Process.GetCurrentProcess ().Handle, out_rd_tmp,
1031                                                 Process.GetCurrentProcess ().Handle, out out_rd, 0, 0, DUPLICATE_SAME_ACCESS);
1032                                                 MonoIO.Close (out_rd_tmp, out error);
1033                                         }
1034                                 }
1035                                 else {
1036                                         ret = MonoIO.CreatePipe (out out_rd,
1037                                                                          out stdout_wr);
1038                                 }
1039
1040                                 process.stdout_rd = out_rd;
1041                                 if (ret == false) {
1042                                         if (startInfo.RedirectStandardInput == true) {
1043                                                 MonoIO.Close (stdin_rd, out error);
1044                                                 MonoIO.Close (stdin_wr, out error);
1045                                         }
1046
1047                                         throw new IOException ("Error creating standard output pipe");
1048                                 }
1049                         } else {
1050                                 process.stdout_rd = (IntPtr)0;
1051                                 stdout_wr = MonoIO.ConsoleOutput;
1052                         }
1053
1054                         if (startInfo.RedirectStandardError == true) {
1055                                 IntPtr err_rd = IntPtr.Zero;
1056                                 if (IsWindows) {
1057                                         IntPtr err_rd_tmp;
1058                                         int DUPLICATE_SAME_ACCESS = 0x00000002;
1059
1060                                         ret = MonoIO.CreatePipe (out err_rd_tmp,
1061                                                                          out stderr_wr);
1062                                         if (ret) {
1063                                                 MonoIO.DuplicateHandle (Process.GetCurrentProcess ().Handle, err_rd_tmp,
1064                                                 Process.GetCurrentProcess ().Handle, out err_rd, 0, 0, DUPLICATE_SAME_ACCESS);
1065                                                 MonoIO.Close (err_rd_tmp, out error);
1066                                         }
1067                                 }
1068                                 else {
1069                                         ret = MonoIO.CreatePipe (out err_rd,
1070                                                                          out stderr_wr);
1071                                 }
1072
1073                                 process.stderr_rd = err_rd;
1074                                 if (ret == false) {
1075                                         if (startInfo.RedirectStandardInput == true) {
1076                                                 MonoIO.Close (stdin_rd, out error);
1077                                                 MonoIO.Close (stdin_wr, out error);
1078                                         }
1079                                         if (startInfo.RedirectStandardOutput == true) {
1080                                                 MonoIO.Close (process.stdout_rd, out error);
1081                                                 MonoIO.Close (stdout_wr, out error);
1082                                         }
1083                                         
1084                                         throw new IOException ("Error creating standard error pipe");
1085                                 }
1086                         } else {
1087                                 process.stderr_rd = (IntPtr)0;
1088                                 stderr_wr = MonoIO.ConsoleError;
1089                         }
1090
1091                         FillUserInfo (startInfo, ref proc_info);
1092                         try {
1093                                 ret = CreateProcess_internal (startInfo,
1094                                                               stdin_rd, stdout_wr, stderr_wr,
1095                                                               ref proc_info);
1096                         } finally {
1097                                 if (proc_info.Password != IntPtr.Zero)
1098                                         Marshal.FreeBSTR (proc_info.Password);
1099                                 proc_info.Password = IntPtr.Zero;
1100                         }
1101                         if (!ret) {
1102                                 if (startInfo.RedirectStandardInput == true) {
1103                                         MonoIO.Close (stdin_rd, out error);
1104                                         MonoIO.Close (stdin_wr, out error);
1105                                 }
1106
1107                                 if (startInfo.RedirectStandardOutput == true) {
1108                                         MonoIO.Close (process.stdout_rd, out error);
1109                                         MonoIO.Close (stdout_wr, out error);
1110                                 }
1111
1112                                 if (startInfo.RedirectStandardError == true) {
1113                                         MonoIO.Close (process.stderr_rd, out error);
1114                                         MonoIO.Close (stderr_wr, out error);
1115                                 }
1116
1117                                 throw new Win32Exception (-proc_info.pid,
1118                                         "ApplicationName='" + startInfo.FileName +
1119                                         "', CommandLine='" + startInfo.Arguments +
1120                                         "', CurrentDirectory='" + startInfo.WorkingDirectory + "'");
1121                         }
1122
1123                         process.process_handle = proc_info.process_handle;
1124                         process.pid = proc_info.pid;
1125                         
1126                         if (startInfo.RedirectStandardInput == true) {
1127                                 MonoIO.Close (stdin_rd, out error);
1128                                 process.input_stream = new StreamWriter (new MonoSyncFileStream (stdin_wr, FileAccess.Write, true, 8192), Console.Out.Encoding);
1129                                 process.input_stream.AutoFlush = true;
1130                         }
1131
1132 #if NET_2_0
1133                         Encoding stdoutEncoding = startInfo.StandardOutputEncoding ?? Console.Out.Encoding;
1134                         Encoding stderrEncoding = startInfo.StandardErrorEncoding ?? Console.Out.Encoding;
1135 #else
1136                         Encoding stdoutEncoding = Console.Out.Encoding;
1137                         Encoding stderrEncoding = stdoutEncoding;
1138 #endif
1139
1140                         if (startInfo.RedirectStandardOutput == true) {
1141                                 MonoIO.Close (stdout_wr, out error);
1142                                 process.output_stream = new StreamReader (new MonoSyncFileStream (process.stdout_rd, FileAccess.Read, true, 8192), stdoutEncoding, true, 8192);
1143                         }
1144
1145                         if (startInfo.RedirectStandardError == true) {
1146                                 MonoIO.Close (stderr_wr, out error);
1147                                 process.error_stream = new StreamReader (new MonoSyncFileStream (process.stderr_rd, FileAccess.Read, true, 8192), stderrEncoding, true, 8192);
1148                         }
1149
1150                         process.StartExitCallbackIfNeeded ();
1151
1152                         return(ret);
1153                 }
1154
1155                 // Note that ProcInfo.Password must be freed.
1156                 private static void FillUserInfo (ProcessStartInfo startInfo, ref ProcInfo proc_info)
1157                 {
1158 #if NET_2_0
1159                         if (startInfo.UserName != null) {
1160                                 proc_info.UserName = startInfo.UserName;
1161                                 proc_info.Domain = startInfo.Domain;
1162                                 if (startInfo.Password != null)
1163                                         proc_info.Password = Marshal.SecureStringToBSTR (startInfo.Password);
1164                                 else
1165                                         proc_info.Password = IntPtr.Zero;
1166                                 proc_info.LoadUserProfile = startInfo.LoadUserProfile;
1167                         }
1168 #endif
1169                 }
1170
1171                 private static bool Start_common (ProcessStartInfo startInfo,
1172                                                   Process process)
1173                 {
1174                         if (startInfo.FileName == null || startInfo.FileName.Length == 0)
1175                                 throw new InvalidOperationException("File name has not been set");
1176                         
1177 #if NET_2_0
1178                         if (startInfo.StandardErrorEncoding != null && !startInfo.RedirectStandardError)
1179                                 throw new InvalidOperationException ("StandardErrorEncoding is only supported when standard error is redirected");
1180                         if (startInfo.StandardOutputEncoding != null && !startInfo.RedirectStandardOutput)
1181                                 throw new InvalidOperationException ("StandardOutputEncoding is only supported when standard output is redirected");
1182 #endif
1183                         
1184                         if (startInfo.UseShellExecute) {
1185 #if NET_2_0
1186                                 if (!String.IsNullOrEmpty (startInfo.UserName))
1187                                         throw new InvalidOperationException ("UserShellExecute must be false if an explicit UserName is specified when starting a process");
1188 #endif
1189                                 return (Start_shell (startInfo, process));
1190                         } else {
1191                                 return (Start_noshell (startInfo, process));
1192                         }
1193                 }
1194                 
1195                 public bool Start ()
1196                 {
1197                         if (process_handle != IntPtr.Zero) {
1198                                 Process_free_internal (process_handle);
1199                                 process_handle = IntPtr.Zero;
1200                         }
1201                         return Start_common(start_info, this);
1202                 }
1203
1204                 public static Process Start (ProcessStartInfo startInfo)
1205                 {
1206                         if (startInfo == null)
1207                                 throw new ArgumentNullException ("startInfo");
1208
1209                         Process process=new Process();
1210                         process.StartInfo = startInfo;
1211                         if (Start_common(startInfo, process))
1212                                 return process;
1213                         return null;
1214                 }
1215
1216                 public static Process Start (string fileName)
1217                 {
1218                         return Start (new ProcessStartInfo (fileName));
1219                 }
1220
1221                 public static Process Start(string fileName, string arguments)
1222                 {
1223                         return Start (new ProcessStartInfo (fileName, arguments));
1224                 }
1225
1226 #if NET_2_0
1227                 public static Process Start(string fileName, string username, SecureString password, string domain) {
1228                         return Start(fileName, null, username, password, domain);
1229                 }
1230
1231                 public static Process Start(string fileName, string arguments, string username, SecureString password, string domain) {
1232                         ProcessStartInfo psi = new ProcessStartInfo(fileName, arguments);
1233                         psi.UserName = username;
1234                         psi.Password = password;
1235                         psi.Domain = domain;
1236                         psi.UseShellExecute = false;
1237                         return Start(psi);
1238                 }
1239 #endif
1240
1241                 public override string ToString()
1242                 {
1243                         return(base.ToString() + " (" + this.ProcessName + ")");
1244                 }
1245
1246                 /* Waits up to ms milliseconds for process 'handle' to
1247                  * exit.  ms can be <0 to mean wait forever.
1248                  */
1249                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1250                 private extern bool WaitForExit_internal(IntPtr handle, int ms);
1251
1252                 public void WaitForExit ()
1253                 {
1254                         WaitForExit (-1);
1255                 }
1256
1257                 public bool WaitForExit(int milliseconds) {
1258                         int ms = milliseconds;
1259                         if (ms == int.MaxValue)
1260                                 ms = -1;
1261
1262 #if NET_2_0
1263                         DateTime start = DateTime.UtcNow;
1264                         if (async_output != null && !async_output.IsCompleted) {
1265                                 if (false == async_output.WaitHandle.WaitOne (ms, false))
1266                                         return false; // Timed out
1267
1268                                 if (ms >= 0) {
1269                                         DateTime now = DateTime.UtcNow;
1270                                         ms -= (int) (now - start).TotalMilliseconds;
1271                                         if (ms <= 0)
1272                                                 return false;
1273                                         start = now;
1274                                 }
1275                         }
1276
1277                         if (async_error != null && !async_error.IsCompleted) {
1278                                 if (false == async_error.WaitHandle.WaitOne (ms, false))
1279                                         return false; // Timed out
1280
1281                                 if (ms >= 0) {
1282                                         ms -= (int) (DateTime.UtcNow - start).TotalMilliseconds;
1283                                         if (ms <= 0)
1284                                                 return false;
1285                                 }
1286                         }
1287 #endif
1288                         return WaitForExit_internal (process_handle, ms);
1289                 }
1290
1291                 /* Waits up to ms milliseconds for process 'handle' to 
1292                  * wait for input.  ms can be <0 to mean wait forever.
1293                  */
1294                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1295                 private extern bool WaitForInputIdle_internal(IntPtr handle, int ms);
1296
1297                 // The internal call is only implemented properly on Windows.
1298                 [MonoTODO]
1299                 public bool WaitForInputIdle() {
1300                         return WaitForInputIdle (-1);
1301                 }
1302
1303                 // The internal call is only implemented properly on Windows.
1304                 [MonoTODO]
1305                 public bool WaitForInputIdle(int milliseconds) {
1306                         return WaitForInputIdle_internal (process_handle, milliseconds);
1307                 }
1308
1309                 private static bool IsLocalMachine (string machineName)
1310                 {
1311                         if (machineName == "." || machineName.Length == 0)
1312                                 return true;
1313
1314                         return (string.Compare (machineName, Environment.MachineName, true) == 0);
1315                 }
1316
1317 #if NET_2_0
1318                 [Browsable (true)]
1319                 [MonitoringDescription ("Raised when it receives output data")]
1320                 public event DataReceivedEventHandler OutputDataReceived;
1321                 [Browsable (true)]
1322                 [MonitoringDescription ("Raised when it receives error data")]
1323                 public event DataReceivedEventHandler ErrorDataReceived;
1324
1325                 void OnOutputDataReceived (string str)
1326                 {
1327                         if (OutputDataReceived != null)
1328                                 OutputDataReceived (this, new DataReceivedEventArgs (str));
1329                 }
1330
1331                 void OnErrorDataReceived (string str)
1332                 {
1333                         if (ErrorDataReceived != null)
1334                                 ErrorDataReceived (this, new DataReceivedEventArgs (str));
1335                 }
1336
1337                 [Flags]
1338                 enum AsyncModes {
1339                         NoneYet = 0,
1340                         SyncOutput = 1,
1341                         SyncError = 1 << 1,
1342                         AsyncOutput = 1 << 2,
1343                         AsyncError = 1 << 3
1344                 }
1345
1346                 [StructLayout (LayoutKind.Sequential)]
1347                 sealed class ProcessAsyncReader
1348                 {
1349                         /*
1350                            The following fields match those of SocketAsyncResult.
1351                            This is so that changes needed in the runtime to handle
1352                            asynchronous reads are trivial
1353                            Keep this in sync with SocketAsyncResult in 
1354                            ./System.Net.Sockets/Socket.cs and MonoSocketAsyncResult
1355                            in metadata/socket-io.h.
1356                         */
1357                         /* DON'T shuffle fields around. DON'T remove fields */
1358                         public object Sock;
1359                         public IntPtr handle;
1360                         public object state;
1361                         public AsyncCallback callback;
1362                         public ManualResetEvent wait_handle;
1363
1364                         public Exception delayedException;
1365
1366                         public object EndPoint;
1367                         byte [] buffer = new byte [4196];
1368                         public int Offset;
1369                         public int Size;
1370                         public int SockFlags;
1371
1372                         public object AcceptSocket;
1373                         public object[] Addresses;
1374                         public int port;
1375                         public object Buffers;          // Reserve this slot in older profiles
1376                         public bool ReuseSocket;        // Disconnect
1377                         public object acc_socket;
1378                         public int total;
1379                         public bool completed_sync;
1380                         bool completed;
1381                         bool err_out; // true -> stdout, false -> stderr
1382                         internal int error;
1383                         public int operation = 8; // MAGIC NUMBER: see Socket.cs:AsyncOperation
1384                         public object ares;
1385
1386
1387                         // These fields are not in SocketAsyncResult
1388                         Process process;
1389                         Stream stream;
1390                         StringBuilder sb = new StringBuilder ();
1391                         public AsyncReadHandler ReadHandler;
1392
1393                         public ProcessAsyncReader (Process process, IntPtr handle, bool err_out)
1394                         {
1395                                 this.process = process;
1396                                 this.handle = handle;
1397                                 stream = new FileStream (handle, FileAccess.Read, false);
1398                                 this.ReadHandler = new AsyncReadHandler (AddInput);
1399                                 this.err_out = err_out;
1400                         }
1401
1402                         public void AddInput ()
1403                         {
1404                                 lock (this) {
1405                                         int nread = stream.Read (buffer, 0, buffer.Length);
1406                                         if (nread == 0) {
1407                                                 completed = true;
1408                                                 if (wait_handle != null)
1409                                                         wait_handle.Set ();
1410                                                 FlushLast ();
1411                                                 return;
1412                                         }
1413
1414                                         try {
1415                                                 sb.Append (Encoding.Default.GetString (buffer, 0, nread));
1416                                         } catch {
1417                                                 // Just in case the encoding fails...
1418                                                 for (int i = 0; i < nread; i++) {
1419                                                         sb.Append ((char) buffer [i]);
1420                                                 }
1421                                         }
1422
1423                                         Flush (false);
1424                                         ReadHandler.BeginInvoke (null, this);
1425                                 }
1426                         }
1427
1428                         void FlushLast ()
1429                         {
1430                                 Flush (true);
1431                                 if (err_out) {
1432                                         process.OnOutputDataReceived (null);
1433                                 } else {
1434                                         process.OnErrorDataReceived (null);
1435                                 }
1436                         }
1437                         
1438                         void Flush (bool last)
1439                         {
1440                                 if (sb.Length == 0 ||
1441                                     (err_out && process.output_canceled) ||
1442                                     (!err_out && process.error_canceled))
1443                                         return;
1444
1445                                 string total = sb.ToString ();
1446                                 sb.Length = 0;
1447                                 string [] strs = total.Split ('\n');
1448                                 int len = strs.Length;
1449                                 if (len == 0)
1450                                         return;
1451
1452                                 for (int i = 0; i < len - 1; i++) {
1453                                         if (err_out)
1454                                                 process.OnOutputDataReceived (strs [i]);
1455                                         else
1456                                                 process.OnErrorDataReceived (strs [i]);
1457                                 }
1458
1459                                 string end = strs [len - 1];
1460                                 if (last || (len == 1 && end == "")) {
1461                                         if (err_out) {
1462                                                 process.OnOutputDataReceived (end);
1463                                         } else {
1464                                                 process.OnErrorDataReceived (end);
1465                                         }
1466                                 } else {
1467                                         sb.Append (end);
1468                                 }
1469                         }
1470
1471                         public bool IsCompleted {
1472                                 get { return completed; }
1473                         }
1474
1475                         public WaitHandle WaitHandle {
1476                                 get {
1477                                         lock (this) {
1478                                                 if (wait_handle == null)
1479                                                         wait_handle = new ManualResetEvent (completed);
1480                                                 return wait_handle;
1481                                         }
1482                                 }
1483                         }
1484                 }
1485
1486                 AsyncModes async_mode;
1487                 bool output_canceled;
1488                 bool error_canceled;
1489                 ProcessAsyncReader async_output;
1490                 ProcessAsyncReader async_error;
1491                 delegate void AsyncReadHandler ();
1492
1493                 [ComVisibleAttribute(false)] 
1494                 public void BeginOutputReadLine ()
1495                 {
1496                         if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1497                                 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1498
1499                         if ((async_mode & AsyncModes.SyncOutput) != 0)
1500                                 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
1501
1502                         async_mode |= AsyncModes.AsyncOutput;
1503                         output_canceled = false;
1504                         if (async_output == null) {
1505                                 async_output = new ProcessAsyncReader (this, stdout_rd, true);
1506                                 async_output.ReadHandler.BeginInvoke (null, async_output);
1507                         }
1508                 }
1509
1510                 [ComVisibleAttribute(false)] 
1511                 public void CancelOutputRead ()
1512                 {
1513                         if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1514                                 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1515
1516                         if ((async_mode & AsyncModes.SyncOutput) != 0)
1517                                 throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
1518
1519                         if (async_output == null)
1520                                 throw new InvalidOperationException ("No async operation in progress.");
1521
1522                         output_canceled = true;
1523                 }
1524
1525                 [ComVisibleAttribute(false)] 
1526                 public void BeginErrorReadLine ()
1527                 {
1528                         if (process_handle == IntPtr.Zero || error_stream == null || StartInfo.RedirectStandardError == false)
1529                                 throw new InvalidOperationException ("Standard error has not been redirected or process has not been started.");
1530
1531                         if ((async_mode & AsyncModes.SyncError) != 0)
1532                                 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
1533
1534                         async_mode |= AsyncModes.AsyncError;
1535                         error_canceled = false;
1536                         if (async_error == null) {
1537                                 async_error = new ProcessAsyncReader (this, stderr_rd, false);
1538                                 async_error.ReadHandler.BeginInvoke (null, async_error);
1539                         }
1540                 }
1541
1542                 [ComVisibleAttribute(false)] 
1543                 public void CancelErrorRead ()
1544                 {
1545                         if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1546                                 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1547
1548                         if ((async_mode & AsyncModes.SyncOutput) != 0)
1549                                 throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
1550
1551                         if (async_error == null)
1552                                 throw new InvalidOperationException ("No async operation in progress.");
1553
1554                         error_canceled = true;
1555                 }
1556 #endif
1557
1558                 [Category ("Behavior")]
1559                 [MonitoringDescription ("Raised when this process exits.")]
1560                 public event EventHandler Exited {
1561                         add {
1562                                 if (process_handle != IntPtr.Zero && HasExited) {
1563                                         value.BeginInvoke (null, null, null, null);
1564                                 } else {
1565                                         exited_event = (EventHandler) Delegate.Combine (exited_event, value);
1566                                         if (exited_event != null)
1567                                                 StartExitCallbackIfNeeded ();
1568                                 }
1569                         }
1570                         remove {
1571                                 exited_event = (EventHandler) Delegate.Remove (exited_event, value);
1572                         }
1573                 }
1574
1575                 // Closes the system process handle
1576                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1577                 private extern void Process_free_internal(IntPtr handle);
1578                 
1579                 private bool disposed = false;
1580                 
1581                 protected override void Dispose(bool disposing) {
1582                         // Check to see if Dispose has already been called.
1583                         if(this.disposed == false) {
1584                                 this.disposed=true;
1585                                 // If this is a call to Dispose,
1586                                 // dispose all managed resources.
1587                                 if(disposing) {
1588                                         // Do stuff here
1589                                 }
1590                                 
1591                                 // Release unmanaged resources
1592
1593                                 lock(this) {
1594                                         if(process_handle!=IntPtr.Zero) {
1595                                                 Process_free_internal(process_handle);
1596                                                 process_handle=IntPtr.Zero;
1597                                         }
1598
1599                                         if (input_stream != null) {
1600                                                 input_stream.Close();
1601                                                 input_stream = null;
1602                                         }
1603
1604                                         if (output_stream != null) {
1605                                                 output_stream.Close();
1606                                                 output_stream = null;
1607                                         }
1608
1609                                         if (error_stream != null) {
1610                                                 error_stream.Close();
1611                                                 error_stream = null;
1612                                         }
1613                                 }
1614                         }
1615                         base.Dispose (disposing);
1616                 }
1617
1618                 ~Process ()
1619                 {
1620                         Dispose (false);
1621                 }
1622
1623                 static void CBOnExit (object state, bool unused)
1624                 {
1625                         Process p = (Process) state;
1626                         p.OnExited ();
1627                 }
1628
1629                 protected void OnExited() 
1630                 {
1631                         if (exited_event == null)
1632                                 return;
1633
1634                         if (synchronizingObject == null) {
1635                                 foreach (EventHandler d in exited_event.GetInvocationList ()) {
1636                                         try {
1637                                                 d (this, EventArgs.Empty);
1638                                         } catch {}
1639                                 }
1640                                 return;
1641                         }
1642                         
1643                         object [] args = new object [] {this, EventArgs.Empty};
1644                         synchronizingObject.BeginInvoke (exited_event, args);
1645                 }
1646
1647                 static bool IsWindows
1648                 {
1649                         get
1650                         {
1651                                 PlatformID platform = Environment.OSVersion.Platform;
1652                                 if (platform == PlatformID.Win32S ||
1653                                         platform == PlatformID.Win32Windows ||
1654                                         platform == PlatformID.Win32NT ||
1655                                         platform == PlatformID.WinCE) {
1656                                         return true;
1657                                 }
1658                                 return false;
1659                         }
1660                 }
1661
1662                 class ProcessWaitHandle : WaitHandle
1663                 {
1664                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
1665                         private extern static IntPtr ProcessHandle_duplicate (IntPtr handle);
1666                         
1667                         public ProcessWaitHandle (IntPtr handle)
1668                         {
1669                                 // Need to keep a reference to this handle,
1670                                 // in case the Process object is collected
1671                                 Handle = ProcessHandle_duplicate (handle);
1672
1673                                 // When the wait handle is disposed, the duplicated handle will be
1674                                 // closed, so no need to override dispose (bug #464628).
1675                         }
1676                 }
1677         }
1678 }
1679