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