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