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