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