Merge pull request #1860 from saper/tz-fix
[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                 bool error_stream_exposed;
620
621                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
622                 [MonitoringDescription ("The standard error stream of this process.")]
623                 public StreamReader StandardError {
624                         get {
625                                 if (error_stream == null)
626                                         throw new InvalidOperationException("Standard error has not been redirected");
627
628                                 if ((async_mode & AsyncModes.AsyncError) != 0)
629                                         throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
630
631                                 async_mode |= AsyncModes.SyncError;
632
633                                 error_stream_exposed = true;
634                                 return(error_stream);
635                         }
636                 }
637
638                 private StreamWriter input_stream=null;
639                 bool input_stream_exposed;
640                 
641                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
642                 [MonitoringDescription ("The standard input stream of this process.")]
643                 public StreamWriter StandardInput {
644                         get {
645                                 if (input_stream == null)
646                                         throw new InvalidOperationException("Standard input has not been redirected");
647
648                                 input_stream_exposed = true;
649                                 return(input_stream);
650                         }
651                 }
652
653                 private StreamReader output_stream=null;
654                 bool output_stream_exposed;
655                 
656                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
657                 [MonitoringDescription ("The standard output stream of this process.")]
658                 public StreamReader StandardOutput {
659                         get {
660                                 if (output_stream == null)
661                                         throw new InvalidOperationException("Standard output has not been redirected");
662
663                                 if ((async_mode & AsyncModes.AsyncOutput) != 0)
664                                         throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
665
666                                 async_mode |= AsyncModes.SyncOutput;
667
668                                 output_stream_exposed = true;
669                                 return(output_stream);
670                         }
671                 }
672
673                 private ProcessStartInfo start_info=null;
674                 
675                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content), Browsable (false)]
676                 [MonitoringDescription ("Information for the start of this process.")]
677                 public ProcessStartInfo StartInfo {
678                         get {
679                                 if (start_info == null)
680                                         start_info = new ProcessStartInfo();
681                                 return start_info;
682                         }
683                         set {
684                                 if (value == null)
685                                         throw new ArgumentNullException("value");
686                                 start_info = value;
687                         }
688                 }
689
690                 /* Returns the process start time in Windows file
691                  * times (ticks from DateTime(1/1/1601 00:00 GMT))
692                  */
693                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
694                 private extern static long StartTime_internal(IntPtr handle);
695                 
696                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
697                 [MonitoringDescription ("The time this process started.")]
698                 public DateTime StartTime {
699                         get {
700                                 return(DateTime.FromFileTime(StartTime_internal(process_handle)));
701                         }
702                 }
703
704                 [DefaultValue (null), Browsable (false)]
705                 [MonitoringDescription ("The object that is used to synchronize event handler calls for this process.")]
706                 public ISynchronizeInvoke SynchronizingObject {
707                         get { return synchronizingObject; }
708                         set { synchronizingObject = value; }
709                 }
710
711                 [MonoTODO]
712                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
713                 [MonitoringDescription ("The number of threads of this process.")]
714                 public ProcessThreadCollection Threads {
715                         get {
716                                 // This'll return a correctly-sized array of empty ProcessThreads for now.
717                                 int error;
718                                 return new ProcessThreadCollection(new ProcessThread[GetProcessData (pid, 0, out error)]);
719                         }
720                 }
721
722                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
723                 [MonitoringDescription ("The total CPU time spent for this process.")]
724                 public TimeSpan TotalProcessorTime {
725                         get {
726                                 return new TimeSpan (Times (process_handle, 2));
727                         }
728                 }
729
730                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
731                 [MonitoringDescription ("The CPU time spent for this process in user mode.")]
732                 public TimeSpan UserProcessorTime {
733                         get {
734                                 return new TimeSpan (Times (process_handle, 0));
735                         }
736                 }
737
738                 [Obsolete ("Use VirtualMemorySize64")]
739                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
740                 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
741                 public int VirtualMemorySize {
742                         get {
743                                 int error;
744                                 return (int)GetProcessData (pid, 7, out error);
745                         }
746                 }
747
748                 [Obsolete ("Use WorkingSet64")]
749                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
750                 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
751                 public int WorkingSet {
752                         get {
753                                 int error;
754                                 return (int)GetProcessData (pid, 4, out error);
755                         }
756                 }
757
758                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
759                 [MonitoringDescription ("The amount of memory exclusively used by this process.")]
760                 [ComVisible (false)]
761                 public long PrivateMemorySize64 {
762                         get {
763                                 int error;
764                                 return GetProcessData (pid, 6, out error);
765                         }
766                 }
767
768                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
769                 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
770                 [ComVisible (false)]
771                 public long VirtualMemorySize64 {
772                         get {
773                                 int error;
774                                 return GetProcessData (pid, 7, out error);
775                         }
776                 }
777
778                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
779                 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
780                 [ComVisible (false)]
781                 public long WorkingSet64 {
782                         get {
783                                 int error;
784                                 return GetProcessData (pid, 4, out error);
785                         }
786                 }
787
788                 public void Close()
789                 {
790                         Dispose (true);
791                 }
792
793                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
794                 extern static bool Kill_internal (IntPtr handle, int signo);
795
796                 /* int kill -> 1 KILL, 2 CloseMainWindow */
797                 bool Close (int signo)
798                 {
799                         if (process_handle == IntPtr.Zero)
800                                 throw new SystemException ("No process to kill.");
801
802                         int exitcode = ExitCode_internal (process_handle);
803                         if (exitcode != 259)
804                                 throw new InvalidOperationException ("The process already finished.");
805
806                         return Kill_internal (process_handle, signo);
807                 }
808
809                 public bool CloseMainWindow ()
810                 {
811                         return Close (2);
812                 }
813
814                 [MonoTODO]
815                 public static void EnterDebugMode() {
816                 }
817
818                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
819                 private extern static IntPtr GetProcess_internal(int pid);
820                 
821                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
822                 private extern static int GetPid_internal();
823
824                 public static Process GetCurrentProcess()
825                 {
826                         int pid = GetPid_internal();
827                         IntPtr proc = GetProcess_internal(pid);
828                         
829                         if (proc == IntPtr.Zero)
830                                 throw new SystemException("Can't find current process");
831
832                         return (new Process (proc, pid));
833                 }
834
835                 public static Process GetProcessById(int processId)
836                 {
837                         IntPtr proc = GetProcess_internal(processId);
838                         
839                         if (proc == IntPtr.Zero)
840                                 throw new ArgumentException ("Can't find process with ID " + processId.ToString ());
841
842                         return (new Process (proc, processId));
843                 }
844
845                 [MonoTODO ("There is no support for retrieving process information from a remote machine")]
846                 public static Process GetProcessById(int processId, string machineName) {
847                         if (machineName == null)
848                                 throw new ArgumentNullException ("machineName");
849
850                         if (!IsLocalMachine (machineName))
851                                 throw new NotImplementedException ();
852
853                         return GetProcessById (processId);
854                 }
855
856                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
857                 private extern static int[] GetProcesses_internal();
858
859                 public static Process[] GetProcesses ()
860                 {
861                         int [] pids = GetProcesses_internal ();
862                         if (pids == null)
863                                 return new Process [0];
864
865                         var proclist = new List<Process> (pids.Length);
866                         for (int i = 0; i < pids.Length; i++) {
867                                 try {
868                                         proclist.Add (GetProcessById (pids [i]));
869                                 } catch (SystemException) {
870                                         /* The process might exit
871                                          * between
872                                          * GetProcesses_internal and
873                                          * GetProcessById
874                                          */
875                                 }
876                         }
877
878                         return proclist.ToArray ();
879                 }
880
881                 [MonoTODO ("There is no support for retrieving process information from a remote machine")]
882                 public static Process[] GetProcesses(string machineName) {
883                         if (machineName == null)
884                                 throw new ArgumentNullException ("machineName");
885
886                         if (!IsLocalMachine (machineName))
887                                 throw new NotImplementedException ();
888
889                         return GetProcesses ();
890                 }
891
892                 public static Process[] GetProcessesByName(string processName)
893                 {
894                         int [] pids = GetProcesses_internal ();
895                         if (pids == null)
896                                 return new Process [0];
897                         
898                         var proclist = new List<Process> (pids.Length);
899                         for (int i = 0; i < pids.Length; i++) {
900                                 try {
901                                         Process p = GetProcessById (pids [i]);
902                                         if (String.Compare (processName, p.ProcessName, true) == 0)
903                                                 proclist.Add (p);
904                                 } catch (SystemException) {
905                                         /* The process might exit
906                                          * between
907                                          * GetProcesses_internal and
908                                          * GetProcessById
909                                          */
910                                 }
911                         }
912
913                         return proclist.ToArray ();
914                 }
915
916                 [MonoTODO]
917                 public static Process[] GetProcessesByName(string processName, string machineName) {
918                         throw new NotImplementedException();
919                 }
920
921                 public void Kill ()
922                 {
923                         Close (1);
924                 }
925
926                 [MonoTODO]
927                 public static void LeaveDebugMode() {
928                 }
929
930                 public void Refresh ()
931                 {
932                         // FIXME: should refresh any cached data we might have about
933                         // the process (currently we have none).
934                 }
935
936                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
937                 private extern static bool ShellExecuteEx_internal(ProcessStartInfo startInfo,
938                                                                    ref ProcInfo proc_info);
939
940                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
941                 private extern static bool CreateProcess_internal(ProcessStartInfo startInfo,
942                                                                   IntPtr stdin,
943                                                                   IntPtr stdout,
944                                                                   IntPtr stderr,
945                                                                   ref ProcInfo proc_info);
946
947                 private static bool Start_shell (ProcessStartInfo startInfo, Process process)
948                 {
949                         ProcInfo proc_info=new ProcInfo();
950                         bool ret;
951
952                         if (startInfo.RedirectStandardInput ||
953                             startInfo.RedirectStandardOutput ||
954                             startInfo.RedirectStandardError) {
955                                 throw new InvalidOperationException ("UseShellExecute must be false when redirecting I/O.");
956                         }
957
958                         if (startInfo.HaveEnvVars)
959                                 throw new InvalidOperationException ("UseShellExecute must be false in order to use environment variables.");
960
961                         FillUserInfo (startInfo, ref proc_info);
962                         try {
963                                 ret = ShellExecuteEx_internal (startInfo,
964                                                                ref proc_info);
965                         } finally {
966                                 if (proc_info.Password != IntPtr.Zero)
967                                         Marshal.ZeroFreeBSTR (proc_info.Password);
968                                 proc_info.Password = IntPtr.Zero;
969                         }
970                         if (!ret) {
971                                 throw new Win32Exception (-proc_info.pid);
972                         }
973
974                         process.process_handle = proc_info.process_handle;
975                         process.pid = proc_info.pid;
976                         process.StartExitCallbackIfNeeded ();
977                         return(ret);
978                 }
979
980                 private static bool Start_noshell (ProcessStartInfo startInfo,
981                                                    Process process)
982                 {
983                         ProcInfo proc_info=new ProcInfo();
984                         IntPtr stdin_rd = IntPtr.Zero, stdin_wr = IntPtr.Zero;
985                         IntPtr stdout_wr;
986                         IntPtr stderr_wr;
987                         bool ret;
988                         MonoIOError error;
989
990                         if (startInfo.HaveEnvVars) {
991                                 string [] strs = new string [startInfo.EnvironmentVariables.Count];
992                                 startInfo.EnvironmentVariables.Keys.CopyTo (strs, 0);
993                                 proc_info.envKeys = strs;
994
995                                 strs = new string [startInfo.EnvironmentVariables.Count];
996                                 startInfo.EnvironmentVariables.Values.CopyTo (strs, 0);
997                                 proc_info.envValues = strs;
998                         }
999
1000                         if (startInfo.RedirectStandardInput == true) {
1001                                 if (IsWindows) {
1002                                         int DUPLICATE_SAME_ACCESS = 0x00000002;
1003                                         IntPtr stdin_wr_tmp;
1004
1005                                         ret = MonoIO.CreatePipe (out stdin_rd,
1006                                                                          out stdin_wr_tmp);
1007                                         if (ret) {
1008                                                 ret = MonoIO.DuplicateHandle (Process.GetCurrentProcess ().Handle, stdin_wr_tmp,
1009                                                 Process.GetCurrentProcess ().Handle, out stdin_wr, 0, 0, DUPLICATE_SAME_ACCESS);
1010                                                 MonoIO.Close (stdin_wr_tmp, out error);
1011                                         }
1012                                 }
1013                                 else
1014                                 {
1015                                         ret = MonoIO.CreatePipe (out stdin_rd,
1016                                                                          out stdin_wr);
1017                                 }
1018                                 if (ret == false) {
1019                                         throw new IOException ("Error creating standard input pipe");
1020                                 }
1021                         } else {
1022                                 stdin_rd = MonoIO.ConsoleInput;
1023                                 /* This is required to stop the
1024                                  * &$*£ing stupid compiler moaning
1025                                  * that stdin_wr is unassigned, below.
1026                                  */
1027                                 stdin_wr = (IntPtr)0;
1028                         }
1029
1030                         if (startInfo.RedirectStandardOutput == true) {
1031                                 IntPtr out_rd = IntPtr.Zero;
1032                                 if (IsWindows) {
1033                                         IntPtr out_rd_tmp;
1034                                         int DUPLICATE_SAME_ACCESS = 0x00000002;
1035
1036                                         ret = MonoIO.CreatePipe (out out_rd_tmp,
1037                                                                          out stdout_wr);
1038                                         if (ret) {
1039                                                 MonoIO.DuplicateHandle (Process.GetCurrentProcess ().Handle, out_rd_tmp,
1040                                                 Process.GetCurrentProcess ().Handle, out out_rd, 0, 0, DUPLICATE_SAME_ACCESS);
1041                                                 MonoIO.Close (out_rd_tmp, out error);
1042                                         }
1043                                 }
1044                                 else {
1045                                         ret = MonoIO.CreatePipe (out out_rd,
1046                                                                          out stdout_wr);
1047                                 }
1048
1049                                 process.stdout_rd = out_rd;
1050                                 if (ret == false) {
1051                                         if (startInfo.RedirectStandardInput == true) {
1052                                                 MonoIO.Close (stdin_rd, out error);
1053                                                 MonoIO.Close (stdin_wr, out error);
1054                                         }
1055
1056                                         throw new IOException ("Error creating standard output pipe");
1057                                 }
1058                         } else {
1059                                 process.stdout_rd = (IntPtr)0;
1060                                 stdout_wr = MonoIO.ConsoleOutput;
1061                         }
1062
1063                         if (startInfo.RedirectStandardError == true) {
1064                                 IntPtr err_rd = IntPtr.Zero;
1065                                 if (IsWindows) {
1066                                         IntPtr err_rd_tmp;
1067                                         int DUPLICATE_SAME_ACCESS = 0x00000002;
1068
1069                                         ret = MonoIO.CreatePipe (out err_rd_tmp,
1070                                                                          out stderr_wr);
1071                                         if (ret) {
1072                                                 MonoIO.DuplicateHandle (Process.GetCurrentProcess ().Handle, err_rd_tmp,
1073                                                 Process.GetCurrentProcess ().Handle, out err_rd, 0, 0, DUPLICATE_SAME_ACCESS);
1074                                                 MonoIO.Close (err_rd_tmp, out error);
1075                                         }
1076                                 }
1077                                 else {
1078                                         ret = MonoIO.CreatePipe (out err_rd,
1079                                                                          out stderr_wr);
1080                                 }
1081
1082                                 process.stderr_rd = err_rd;
1083                                 if (ret == false) {
1084                                         if (startInfo.RedirectStandardInput == true) {
1085                                                 MonoIO.Close (stdin_rd, out error);
1086                                                 MonoIO.Close (stdin_wr, out error);
1087                                         }
1088                                         if (startInfo.RedirectStandardOutput == true) {
1089                                                 MonoIO.Close (process.stdout_rd, out error);
1090                                                 MonoIO.Close (stdout_wr, out error);
1091                                         }
1092                                         
1093                                         throw new IOException ("Error creating standard error pipe");
1094                                 }
1095                         } else {
1096                                 process.stderr_rd = (IntPtr)0;
1097                                 stderr_wr = MonoIO.ConsoleError;
1098                         }
1099
1100                         FillUserInfo (startInfo, ref proc_info);
1101                         try {
1102                                 ret = CreateProcess_internal (startInfo,
1103                                                               stdin_rd, stdout_wr, stderr_wr,
1104                                                               ref proc_info);
1105                         } finally {
1106                                 if (proc_info.Password != IntPtr.Zero)
1107                                         Marshal.ZeroFreeBSTR (proc_info.Password);
1108                                 proc_info.Password = IntPtr.Zero;
1109                         }
1110                         if (!ret) {
1111                                 if (startInfo.RedirectStandardInput == true) {
1112                                         MonoIO.Close (stdin_rd, out error);
1113                                         MonoIO.Close (stdin_wr, out error);
1114                                 }
1115
1116                                 if (startInfo.RedirectStandardOutput == true) {
1117                                         MonoIO.Close (process.stdout_rd, out error);
1118                                         MonoIO.Close (stdout_wr, out error);
1119                                 }
1120
1121                                 if (startInfo.RedirectStandardError == true) {
1122                                         MonoIO.Close (process.stderr_rd, out error);
1123                                         MonoIO.Close (stderr_wr, out error);
1124                                 }
1125
1126                                 throw new Win32Exception (-proc_info.pid,
1127                                         "ApplicationName='" + startInfo.FileName +
1128                                         "', CommandLine='" + startInfo.Arguments +
1129                                         "', CurrentDirectory='" + startInfo.WorkingDirectory +
1130                                         "', Native error= " + Win32Exception.W32ErrorMessage (-proc_info.pid));
1131                         }
1132
1133                         process.process_handle = proc_info.process_handle;
1134                         process.pid = proc_info.pid;
1135                         
1136                         if (startInfo.RedirectStandardInput == true) {
1137                                 MonoIO.Close (stdin_rd, out error);
1138                                 process.input_stream = new StreamWriter (new MonoSyncFileStream (stdin_wr, FileAccess.Write, true, 8192), Console.Out.Encoding);
1139                                 process.input_stream.AutoFlush = true;
1140                         }
1141
1142                         Encoding stdoutEncoding = startInfo.StandardOutputEncoding ?? Console.Out.Encoding;
1143                         Encoding stderrEncoding = startInfo.StandardErrorEncoding ?? Console.Out.Encoding;
1144
1145                         if (startInfo.RedirectStandardOutput == true) {
1146                                 MonoIO.Close (stdout_wr, out error);
1147                                 process.output_stream = new StreamReader (new MonoSyncFileStream (process.stdout_rd, FileAccess.Read, true, 8192), stdoutEncoding, true, 8192);
1148                         }
1149
1150                         if (startInfo.RedirectStandardError == true) {
1151                                 MonoIO.Close (stderr_wr, out error);
1152                                 process.error_stream = new StreamReader (new MonoSyncFileStream (process.stderr_rd, FileAccess.Read, true, 8192), stderrEncoding, true, 8192);
1153                         }
1154
1155                         process.StartExitCallbackIfNeeded ();
1156
1157                         return(ret);
1158                 }
1159
1160                 // Note that ProcInfo.Password must be freed.
1161                 private static void FillUserInfo (ProcessStartInfo startInfo, ref ProcInfo proc_info)
1162                 {
1163                         if (startInfo.UserName.Length != 0) {
1164                                 proc_info.UserName = startInfo.UserName;
1165                                 proc_info.Domain = startInfo.Domain;
1166                                 if (startInfo.Password != null)
1167                                         proc_info.Password = Marshal.SecureStringToBSTR (startInfo.Password);
1168                                 else
1169                                         proc_info.Password = IntPtr.Zero;
1170                                 proc_info.LoadUserProfile = startInfo.LoadUserProfile;
1171                         }
1172                 }
1173
1174                 private static bool Start_common (ProcessStartInfo startInfo,
1175                                                   Process process)
1176                 {
1177                         if (startInfo.FileName.Length == 0)
1178                                 throw new InvalidOperationException("File name has not been set");
1179                         
1180                         if (startInfo.StandardErrorEncoding != null && !startInfo.RedirectStandardError)
1181                                 throw new InvalidOperationException ("StandardErrorEncoding is only supported when standard error is redirected");
1182                         if (startInfo.StandardOutputEncoding != null && !startInfo.RedirectStandardOutput)
1183                                 throw new InvalidOperationException ("StandardOutputEncoding is only supported when standard output is redirected");
1184                         
1185                         if (startInfo.UseShellExecute) {
1186                                 if (startInfo.UserName.Length != 0)
1187                                         throw new InvalidOperationException ("UseShellExecute must be false if an explicit UserName is specified when starting a process");
1188                                 return (Start_shell (startInfo, process));
1189                         } else {
1190                                 return (Start_noshell (startInfo, process));
1191                         }
1192                 }
1193                 
1194                 public bool Start ()
1195                 {
1196                         if (process_handle != IntPtr.Zero) {
1197                                 Process_free_internal (process_handle);
1198                                 process_handle = IntPtr.Zero;
1199                         }
1200                         return Start_common(start_info, this);
1201                 }
1202
1203                 public static Process Start (ProcessStartInfo startInfo)
1204                 {
1205                         if (startInfo == null)
1206                                 throw new ArgumentNullException ("startInfo");
1207
1208                         Process process = new Process();
1209                         process.StartInfo = startInfo;
1210                         if (Start_common(startInfo, process) && process.process_handle != IntPtr.Zero)
1211                                 return process;
1212                         return null;
1213                 }
1214
1215                 public static Process Start (string fileName)
1216                 {
1217                         return Start (new ProcessStartInfo (fileName));
1218                 }
1219
1220                 public static Process Start(string fileName, string arguments)
1221                 {
1222                         return Start (new ProcessStartInfo (fileName, arguments));
1223                 }
1224
1225                 public static Process Start(string fileName, string username, SecureString password, string domain) {
1226                         return Start(fileName, null, username, password, domain);
1227                 }
1228
1229                 public static Process Start(string fileName, string arguments, string username, SecureString password, string domain) {
1230                         ProcessStartInfo psi = new ProcessStartInfo(fileName, arguments);
1231                         psi.UserName = username;
1232                         psi.Password = password;
1233                         psi.Domain = domain;
1234                         psi.UseShellExecute = false;
1235                         return Start(psi);
1236                 }
1237
1238                 public override string ToString()
1239                 {
1240                         return(base.ToString() + " (" + this.ProcessName + ")");
1241                 }
1242
1243                 /* Waits up to ms milliseconds for process 'handle' to
1244                  * exit.  ms can be <0 to mean wait forever.
1245                  */
1246                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1247                 private extern bool WaitForExit_internal(IntPtr handle, int ms);
1248
1249                 public void WaitForExit ()
1250                 {
1251                         WaitForExit (-1);
1252                 }
1253
1254                 public bool WaitForExit(int milliseconds) {
1255                         int ms = milliseconds;
1256                         if (ms == int.MaxValue)
1257                                 ms = -1;
1258
1259                         if (process_handle == IntPtr.Zero)
1260                                 throw new InvalidOperationException ("No process is associated with this object.");
1261
1262
1263                         DateTime start = DateTime.UtcNow;
1264                         if (async_output != null && !async_output.IsCompleted) {
1265                                 if (false == async_output.WaitHandle.WaitOne (ms, false))
1266                                         return false; // Timed out
1267
1268                                 if (ms >= 0) {
1269                                         DateTime now = DateTime.UtcNow;
1270                                         ms -= (int) (now - start).TotalMilliseconds;
1271                                         if (ms <= 0)
1272                                                 return false;
1273                                         start = now;
1274                                 }
1275                         }
1276
1277                         if (async_error != null && !async_error.IsCompleted) {
1278                                 if (false == async_error.WaitHandle.WaitOne (ms, false))
1279                                         return false; // Timed out
1280
1281                                 if (ms >= 0) {
1282                                         ms -= (int) (DateTime.UtcNow - start).TotalMilliseconds;
1283                                         if (ms <= 0)
1284                                                 return false;
1285                                 }
1286                         }
1287
1288                         bool exited = WaitForExit_internal (process_handle, ms);
1289
1290                         if (exited)
1291                                 OnExited ();
1292
1293                         return exited;
1294                 }
1295
1296                 /* Waits up to ms milliseconds for process 'handle' to 
1297                  * wait for input.  ms can be <0 to mean wait forever.
1298                  */
1299                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1300                 private extern bool WaitForInputIdle_internal(IntPtr handle, int ms);
1301
1302                 // The internal call is only implemented properly on Windows.
1303                 [MonoTODO]
1304                 public bool WaitForInputIdle() {
1305                         return WaitForInputIdle (-1);
1306                 }
1307
1308                 // The internal call is only implemented properly on Windows.
1309                 [MonoTODO]
1310                 public bool WaitForInputIdle(int milliseconds) {
1311                         return WaitForInputIdle_internal (process_handle, milliseconds);
1312                 }
1313
1314                 private static bool IsLocalMachine (string machineName)
1315                 {
1316                         if (machineName == "." || machineName.Length == 0)
1317                                 return true;
1318
1319                         return (string.Compare (machineName, Environment.MachineName, true) == 0);
1320                 }
1321
1322                 [Browsable (true)]
1323                 [MonitoringDescription ("Raised when it receives output data")]
1324                 public event DataReceivedEventHandler OutputDataReceived;
1325                 [Browsable (true)]
1326                 [MonitoringDescription ("Raised when it receives error data")]
1327                 public event DataReceivedEventHandler ErrorDataReceived;
1328
1329                 void OnOutputDataReceived (string str)
1330                 {
1331                         if (OutputDataReceived != null)
1332                                 OutputDataReceived (this, new DataReceivedEventArgs (str));
1333                 }
1334
1335                 void OnErrorDataReceived (string str)
1336                 {
1337                         if (ErrorDataReceived != null)
1338                                 ErrorDataReceived (this, new DataReceivedEventArgs (str));
1339                 }
1340
1341                 [Flags]
1342                 enum AsyncModes {
1343                         NoneYet = 0,
1344                         SyncOutput = 1,
1345                         SyncError = 1 << 1,
1346                         AsyncOutput = 1 << 2,
1347                         AsyncError = 1 << 3
1348                 }
1349
1350                 [StructLayout (LayoutKind.Sequential)]
1351                 sealed class ProcessAsyncReader : IThreadPoolWorkItem
1352                 {
1353                         /*
1354                            The following fields match those of SocketAsyncResult.
1355                            This is so that changes needed in the runtime to handle
1356                            asynchronous reads are trivial
1357                            Keep this in sync with SocketAsyncResult in 
1358                            ./System.Net.Sockets/Socket.cs and MonoSocketAsyncResult
1359                            in metadata/socket-io.h.
1360                         */
1361                         /* DON'T shuffle fields around. DON'T remove fields */
1362                         public object Sock;
1363                         public IntPtr handle;
1364                         public object state;
1365                         public AsyncCallback callback;
1366                         public ManualResetEvent wait_handle;
1367
1368                         public Exception delayedException;
1369
1370                         public object EndPoint;
1371                         byte [] buffer = new byte [4196];
1372                         public int Offset;
1373                         public int Size;
1374                         public int SockFlags;
1375
1376                         public object AcceptSocket;
1377                         public object[] Addresses;
1378                         public int port;
1379                         public object Buffers;          // Reserve this slot in older profiles
1380                         public bool ReuseSocket;        // Disconnect
1381                         public object acc_socket;
1382                         public int total;
1383                         public bool completed_sync;
1384                         bool completed;
1385                         bool err_out; // true -> stdout, false -> stderr
1386                         internal int error;
1387                         public int operation = 8; // MAGIC NUMBER: see Socket.cs:AsyncOperation
1388                         public AsyncResult async_result;
1389                         public int EndCalled;
1390
1391                         // These fields are not in SocketAsyncResult
1392                         Process process;
1393                         Stream stream;
1394                         StringBuilder sb = new StringBuilder ();
1395                         public AsyncReadHandler ReadHandler;
1396
1397                         public ProcessAsyncReader (Process process, IntPtr handle, bool err_out)
1398                         {
1399                                 this.process = process;
1400                                 this.handle = handle;
1401                                 stream = new FileStream (handle, FileAccess.Read, false);
1402                                 this.ReadHandler = new AsyncReadHandler (AddInput);
1403                                 this.err_out = err_out;
1404                         }
1405
1406                         public void AddInput ()
1407                         {
1408                                 lock (this) {
1409                                         int nread = stream.Read (buffer, 0, buffer.Length);
1410                                         if (nread == 0) {
1411                                                 completed = true;
1412                                                 if (wait_handle != null)
1413                                                         wait_handle.Set ();
1414                                                 FlushLast ();
1415                                                 return;
1416                                         }
1417
1418                                         try {
1419                                                 sb.Append (Encoding.Default.GetString (buffer, 0, nread));
1420                                         } catch {
1421                                                 // Just in case the encoding fails...
1422                                                 for (int i = 0; i < nread; i++) {
1423                                                         sb.Append ((char) buffer [i]);
1424                                                 }
1425                                         }
1426
1427                                         Flush (false);
1428                                         ReadHandler.BeginInvoke (null, this);
1429                                 }
1430                         }
1431
1432                         void FlushLast ()
1433                         {
1434                                 Flush (true);
1435                                 if (err_out) {
1436                                         process.OnOutputDataReceived (null);
1437                                 } else {
1438                                         process.OnErrorDataReceived (null);
1439                                 }
1440                         }
1441                         
1442                         void Flush (bool last)
1443                         {
1444                                 if (sb.Length == 0 ||
1445                                     (err_out && process.output_canceled) ||
1446                                     (!err_out && process.error_canceled))
1447                                         return;
1448
1449                                 string total = sb.ToString ();
1450                                 sb.Length = 0;
1451                                 string [] strs = total.Split ('\n');
1452                                 int len = strs.Length;
1453                                 if (len == 0)
1454                                         return;
1455
1456                                 for (int i = 0; i < len - 1; i++) {
1457                                         if (err_out)
1458                                                 process.OnOutputDataReceived (strs [i]);
1459                                         else
1460                                                 process.OnErrorDataReceived (strs [i]);
1461                                 }
1462
1463                                 string end = strs [len - 1];
1464                                 if (last || (len == 1 && end == "")) {
1465                                         if (err_out) {
1466                                                 process.OnOutputDataReceived (end);
1467                                         } else {
1468                                                 process.OnErrorDataReceived (end);
1469                                         }
1470                                 } else {
1471                                         sb.Append (end);
1472                                 }
1473                         }
1474
1475                         public bool IsCompleted {
1476                                 get { return completed; }
1477                         }
1478
1479                         public WaitHandle WaitHandle {
1480                                 get {
1481                                         lock (this) {
1482                                                 if (wait_handle == null)
1483                                                         wait_handle = new ManualResetEvent (completed);
1484                                                 return wait_handle;
1485                                         }
1486                                 }
1487                         }
1488
1489                         public void Close () {
1490                                 stream.Close ();
1491                         }
1492
1493                         void IThreadPoolWorkItem.ExecuteWorkItem()
1494                         {
1495                                 async_result.Invoke ();
1496                         }
1497
1498                         void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
1499                         {
1500                         }
1501                 }
1502
1503                 AsyncModes async_mode;
1504                 bool output_canceled;
1505                 bool error_canceled;
1506                 ProcessAsyncReader async_output;
1507                 ProcessAsyncReader async_error;
1508                 delegate void AsyncReadHandler ();
1509
1510                 [ComVisibleAttribute(false)] 
1511                 public void BeginOutputReadLine ()
1512                 {
1513                         if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1514                                 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1515
1516                         if ((async_mode & AsyncModes.SyncOutput) != 0)
1517                                 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
1518
1519                         async_mode |= AsyncModes.AsyncOutput;
1520                         output_canceled = false;
1521                         if (async_output == null) {
1522                                 async_output = new ProcessAsyncReader (this, stdout_rd, true);
1523                                 async_output.ReadHandler.BeginInvoke (null, async_output);
1524                         }
1525                 }
1526
1527                 [ComVisibleAttribute(false)] 
1528                 public void CancelOutputRead ()
1529                 {
1530                         if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
1531                                 throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
1532
1533                         if ((async_mode & AsyncModes.SyncOutput) != 0)
1534                                 throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
1535
1536                         if (async_output == null)
1537                                 throw new InvalidOperationException ("No async operation in progress.");
1538
1539                         output_canceled = true;
1540                 }
1541
1542                 [ComVisibleAttribute(false)] 
1543                 public void BeginErrorReadLine ()
1544                 {
1545                         if (process_handle == IntPtr.Zero || error_stream == null || StartInfo.RedirectStandardError == false)
1546                                 throw new InvalidOperationException ("Standard error has not been redirected or process has not been started.");
1547
1548                         if ((async_mode & AsyncModes.SyncError) != 0)
1549                                 throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
1550
1551                         async_mode |= AsyncModes.AsyncError;
1552                         error_canceled = false;
1553                         if (async_error == null) {
1554                                 async_error = new ProcessAsyncReader (this, stderr_rd, false);
1555                                 async_error.ReadHandler.BeginInvoke (null, async_error);
1556                         }
1557                 }
1558
1559                 [ComVisibleAttribute(false)] 
1560                 public void CancelErrorRead ()
1561                 {
1562                         if (process_handle == IntPtr.Zero || error_stream == null || StartInfo.RedirectStandardError == false)
1563                                 throw new InvalidOperationException ("Standard error has not been redirected or process has not been started.");
1564
1565                         if ((async_mode & AsyncModes.SyncOutput) != 0)
1566                                 throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
1567
1568                         if (async_error == null)
1569                                 throw new InvalidOperationException ("No async operation in progress.");
1570
1571                         error_canceled = true;
1572                 }
1573
1574                 [Category ("Behavior")]
1575                 [MonitoringDescription ("Raised when this process exits.")]
1576                 public event EventHandler Exited {
1577                         add {
1578                                 if (process_handle != IntPtr.Zero && HasExited) {
1579                                         value.BeginInvoke (null, null, null, null);
1580                                 } else {
1581                                         exited_event = (EventHandler) Delegate.Combine (exited_event, value);
1582                                         if (exited_event != null)
1583                                                 StartExitCallbackIfNeeded ();
1584                                 }
1585                         }
1586                         remove {
1587                                 exited_event = (EventHandler) Delegate.Remove (exited_event, value);
1588                         }
1589                 }
1590
1591                 // Closes the system process handle
1592                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1593                 private extern void Process_free_internal(IntPtr handle);
1594                 
1595                 private bool disposed = false;
1596                 
1597                 protected override void Dispose(bool disposing) {
1598                         // Check to see if Dispose has already been called.
1599                         if(this.disposed == false) {
1600                                 this.disposed=true;
1601                                 // If this is a call to Dispose,
1602                                 // dispose all managed resources.
1603                                 if(disposing) {
1604                                         // Do stuff here
1605                                         lock (thisLock) {
1606                                                 /* These have open FileStreams on the pipes we are about to close */
1607                                                 if (async_output != null)
1608                                                         async_output.Close ();
1609                                                 if (async_error != null)
1610                                                         async_error.Close ();
1611
1612                                                 if (input_stream != null) {
1613                                                         if (!input_stream_exposed)
1614                                                                 input_stream.Close ();
1615                                                         input_stream = null;
1616                                                 }
1617                                                 if (output_stream != null) {
1618                                                         if (!output_stream_exposed)
1619                                                                 output_stream.Close ();
1620                                                         output_stream = null;
1621                                                 }
1622                                                 if (error_stream != null) {
1623                                                         if (!error_stream_exposed)
1624                                                                 error_stream.Close ();
1625                                                         error_stream = null;
1626                                                 }
1627                                         }
1628                                 }
1629                                 
1630                                 // Release unmanaged resources
1631
1632                                 lock (thisLock) {
1633                                         if(process_handle!=IntPtr.Zero) {
1634                                                 Process_free_internal(process_handle);
1635                                                 process_handle=IntPtr.Zero;
1636                                         }
1637                                 }
1638                         }
1639                         base.Dispose (disposing);
1640                 }
1641
1642                 ~Process ()
1643                 {
1644                         Dispose (false);
1645                 }
1646
1647                 static void CBOnExit (object state, bool unused)
1648                 {
1649                         Process p = (Process) state;
1650
1651                         if (!p.IsExitCallbackPending ())
1652                                 return;
1653
1654                         if (!p.HasExited) {
1655                                 p.UnregisterExitCallback ();
1656                                 p.StartExitCallbackIfNeeded ();
1657                                 return;
1658                         }
1659
1660                         p.OnExited ();
1661                 }
1662
1663                 int on_exited_called = 0;
1664
1665                 protected void OnExited() 
1666                 {
1667                         if (exited_event == null)
1668                                 return;
1669
1670                         if (on_exited_called != 0 || Interlocked.CompareExchange (ref on_exited_called, 1, 0) != 0)
1671                                 return;
1672
1673                         UnregisterExitCallback ();
1674
1675                         if (synchronizingObject == null) {
1676                                 foreach (EventHandler d in exited_event.GetInvocationList ()) {
1677                                         try {
1678                                                 d (this, EventArgs.Empty);
1679                                         } catch {}
1680                                 }
1681                                 return;
1682                         }
1683                         
1684                         object [] args = new object [] {this, EventArgs.Empty};
1685                         synchronizingObject.BeginInvoke (exited_event, args);
1686                 }
1687
1688                 static bool IsWindows
1689                 {
1690                         get
1691                         {
1692                                 PlatformID platform = Environment.OSVersion.Platform;
1693                                 if (platform == PlatformID.Win32S ||
1694                                         platform == PlatformID.Win32Windows ||
1695                                         platform == PlatformID.Win32NT ||
1696                                         platform == PlatformID.WinCE) {
1697                                         return true;
1698                                 }
1699                                 return false;
1700                         }
1701                 }
1702
1703                 class ProcessWaitHandle : WaitHandle
1704                 {
1705                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
1706                         private extern static IntPtr ProcessHandle_duplicate (IntPtr handle);
1707                         
1708                         public ProcessWaitHandle (IntPtr handle)
1709                         {
1710                                 // Need to keep a reference to this handle,
1711                                 // in case the Process object is collected
1712                                 Handle = ProcessHandle_duplicate (handle);
1713
1714                                 // When the wait handle is disposed, the duplicated handle will be
1715                                 // closed, so no need to override dispose (bug #464628).
1716                         }
1717                 }
1718         }
1719 }
1720