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