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