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