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