[System] Fix handle leak in Process::GetProcess.
[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.Collections;
38 using System.ComponentModel;
39 using System.ComponentModel.Design;
40 using System.Runtime.CompilerServices;
41 using System.Runtime.InteropServices;
42 using System.Runtime.Remoting.Messaging;
43 using System.Security.Permissions;
44 using System.Collections.Generic;
45 using System.Security;
46 using System.Threading;
47 using Microsoft.Win32;
48 using Microsoft.Win32.SafeHandles;
49
50 namespace System.Diagnostics
51 {
52         public partial class Process : Component 
53         {
54                 [StructLayout(LayoutKind.Sequential)]
55                 private struct ProcInfo 
56                 {
57                         public IntPtr process_handle;
58                         /* If thread_handle is ever needed for
59                          * something, take out the CloseHandle() in
60                          * the Start_internal icall in
61                          * mono/metadata/process.c
62                          */
63                         public IntPtr thread_handle;
64                         public int pid; // Contains -GetLastError () on failure.
65                         public int tid;
66                         public string[] envVariables;
67                         public string UserName;
68                         public string Domain;
69                         public IntPtr Password;
70                         public bool LoadUserProfile;
71                 };
72
73                 string process_name;
74
75                 /* Private constructor called from other methods */
76                 private Process (SafeProcessHandle handle, int id) {
77                         SetProcessHandle (handle);
78                         SetProcessId (id);
79                 }
80
81                 [MonoTODO]
82                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
83                 [MonitoringDescription ("Base process priority.")]
84                 public int BasePriority {
85                         get { return 0; }
86                 }
87
88                 [MonoTODO]
89                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
90                 [MonitoringDescription ("Handles for this process.")]
91                 public int HandleCount {
92                         get {
93                                 return(0);
94                         }
95                 }
96
97                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
98                 [MonitoringDescription ("The main module of the process.")]
99                 public ProcessModule MainModule {
100                         get {
101                                 return(this.Modules[0]);
102                         }
103                 }
104
105                 [MonoTODO]
106                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
107                 [MonitoringDescription ("The handle of the main window of the process.")]
108                 public IntPtr MainWindowHandle {
109                         get {
110                                 return((IntPtr)0);
111                         }
112                 }
113
114                 [MonoTODO]
115                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
116                 [MonitoringDescription ("The title of the main window of the process.")]
117                 public string MainWindowTitle {
118                         get {
119                                 return("null");
120                         }
121                 }
122
123                 /* Returns the list of process modules.  The main module is
124                  * element 0.
125                  */
126                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
127                 private extern ProcessModule[] GetModules_internal(IntPtr handle);
128
129                 ProcessModule[] GetModules_internal (SafeProcessHandle handle)
130                 {
131                         bool release = false;
132                         try {
133                                 handle.DangerousAddRef (ref release);
134                                 return GetModules_internal (handle.DangerousGetHandle ());
135                         } finally {
136                                 if (release)
137                                         handle.DangerousRelease ();
138                         }
139                 }
140
141                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
142                 [MonitoringDescription ("The modules that are loaded as part of this process.")]
143                 public ProcessModuleCollection Modules {
144                         get {
145                                 if (modules == null) {
146                                         SafeProcessHandle handle = null;
147                                         try {
148                                                 handle = GetProcessHandle (NativeMethods.PROCESS_QUERY_INFORMATION);
149                                                 modules = new ProcessModuleCollection (GetModules_internal (handle));
150                                         } finally {
151                                                 ReleaseProcessHandle (handle);
152                                         }
153                                 }
154
155                                 return modules;
156                         }
157                 }
158
159                 /* data type is from the MonoProcessData enum in mono-proclib.h in the runtime */
160                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
161                 private extern static long GetProcessData (int pid, int data_type, out int error);
162
163                 [MonoTODO]
164                 [Obsolete ("Use NonpagedSystemMemorySize64")]
165                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
166                 [MonitoringDescription ("The number of bytes that are not pageable.")]
167                 public int NonpagedSystemMemorySize {
168                         get {
169                                 return(0);
170                         }
171                 }
172
173                 [Obsolete ("Use PagedMemorySize64")]
174                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
175                 [MonitoringDescription ("The number of bytes that are paged.")]
176                 public int PagedMemorySize {
177                         get {
178                                 return(int)PagedMemorySize64;
179                         }
180                 }
181
182                 [Obsolete ("Use PagedSystemMemorySize64")]
183                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
184                 [MonitoringDescription ("The amount of paged system memory in bytes.")]
185                 public int PagedSystemMemorySize {
186                         get {
187                                 return(int)PagedMemorySize64;
188                         }
189                 }
190
191                 [MonoTODO]
192                 [Obsolete ("Use PeakPagedMemorySize64")]
193                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
194                 [MonitoringDescription ("The maximum amount of paged memory used by this process.")]
195                 public int PeakPagedMemorySize {
196                         get {
197                                 return(0);
198                         }
199                 }
200
201                 [Obsolete ("Use PeakVirtualMemorySize64")]
202                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
203                 [MonitoringDescription ("The maximum amount of virtual memory used by this process.")]
204                 public int PeakVirtualMemorySize {
205                         get {
206                                 int error;
207                                 return (int)GetProcessData (processId, 8, out error);
208                         }
209                 }
210
211                 [Obsolete ("Use PeakWorkingSet64")]
212                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
213                 [MonitoringDescription ("The maximum amount of system memory used by this process.")]
214                 public int PeakWorkingSet {
215                         get {
216                                 int error;
217                                 return (int)GetProcessData (processId, 5, out error);
218                         }
219                 }
220
221                 [MonoTODO]
222                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
223                 [MonitoringDescription ("The number of bytes that are not pageable.")]
224                 [ComVisible (false)]
225                 public long NonpagedSystemMemorySize64 {
226                         get {
227                                 return(0);
228                         }
229                 }
230
231                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
232                 [MonitoringDescription ("The number of bytes that are paged.")]
233                 [ComVisible (false)]
234                 public long PagedMemorySize64 {
235                         get {
236                                 int error;
237                                 return GetProcessData (processId, 12, out error);
238                         }
239                 }
240
241                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
242                 [MonitoringDescription ("The amount of paged system memory in bytes.")]
243                 [ComVisible (false)]
244                 public long PagedSystemMemorySize64 {
245                         get {
246                                 return PagedMemorySize64;
247                         }
248                 }
249
250                 [MonoTODO]
251                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
252                 [MonitoringDescription ("The maximum amount of paged memory used by this process.")]
253                 [ComVisible (false)]
254                 public long PeakPagedMemorySize64 {
255                         get {
256                                 return(0);
257                         }
258                 }
259
260                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
261                 [MonitoringDescription ("The maximum amount of virtual memory used by this process.")]
262                 [ComVisible (false)]
263                 public long PeakVirtualMemorySize64 {
264                         get {
265                                 int error;
266                                 return GetProcessData (processId, 8, out error);
267                         }
268                 }
269
270                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
271                 [MonitoringDescription ("The maximum amount of system memory used by this process.")]
272                 [ComVisible (false)]
273                 public long PeakWorkingSet64 {
274                         get {
275                                 int error;
276                                 return GetProcessData (processId, 5, out error);
277                         }
278                 }
279
280                 [MonoTODO]
281                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
282                 [MonitoringDescription ("Process will be of higher priority while it is actively used.")]
283                 public bool PriorityBoostEnabled {
284                         get {
285                                 return(false);
286                         }
287                         set {
288                         }
289                 }
290
291                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
292                 [MonitoringDescription ("The amount of memory exclusively used by this process.")]
293                 [Obsolete ("Use PrivateMemorySize64")]
294                 public int PrivateMemorySize {
295                         get {
296                                 int error;
297                                 return (int)GetProcessData (processId, 6, out error);
298                         }
299                 }
300
301                 [MonoNotSupported ("")]
302                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
303                 [MonitoringDescription ("The session ID for this process.")]
304                 public int SessionId {
305                         get { return 0; }
306                 }
307
308                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
309                 private extern static string ProcessName_internal(IntPtr handle);
310
311                 static string ProcessName_internal(SafeProcessHandle handle)
312                 {
313                         bool release = false;
314                         try {
315                                 handle.DangerousAddRef (ref release);
316                                 return ProcessName_internal (handle.DangerousGetHandle ());
317                         } finally {
318                                 if (release)
319                                         handle.DangerousRelease ();
320                         }
321                 }
322
323                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
324                 [MonitoringDescription ("The name of this process.")]
325                 public string ProcessName {
326                         get {
327                                 if (process_name == null) {
328                                         SafeProcessHandle handle = null;
329                                         try {
330                                                 handle = GetProcessHandle (NativeMethods.PROCESS_QUERY_INFORMATION);
331
332                                                 process_name = ProcessName_internal (handle);
333
334                                                 /* If process_name is _still_ null, assume the process has exited or is inaccessible */
335                                                 if (process_name == null)
336                                                         throw new InvalidOperationException ("Process has exited or is inaccessible, so the requested information is not available.");
337
338                                                 /* Strip the suffix (if it exists) simplistically instead of removing
339                                                  * any trailing \.???, so we dont get stupid results on sane systems */
340                                                 if(process_name.EndsWith(".exe") || process_name.EndsWith(".bat") || process_name.EndsWith(".com"))
341                                                         process_name = process_name.Substring (0, process_name.Length - 4);
342                                         } finally {
343                                                 ReleaseProcessHandle (handle);
344                                         }
345                                 }
346                                 return process_name;
347                         }
348                 }
349
350                 [MonoTODO]
351                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
352                 [MonitoringDescription ("Allowed processor that can be used by this process.")]
353                 public IntPtr ProcessorAffinity {
354                         get {
355                                 return((IntPtr)0);
356                         }
357                         set {
358                         }
359                 }
360
361                 [MonoTODO]
362                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
363                 [MonitoringDescription ("Is this process responsive.")]
364                 public bool Responding {
365                         get {
366                                 return(false);
367                         }
368                 }
369
370 #if !MONO_FEATURE_PROCESS_START
371                 [Obsolete ("Process.StartInfo is not supported on the current platform.", true)]
372                 public ProcessStartInfo StartInfo {
373                         get { throw new PlatformNotSupportedException ("Process.StartInfo is not supported on the current platform."); }
374                         set { throw new PlatformNotSupportedException ("Process.StartInfo is not supported on the current platform."); }
375                 }
376
377                 [Obsolete ("Process.StandardError is not supported on the current platform.", true)]
378                 public StreamReader StandardError {
379                         get { throw new PlatformNotSupportedException ("Process.StandardError is not supported on the current platform."); }
380                 }
381
382                 [Obsolete ("Process.StandardInput is not supported on the current platform.", true)]
383                 public StreamWriter StandardInput {
384                         get { throw new PlatformNotSupportedException ("Process.StandardInput is not supported on the current platform."); }
385                 }
386
387                 [Obsolete ("Process.StandardOutput is not supported on the current platform.", true)]
388                 public StreamReader StandardOutput {
389                         get { throw new PlatformNotSupportedException ("Process.StandardOutput is not supported on the current platform."); }
390                 }
391 #endif // !MONO_FEATURE_PROCESS_START
392
393                 [MonoTODO]
394                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
395                 [MonitoringDescription ("The number of threads of this process.")]
396                 public ProcessThreadCollection Threads {
397                         get {
398                                 if (threads == null) {
399                                         int error;
400                                         // This'll return a correctly-sized array of empty ProcessThreads for now.
401                                         threads = new ProcessThreadCollection(new ProcessThread [GetProcessData (processId, 0, out error)]);
402                                 }
403
404                                 return threads;
405                         }
406                 }
407
408                 [Obsolete ("Use VirtualMemorySize64")]
409                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
410                 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
411                 public int VirtualMemorySize {
412                         get {
413                                 int error;
414                                 return (int)GetProcessData (processId, 7, out error);
415                         }
416                 }
417
418                 [Obsolete ("Use WorkingSet64")]
419                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
420                 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
421                 public int WorkingSet {
422                         get {
423                                 int error;
424                                 return (int)GetProcessData (processId, 4, out error);
425                         }
426                 }
427
428                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
429                 [MonitoringDescription ("The amount of memory exclusively used by this process.")]
430                 [ComVisible (false)]
431                 public long PrivateMemorySize64 {
432                         get {
433                                 int error;
434                                 return GetProcessData (processId, 6, out error);
435                         }
436                 }
437
438                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
439                 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
440                 [ComVisible (false)]
441                 public long VirtualMemorySize64 {
442                         get {
443                                 int error;
444                                 return GetProcessData (processId, 7, out error);
445                         }
446                 }
447
448                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
449                 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
450                 [ComVisible (false)]
451                 public long WorkingSet64 {
452                         get {
453                                 int error;
454                                 return GetProcessData (processId, 4, out error);
455                         }
456                 }
457
458                 public bool CloseMainWindow ()
459                 {
460                         SafeProcessHandle handle = null;
461                         try {
462                                 handle = GetProcessHandle (NativeMethods.PROCESS_TERMINATE);
463                                 return NativeMethods.TerminateProcess(handle, -2);
464                         } finally {
465                                 ReleaseProcessHandle(handle);
466                         }
467                 }
468
469                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
470                 private extern static IntPtr GetProcess_internal(int pid);
471
472                 [MonoTODO ("There is no support for retrieving process information from a remote machine")]
473                 public static Process GetProcessById(int processId, string machineName) {
474                         if (machineName == null)
475                                 throw new ArgumentNullException ("machineName");
476
477                         if (!IsLocalMachine (machineName))
478                                 throw new NotImplementedException ();
479
480                         IntPtr proc = GetProcess_internal(processId);
481
482                         if (proc == IntPtr.Zero)
483                                 throw new ArgumentException ("Can't find process with ID " + processId.ToString ());
484
485                         /* The handle returned by GetProcess_internal is owned by its caller, so we must pass true to SafeProcessHandle */
486                         return (new Process (new SafeProcessHandle (proc, true), processId));
487                 }
488
489                 public static Process[] GetProcessesByName(string processName, string machineName)
490                 {
491                         if (machineName == null)
492                                 throw new ArgumentNullException ("machineName");
493
494                         if (!IsLocalMachine (machineName))
495                                 throw new NotImplementedException ();
496
497                         Process[] processes = GetProcesses ();
498                         if (processes.Length == 0)
499                                 return processes;
500
501                         int size = 0;
502
503                         for (int i = 0; i < processes.Length; i++) {
504                                 try {
505                                         if (String.Compare (processName, processes[i].ProcessName, true) == 0)
506                                                 processes [size++] = processes[i];
507                                 } catch (SystemException) {
508                                         /* The process might exit between GetProcesses_internal and GetProcessById */
509                                 }
510                         }
511
512                         Array.Resize<Process> (ref processes, size);
513
514                         return processes;
515                 }
516
517                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
518                 private extern static int[] GetProcesses_internal();
519
520                 [MonoTODO ("There is no support for retrieving process information from a remote machine")]
521                 public static Process[] GetProcesses(string machineName) {
522                         if (machineName == null)
523                                 throw new ArgumentNullException ("machineName");
524
525                         if (!IsLocalMachine (machineName))
526                                 throw new NotImplementedException ();
527
528                         int [] pids = GetProcesses_internal ();
529                         if (pids == null)
530                                 return new Process [0];
531
532                         var proclist = new List<Process> (pids.Length);
533                         for (int i = 0; i < pids.Length; i++) {
534                                 try {
535                                         proclist.Add (GetProcessById (pids [i]));
536                                 } catch (SystemException) {
537                                         /* The process might exit
538                                          * between
539                                          * GetProcesses_internal and
540                                          * GetProcessById
541                                          */
542                                 }
543                         }
544
545                         return proclist.ToArray ();
546                 }
547
548                 private static bool IsLocalMachine (string machineName)
549                 {
550                         if (machineName == "." || machineName.Length == 0)
551                                 return true;
552
553                         return (string.Compare (machineName, Environment.MachineName, true) == 0);
554                 }
555
556 #if MONO_FEATURE_PROCESS_START
557                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
558                 private extern static bool ShellExecuteEx_internal(ProcessStartInfo startInfo, ref ProcInfo procInfo);
559
560                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
561                 private extern static bool CreateProcess_internal(ProcessStartInfo startInfo, IntPtr stdin, IntPtr stdout, IntPtr stderr, ref ProcInfo procInfo);
562
563                 bool StartWithShellExecuteEx (ProcessStartInfo startInfo)
564                 {
565                         if (this.disposed)
566                                 throw new ObjectDisposedException (GetType ().Name);
567
568                         if (!String.IsNullOrEmpty(startInfo.UserName) || (startInfo.Password != null))
569                                 throw new InvalidOperationException(SR.GetString(SR.CantStartAsUser));
570
571                         if (startInfo.RedirectStandardInput || startInfo.RedirectStandardOutput || startInfo.RedirectStandardError)
572                                 throw new InvalidOperationException(SR.GetString(SR.CantRedirectStreams));
573
574                         if (startInfo.StandardErrorEncoding != null)
575                                 throw new InvalidOperationException(SR.GetString(SR.StandardErrorEncodingNotAllowed));
576
577                         if (startInfo.StandardOutputEncoding != null)
578                                 throw new InvalidOperationException(SR.GetString(SR.StandardOutputEncodingNotAllowed));
579
580                         // can't set env vars with ShellExecuteEx...
581                         if (startInfo.environmentVariables != null)
582                                 throw new InvalidOperationException(SR.GetString(SR.CantUseEnvVars));
583
584                         ProcInfo procInfo = new ProcInfo();
585                         bool ret;
586
587                         FillUserInfo (startInfo, ref procInfo);
588                         try {
589                                 ret = ShellExecuteEx_internal (startInfo, ref procInfo);
590                         } finally {
591                                 if (procInfo.Password != IntPtr.Zero)
592                                         Marshal.ZeroFreeBSTR (procInfo.Password);
593                                 procInfo.Password = IntPtr.Zero;
594                         }
595                         if (!ret) {
596                                 throw new Win32Exception (-procInfo.pid);
597                         }
598
599                         SetProcessHandle (new SafeProcessHandle (procInfo.process_handle, true));
600                         SetProcessId (procInfo.pid);
601
602                         return ret;
603                 }
604
605                 //
606                 // Creates a pipe with read and write descriptors
607                 //
608                 static void CreatePipe (out IntPtr read, out IntPtr write, bool writeDirection)
609                 {
610                         MonoIOError error;
611
612                         //
613                         // Creates read/write pipe from parent -> child perspective
614                         // a child process uses same descriptors after fork. That's
615                         // 4 descriptors in total where only 2. One in child, one in parent
616                         // should be active and the other 2 closed. Which ones depends on
617                         // comunication direction
618                         //
619                         // parent  -------->  child   (parent can write, child can read)
620                         //
621                         // read: closed       read: used
622                         // write: used        write: closed
623                         //
624                         //
625                         // parent  <--------  child   (parent can read, child can write)
626                         //
627                         // read: used         read: closed
628                         // write: closed      write: used
629                         //
630                         // It can still be tricky for predefined descriptiors http://unixwiz.net/techtips/remap-pipe-fds.html
631                         //
632                         if (!MonoIO.CreatePipe (out read, out write, out error))
633                                 throw MonoIO.GetException (error);
634
635                         if (IsWindows) {
636                                 const int DUPLICATE_SAME_ACCESS = 0x00000002;
637                                 var tmp = writeDirection ? write : read;
638
639                                 if (!MonoIO.DuplicateHandle (Process.GetCurrentProcess ().Handle, tmp, Process.GetCurrentProcess ().Handle, out tmp, 0, 0, DUPLICATE_SAME_ACCESS, out error))
640                                         throw MonoIO.GetException (error);
641
642                                 if (writeDirection) {
643                                         if (!MonoIO.Close (write, out error))
644                                                 throw MonoIO.GetException (error);
645                                         write = tmp;
646                                 } else {
647                                         if (!MonoIO.Close (read, out error))
648                                                 throw MonoIO.GetException (error);
649                                         read = tmp;
650                                 }
651                         }
652                 }
653
654                 static bool IsWindows
655                 {
656                         get
657                         {
658                                 PlatformID platform = Environment.OSVersion.Platform;
659                                 if (platform == PlatformID.Win32S ||
660                                         platform == PlatformID.Win32Windows ||
661                                         platform == PlatformID.Win32NT ||
662                                         platform == PlatformID.WinCE) {
663                                         return true;
664                                 }
665                                 return false;
666                         }
667                 }
668
669                 bool StartWithCreateProcess (ProcessStartInfo startInfo)
670                 {
671                         if (startInfo.StandardOutputEncoding != null && !startInfo.RedirectStandardOutput)
672                                 throw new InvalidOperationException (SR.GetString(SR.StandardOutputEncodingNotAllowed));
673
674                         if (startInfo.StandardErrorEncoding != null && !startInfo.RedirectStandardError)
675                                 throw new InvalidOperationException (SR.GetString(SR.StandardErrorEncodingNotAllowed));
676
677                         if (this.disposed)
678                                 throw new ObjectDisposedException (GetType ().Name);
679
680                         var procInfo = new ProcInfo ();
681
682                         if (startInfo.HaveEnvVars) {
683                                 List<string> envVariables = null;
684                                 StringBuilder sb = null;
685
686                                 foreach (DictionaryEntry de in startInfo.EnvironmentVariables) {
687                                         if (de.Value == null)
688                                                 continue;
689
690                                         if (envVariables == null)
691                                                 envVariables = new List<string> ();
692
693                                         if (sb == null)
694                                                 sb = new StringBuilder ();
695                                         else
696                                                 sb.Clear ();
697
698                                         sb.Append ((string) de.Key);
699                                         sb.Append ('=');
700                                         sb.Append ((string) de.Value);
701
702                                         envVariables.Add (sb.ToString ());
703                                 }
704
705                                 procInfo.envVariables = envVariables?.ToArray ();
706                         }
707
708                         MonoIOError error;
709                         IntPtr stdin_read = IntPtr.Zero, stdin_write = IntPtr.Zero;
710                         IntPtr stdout_read = IntPtr.Zero, stdout_write = IntPtr.Zero;
711                         IntPtr stderr_read = IntPtr.Zero, stderr_write = IntPtr.Zero;
712
713                         try {
714                                 if (startInfo.RedirectStandardInput) {
715                                         CreatePipe (out stdin_read, out stdin_write, true);
716                                 } else {
717                                         stdin_read = MonoIO.ConsoleInput;
718                                         stdin_write = IntPtr.Zero;
719                                 }
720
721                                 if (startInfo.RedirectStandardOutput) {
722                                         CreatePipe (out stdout_read, out stdout_write, false);
723                                 } else {
724                                         stdout_read = IntPtr.Zero;
725                                         stdout_write = MonoIO.ConsoleOutput;
726                                 }
727
728                                 if (startInfo.RedirectStandardError) {
729                                         CreatePipe (out stderr_read, out stderr_write, false);
730                                 } else {
731                                         stderr_read = IntPtr.Zero;
732                                         stderr_write = MonoIO.ConsoleError;
733                                 }
734
735                                 FillUserInfo (startInfo, ref procInfo);
736
737                                 //
738                                 // FIXME: For redirected pipes we need to send descriptors of
739                                 // stdin_write, stdout_read, stderr_read to child process and
740                                 // close them there (fork makes exact copy of parent's descriptors)
741                                 //
742                                 if (!CreateProcess_internal (startInfo, stdin_read, stdout_write, stderr_write, ref procInfo)) {
743                                         throw new Win32Exception (-procInfo.pid, "ApplicationName='" + startInfo.FileName + "', CommandLine='" + startInfo.Arguments +
744                                                 "', CurrentDirectory='" + startInfo.WorkingDirectory + "', Native error= " + Win32Exception.GetErrorMessage (-procInfo.pid));
745                                 }
746                         } catch {
747                                 if (startInfo.RedirectStandardInput) {
748                                         if (stdin_read != IntPtr.Zero)
749                                                 MonoIO.Close (stdin_read, out error);
750                                         if (stdin_write != IntPtr.Zero)
751                                                 MonoIO.Close (stdin_write, out error);
752                                 }
753
754                                 if (startInfo.RedirectStandardOutput) {
755                                         if (stdout_read != IntPtr.Zero)
756                                                 MonoIO.Close (stdout_read, out error);
757                                         if (stdout_write != IntPtr.Zero)
758                                                 MonoIO.Close (stdout_write, out error);
759                                 }
760
761                                 if (startInfo.RedirectStandardError) {
762                                         if (stderr_read != IntPtr.Zero)
763                                                 MonoIO.Close (stderr_read, out error);
764                                         if (stderr_write != IntPtr.Zero)
765                                                 MonoIO.Close (stderr_write, out error);
766                                 }
767
768                                 throw;
769                         } finally {
770                                 if (procInfo.Password != IntPtr.Zero) {
771                                         Marshal.ZeroFreeBSTR (procInfo.Password);
772                                         procInfo.Password = IntPtr.Zero;
773                                 }
774                         }
775
776                         SetProcessHandle (new SafeProcessHandle (procInfo.process_handle, true));
777                         SetProcessId (procInfo.pid);
778                         
779 #pragma warning disable 618
780
781                         if (startInfo.RedirectStandardInput) {
782                                 MonoIO.Close (stdin_read, out error);
783
784 #if MOBILE
785                                 var stdinEncoding = Encoding.Default;
786 #else
787                                 var stdinEncoding = Console.InputEncoding;
788 #endif
789                                 standardInput = new StreamWriter (new FileStream (stdin_write, FileAccess.Write, true, 8192), stdinEncoding) {
790                                         AutoFlush = true
791                                 };
792                         }
793
794                         if (startInfo.RedirectStandardOutput) {
795                                 MonoIO.Close (stdout_write, out error);
796
797                                 Encoding stdoutEncoding = startInfo.StandardOutputEncoding ?? Console.Out.Encoding;
798
799                                 standardOutput = new StreamReader (new FileStream (stdout_read, FileAccess.Read, true, 8192), stdoutEncoding, true);
800                         }
801
802                         if (startInfo.RedirectStandardError) {
803                                 MonoIO.Close (stderr_write, out error);
804
805                                 Encoding stderrEncoding = startInfo.StandardErrorEncoding ?? Console.Out.Encoding;
806
807                                 standardError = new StreamReader (new FileStream (stderr_read, FileAccess.Read, true, 8192), stderrEncoding, true);
808                         }
809 #pragma warning restore
810
811                         return true;
812                 }
813
814                 // Note that ProcInfo.Password must be freed.
815                 private static void FillUserInfo (ProcessStartInfo startInfo, ref ProcInfo procInfo)
816                 {
817                         if (startInfo.UserName.Length != 0) {
818                                 procInfo.UserName = startInfo.UserName;
819                                 procInfo.Domain = startInfo.Domain;
820                                 if (startInfo.Password != null)
821                                         procInfo.Password = Marshal.SecureStringToBSTR (startInfo.Password);
822                                 else
823                                         procInfo.Password = IntPtr.Zero;
824                                 procInfo.LoadUserProfile = startInfo.LoadUserProfile;
825                         }
826                 }
827 #else
828                 [Obsolete ("Process.Start is not supported on the current platform.", true)]
829                 public bool Start ()
830                 {
831                         throw new PlatformNotSupportedException ("Process.Start is not supported on the current platform.");
832                 }
833
834                 [Obsolete ("Process.Start is not supported on the current platform.", true)]
835                 public static Process Start (ProcessStartInfo startInfo)
836                 {
837                         throw new PlatformNotSupportedException ("Process.Start is not supported on the current platform.");
838                 }
839
840                 [Obsolete ("Process.Start is not supported on the current platform.", true)]
841                 public static Process Start (string fileName)
842                 {
843                         throw new PlatformNotSupportedException ("Process.Start is not supported on the current platform.");
844                 }
845
846                 [Obsolete ("Process.Start is not supported on the current platform.", true)]
847                 public static Process Start(string fileName, string arguments)
848                 {
849                         throw new PlatformNotSupportedException ("Process.Start is not supported on the current platform.");
850                 }
851
852                 [Obsolete ("Process.Start is not supported on the current platform.", true)]
853                 public static Process Start(string fileName, string userName, SecureString password, string domain)
854                 {
855                         throw new PlatformNotSupportedException ("Process.Start is not supported on the current platform.");
856                 }
857
858                 [Obsolete ("Process.Start is not supported on the current platform.", true)]
859                 public static Process Start(string fileName, string arguments, string userName, SecureString password, string domain)
860                 {
861                         throw new PlatformNotSupportedException ("Process.Start is not supported on the current platform.");
862                 }
863 #endif // MONO_FEATURE_PROCESS_START
864
865 #if !MONO_FEATURE_PROCESS_START
866                 [Obsolete ("Process.BeginOutputReadLine is not supported on the current platform.", true)]
867                 public void BeginOutputReadLine ()
868                 {
869                         throw new PlatformNotSupportedException ("Process.BeginOutputReadLine is not supported on the current platform.");
870                 }
871
872                 [Obsolete ("Process.BeginOutputReadLine is not supported on the current platform.", true)]
873                 public void CancelOutputRead ()
874                 {
875                         throw new PlatformNotSupportedException ("Process.BeginOutputReadLine is not supported on the current platform.");
876                 }
877
878                 [Obsolete ("Process.BeginOutputReadLine is not supported on the current platform.", true)]
879                 public void BeginErrorReadLine ()
880                 {
881                         throw new PlatformNotSupportedException ("Process.BeginOutputReadLine is not supported on the current platform.");
882                 }
883
884                 [Obsolete ("Process.BeginOutputReadLine is not supported on the current platform.", true)]
885                 public void CancelErrorRead ()
886                 {
887                         throw new PlatformNotSupportedException ("Process.BeginOutputReadLine is not supported on the current platform.");
888                 }
889 #endif // !MONO_FEATURE_PROCESS_START
890
891                 /// <devdoc>
892                 ///     Raise the Exited event, but make sure we don't do it more than once.
893                 /// </devdoc>
894                 /// <internalonly/>
895                 void RaiseOnExited() {
896                         if (!watchForExit)
897                                 return;
898                         if (!raisedOnExited) {
899                                 lock (this) {
900                                         if (!raisedOnExited) {
901                                                 raisedOnExited = true;
902                                                 OnExited();
903                                         }
904                                 }
905                         }
906                 }
907         }
908 }
909