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