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