2004-09-06 Dick Porter <dick@ximian.com>
[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 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;
36 using System.IO;
37 using System.ComponentModel;
38 using System.ComponentModel.Design;
39 using System.Runtime.CompilerServices;
40 using System.Runtime.InteropServices;
41 using System.Collections;
42 using System.Threading;
43
44 namespace System.Diagnostics {
45         [DefaultEvent ("Exited"), DefaultProperty ("StartInfo")]
46         [Designer ("System.Diagnostics.Design.ProcessDesigner, " + Consts.AssemblySystem_Design, typeof (IDesigner))]
47         public class Process : Component 
48         {
49                 [StructLayout(LayoutKind.Sequential)]
50                 private struct ProcInfo 
51                 {
52                         public IntPtr process_handle;
53                         /* If thread_handle is ever needed for
54                          * something, take out the CloseHandle() in
55                          * the Start_internal icall in
56                          * mono/metadata/process.c
57                          */
58                         public IntPtr thread_handle;
59                         public int pid; // Contains -GetLastError () on failure.
60                         public int tid;
61                         public string [] envKeys;
62                         public string [] envValues;
63                         public bool useShellExecute;
64                 };
65                 
66                 IntPtr process_handle;
67                 int pid;
68                 bool enableRaisingEvents;
69                 ISynchronizeInvoke synchronizingObject;
70                 
71                 /* Private constructor called from other methods */
72                 private Process(IntPtr handle, int id) {
73                         process_handle=handle;
74                         pid=id;
75                 }
76                 
77                 [MonoTODO]
78                 public Process() {
79                 }
80
81                 [MonoTODO]
82                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
83                 [MonitoringDescription ("Base process priority.")]
84                 public int BasePriority {
85                         get {
86                                 return(0);
87                         }
88                 }
89
90                 [DefaultValue (false), Browsable (false)]
91                 [MonitoringDescription ("Check for exiting of the process to raise the apropriate event.")]
92                 public bool EnableRaisingEvents {
93                         get {
94                                 return enableRaisingEvents;
95                         }
96                         set { 
97                                 if (process_handle != IntPtr.Zero)
98                                         throw new InvalidOperationException ("The process is already started.");
99
100                                 enableRaisingEvents = value;
101                         }
102
103                 }
104
105                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
106                 private extern static int ExitCode_internal(IntPtr handle);
107
108                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
109                 [MonitoringDescription ("The exit code of the process.")]
110                 public int ExitCode {
111                         get {
112                                 if (process_handle == IntPtr.Zero)
113                                         throw new InvalidOperationException ("Process has not been started.");
114
115                                 int code = ExitCode_internal (process_handle);
116                                 if (code == 259)
117                                         throw new InvalidOperationException ("The process must exit before " +
118                                                                         "getting the requested information.");
119
120                                 return code;
121                         }
122                 }
123
124                 /* Returns the process start time in Windows file
125                  * times (ticks from DateTime(1/1/1601 00:00 GMT))
126                  */
127                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
128                 private extern static long ExitTime_internal(IntPtr handle);
129                 
130                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
131                 [MonitoringDescription ("The exit time of the process.")]
132                 public DateTime ExitTime {
133                         get {
134                                 if (process_handle == IntPtr.Zero)
135                                         throw new InvalidOperationException ("Process has not been started.");
136
137                                 if (!HasExited)
138                                         throw new InvalidOperationException ("The process must exit before " +
139                                                                         "getting the requested information.");
140
141                                 return(DateTime.FromFileTime(ExitTime_internal(process_handle)));
142                         }
143                 }
144
145                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
146                 [MonitoringDescription ("Handle for this process.")]
147                 public IntPtr Handle {
148                         get {
149                                 return(process_handle);
150                         }
151                 }
152
153                 [MonoTODO]
154                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
155                 [MonitoringDescription ("Handles for this process.")]
156                 public int HandleCount {
157                         get {
158                                 return(0);
159                         }
160                 }
161
162                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
163                 [MonitoringDescription ("Determines if the process is still running.")]
164                 public bool HasExited {
165                         get {
166                                 if (process_handle == IntPtr.Zero)
167                                         throw new InvalidOperationException ("Process has not been started.");
168                                         
169                                 int exitcode = ExitCode_internal (process_handle);
170
171                                 if(exitcode==259) {
172                                         /* STILL_ACTIVE */
173                                         return(false);
174                                 } else {
175                                         return(true);
176                                 }
177                         }
178                 }
179
180                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
181                 [MonitoringDescription ("Process identifier.")]
182                 public int Id {
183                         get {
184                                 return(pid);
185                         }
186                 }
187
188                 [MonoTODO]
189                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
190                 [MonitoringDescription ("The name of the computer running the process.")]
191                 public string MachineName {
192                         get {
193                                 return("localhost");
194                         }
195                 }
196
197                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
198                 [MonitoringDescription ("The main module of the process.")]
199                 public ProcessModule MainModule {
200                         get {
201                                 return(this.Modules[0]);
202                         }
203                 }
204
205                 [MonoTODO]
206                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
207                 [MonitoringDescription ("The handle of the main window of the process.")]
208                 public IntPtr MainWindowHandle {
209                         get {
210                                 return((IntPtr)0);
211                         }
212                 }
213
214                 [MonoTODO]
215                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
216                 [MonitoringDescription ("The title of the main window of the process.")]
217                 public string MainWindowTitle {
218                         get {
219                                 return("null");
220                         }
221                 }
222
223                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
224                 private extern static bool GetWorkingSet_internal(IntPtr handle, out int min, out int max);
225                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
226                 private extern static bool SetWorkingSet_internal(IntPtr handle, int min, int max, bool use_min);
227
228                 /* LAMESPEC: why is this an IntPtr not a plain int? */
229                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
230                 [MonitoringDescription ("The maximum working set for this process.")]
231                 public IntPtr MaxWorkingSet {
232                         get {
233                                 if(HasExited) {
234                                         throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
235                                 }
236                                 
237                                 int min;
238                                 int max;
239                                 bool ok=GetWorkingSet_internal(process_handle, out min, out max);
240                                 if(ok==false) {
241                                         throw new Win32Exception();
242                                 }
243                                 
244                                 return((IntPtr)max);
245                         }
246                         set {
247                                 if(HasExited) {
248                                         throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
249                                 }
250                                 
251                                 bool ok=SetWorkingSet_internal(process_handle, 0, value.ToInt32(), false);
252                                 if(ok==false) {
253                                         throw new Win32Exception();
254                                 }
255                         }
256                 }
257
258                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
259                 [MonitoringDescription ("The minimum working set for this process.")]
260                 public IntPtr MinWorkingSet {
261                         get {
262                                 if(HasExited) {
263                                         throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
264                                 }
265                                 
266                                 int min;
267                                 int max;
268                                 bool ok=GetWorkingSet_internal(process_handle, out min, out max);
269                                 if(ok==false) {
270                                         throw new Win32Exception();
271                                 }
272                                 
273                                 return((IntPtr)min);
274                         }
275                         set {
276                                 if(HasExited) {
277                                         throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
278                                 }
279                                 
280                                 bool ok=SetWorkingSet_internal(process_handle, value.ToInt32(), 0, true);
281                                 if(ok==false) {
282                                         throw new Win32Exception();
283                                 }
284                         }
285                 }
286
287                 /* Returns the list of process modules.  The main module is
288                  * element 0.
289                  */
290                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
291                 private extern ProcessModule[] GetModules_internal();
292
293                 private ProcessModuleCollection module_collection;
294                 
295                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
296                 [MonitoringDescription ("The modules that are loaded as part of this process.")]
297                 public ProcessModuleCollection Modules {
298                         get {
299                                 if(module_collection==null) {
300                                         module_collection=new ProcessModuleCollection(GetModules_internal());
301                                 }
302
303                                 return(module_collection);
304                         }
305                 }
306
307                 [MonoTODO]
308                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
309                 [MonitoringDescription ("The number of bytes that are not pageable.")]
310                 public int NonpagedSystemMemorySize {
311                         get {
312                                 return(0);
313                         }
314                 }
315
316                 [MonoTODO]
317                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
318                 [MonitoringDescription ("The number of bytes that are paged.")]
319                 public int PagedMemorySize {
320                         get {
321                                 return(0);
322                         }
323                 }
324
325                 [MonoTODO]
326                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
327                 [MonitoringDescription ("The amount of paged system memory in bytes.")]
328                 public int PagedSystemMemorySize {
329                         get {
330                                 return(0);
331                         }
332                 }
333
334                 [MonoTODO]
335                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
336                 [MonitoringDescription ("The maximum amount of paged memory used by this process.")]
337                 public int PeakPagedMemorySize {
338                         get {
339                                 return(0);
340                         }
341                 }
342
343                 [MonoTODO]
344                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
345                 [MonitoringDescription ("The maximum amount of virtual memory used by this process.")]
346                 public int PeakVirtualMemorySize {
347                         get {
348                                 return(0);
349                         }
350                 }
351
352                 [MonoTODO]
353                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
354                 [MonitoringDescription ("The maximum amount of system memory used by this process.")]
355                 public int PeakWorkingSet {
356                         get {
357                                 return(0);
358                         }
359                 }
360
361                 [MonoTODO]
362                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
363                 [MonitoringDescription ("Process will be of higher priority while it is actively used.")]
364                 public bool PriorityBoostEnabled {
365                         get {
366                                 return(false);
367                         }
368                         set {
369                         }
370                 }
371
372                 [MonoTODO]
373                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
374                 [MonitoringDescription ("The relative process priority.")]
375                 public ProcessPriorityClass PriorityClass {
376                         get {
377                                 return(ProcessPriorityClass.Normal);
378                         }
379                         set {
380                         }
381                 }
382
383                 [MonoTODO]
384                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
385                 [MonitoringDescription ("The amount of memory exclusively used by this process.")]
386                 public int PrivateMemorySize {
387                         get {
388                                 return(0);
389                         }
390                 }
391
392                 [MonoTODO]
393                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
394                 [MonitoringDescription ("The amount of processing time spent in the OS core for this process.")]
395                 public TimeSpan PrivilegedProcessorTime {
396                         get {
397                                 return(new TimeSpan(0));
398                         }
399                 }
400
401                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
402                 private extern static string ProcessName_internal(IntPtr handle);
403                 
404                 private string process_name=null;
405                 
406                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
407                 [MonitoringDescription ("The name of this process.")]
408                 public string ProcessName {
409                         get {
410                                 if(process_name==null) {
411                                         process_name=ProcessName_internal(process_handle);
412                                         /* If process_name is _still_
413                                          * null, assume the process
414                                          * has exited
415                                          */
416                                         if(process_name==null) {
417                                                 throw new SystemException("The process has exited");
418                                         }
419                                         
420                                         /* Strip the suffix (if it
421                                          * exists) simplistically
422                                          * instead of removing any
423                                          * trailing \.???, so we dont
424                                          * get stupid results on sane
425                                          * systems
426                                          */
427                                         if(process_name.EndsWith(".exe") ||
428                                            process_name.EndsWith(".bat") ||
429                                            process_name.EndsWith(".com")) {
430                                                 process_name=process_name.Substring(0, process_name.Length-4);
431                                         }
432                                 }
433                                 return(process_name);
434                         }
435                 }
436
437                 [MonoTODO]
438                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
439                 [MonitoringDescription ("Allowed processor that can be used by this process.")]
440                 public IntPtr ProcessorAffinity {
441                         get {
442                                 return((IntPtr)0);
443                         }
444                         set {
445                         }
446                 }
447
448                 [MonoTODO]
449                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
450                 [MonitoringDescription ("Is this process responsive.")]
451                 public bool Responding {
452                         get {
453                                 return(false);
454                         }
455                 }
456
457                 private StreamReader error_stream=null;
458                 
459                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
460                 [MonitoringDescription ("The standard error stream of this process.")]
461                 public StreamReader StandardError {
462                         get {
463                                 return(error_stream);
464                         }
465                 }
466
467                 private StreamWriter input_stream=null;
468                 
469                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
470                 [MonitoringDescription ("The standard input stream of this process.")]
471                 public StreamWriter StandardInput {
472                         get {
473                                 return(input_stream);
474                         }
475                 }
476
477                 private StreamReader output_stream=null;
478                 
479                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
480                 [MonitoringDescription ("The standard output stream of this process.")]
481                 public StreamReader StandardOutput {
482                         get {
483                                 return(output_stream);
484                         }
485                 }
486
487                 private ProcessStartInfo start_info=null;
488                 
489                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
490                 [MonitoringDescription ("Information for the start of this process.")]
491                 public ProcessStartInfo StartInfo {
492                         get {
493                                 if(start_info==null) {
494                                         start_info=new ProcessStartInfo();
495                                 }
496                                 
497                                 return(start_info);
498                         }
499                         set {
500                                 if(value==null) {
501                                         throw new ArgumentException("value is null");
502                                 }
503                                 
504                                 start_info=value;
505                         }
506                 }
507
508                 /* Returns the process start time in Windows file
509                  * times (ticks from DateTime(1/1/1601 00:00 GMT))
510                  */
511                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
512                 private extern static long StartTime_internal(IntPtr handle);
513                 
514                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
515                 [MonitoringDescription ("The time this process started.")]
516                 public DateTime StartTime {
517                         get {
518                                 return(DateTime.FromFileTime(StartTime_internal(process_handle)));
519                         }
520                 }
521
522                 [DefaultValue (null), Browsable (false)]
523                 [MonitoringDescription ("The object that is used to synchronize event handler calls for this process.")]
524                 public ISynchronizeInvoke SynchronizingObject {
525                         get { return synchronizingObject; }
526                         set { synchronizingObject = value; }
527                 }
528
529                 [MonoTODO]
530                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
531                 [MonitoringDescription ("The number of threads of this process.")]
532                 public ProcessThreadCollection Threads {
533                         get {
534                                 return(null);
535                         }
536                 }
537
538                 [MonoTODO]
539                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
540                 [MonitoringDescription ("The total CPU time spent for this process.")]
541                 public TimeSpan TotalProcessorTime {
542                         get {
543                                 return(new TimeSpan(0));
544                         }
545                 }
546
547                 [MonoTODO]
548                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
549                 [MonitoringDescription ("The CPU time spent for this process in user mode.")]
550                 public TimeSpan UserProcessorTime {
551                         get {
552                                 return(new TimeSpan(0));
553                         }
554                 }
555
556                 [MonoTODO]
557                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
558                 [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
559                 public int VirtualMemorySize {
560                         get {
561                                 return(0);
562                         }
563                 }
564
565                 [MonoTODO]
566                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
567                 [MonitoringDescription ("The amount of physical memory currently used for this process.")]
568                 public int WorkingSet {
569                         get {
570                                 return(0);
571                         }
572                 }
573
574                 public void Close()
575                 {
576                         Dispose (true);
577                 }
578
579                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
580                 extern static bool Kill_internal (IntPtr handle, int signo);
581
582                 /* int kill -> 1 KILL, 2 CloseMainWindow */
583                 bool Close (int signo)
584                 {
585                         if (process_handle == IntPtr.Zero)
586                                 throw new SystemException ("No process to kill.");
587
588                         int exitcode = ExitCode_internal (process_handle);
589                         if (exitcode != 259)
590                                 throw new InvalidOperationException ("The process already finished.");
591
592                         return Kill_internal (process_handle, signo);
593                 }
594
595                 public bool CloseMainWindow ()
596                 {
597                         return Close (2);
598                 }
599
600                 [MonoTODO]
601                 public static void EnterDebugMode() {
602                 }
603
604                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
605                 private extern static IntPtr GetProcess_internal(int pid);
606                 
607                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
608                 private extern static int GetPid_internal();
609
610                 public static Process GetCurrentProcess() {
611                         int pid=GetPid_internal();
612                         IntPtr proc=GetProcess_internal(pid);
613                         
614                         if(proc==IntPtr.Zero) {
615                                 throw new SystemException("Can't find current process");
616                         }
617
618                         return(new Process(proc, pid));
619                 }
620
621                 public static Process GetProcessById(int processId) {
622                         IntPtr proc=GetProcess_internal(processId);
623                         
624                         if(proc==IntPtr.Zero) {
625                                 throw new ArgumentException("Can't find process with ID " + processId.ToString());
626                         }
627
628                         return(new Process(proc, processId));
629                 }
630
631                 [MonoTODO]
632                 public static Process GetProcessById(int processId, string machineName) {
633                         throw new NotImplementedException();
634                 }
635
636                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
637                 private extern static int[] GetProcesses_internal();
638
639                 public static Process[] GetProcesses() {
640                         int[] pids=GetProcesses_internal();
641                         ArrayList proclist=new ArrayList();
642                         
643                         for(int i=0; i<pids.Length; i++) {
644                                 try {
645                                         proclist.Add(GetProcessById(pids[i]));
646                                 } catch (SystemException) {
647                                         /* The process might exit
648                                          * between
649                                          * GetProcesses_internal and
650                                          * GetProcessById
651                                          */
652                                 }
653                         }
654
655                         return((Process[])proclist.ToArray(typeof(Process)));
656                 }
657
658                 [MonoTODO]
659                 public static Process[] GetProcesses(string machineName) {
660                         throw new NotImplementedException();
661                 }
662
663                 public static Process[] GetProcessesByName(string processName) {
664                         Process[] procs=GetProcesses();
665                         ArrayList proclist=new ArrayList();
666                         
667                         for(int i=0; i<procs.Length; i++) {
668                                 /* Ignore case */
669                                 if(String.Compare(processName,
670                                                   procs[i].ProcessName,
671                                                   true)==0) {
672                                         proclist.Add(procs[i]);
673                                 }
674                         }
675
676                         return((Process[])proclist.ToArray(typeof(Process)));
677                 }
678
679                 [MonoTODO]
680                 public static Process[] GetProcessesByName(string processName, string machineName) {
681                         throw new NotImplementedException();
682                 }
683
684                 public void Kill ()
685                 {
686                         Close (1);
687                 }
688
689                 [MonoTODO]
690                 public static void LeaveDebugMode() {
691                 }
692
693                 [MonoTODO]
694                 public void Refresh() {
695                 }
696
697                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
698                 private extern static bool Start_internal(string appname,
699                                                           string cmdline,
700                                                           string dir,
701                                                           IntPtr stdin,
702                                                           IntPtr stdout,
703                                                           IntPtr stderr,
704                                                           ref ProcInfo proc_info);
705
706                 private static bool Start_common(ProcessStartInfo startInfo,
707                                                  Process process) {
708                         ProcInfo proc_info=new ProcInfo();
709                         IntPtr stdin_rd, stdin_wr;
710                         IntPtr stdout_rd, stdout_wr;
711                         IntPtr stderr_rd, stderr_wr;
712                         bool ret;
713                         
714                         if(startInfo.FileName == null || startInfo.FileName == "") {
715                                 throw new InvalidOperationException("File name has not been set");
716                         }
717                         
718                         proc_info.useShellExecute = startInfo.UseShellExecute;
719                         if (proc_info.useShellExecute && (startInfo.RedirectStandardInput ||
720                             startInfo.RedirectStandardOutput || startInfo.RedirectStandardError)) {
721                                 throw new InvalidOperationException ("UseShellExecute must be false when " +
722                                                                      "redirecting I/O.");
723                         }
724
725                         if (startInfo.HaveEnvVars) {
726                                 if (startInfo.UseShellExecute)
727                                         throw new InvalidOperationException ("UseShellExecute must be false in order " +
728                                                                              "to use environment variables.");
729
730                                 string [] strs = new string [startInfo.EnvironmentVariables.Count];
731                                 startInfo.EnvironmentVariables.Keys.CopyTo (strs, 0);
732                                 proc_info.envKeys = strs;
733
734                                 strs = new string [startInfo.EnvironmentVariables.Count];
735                                 startInfo.EnvironmentVariables.Values.CopyTo (strs, 0);
736                                 proc_info.envValues = strs;
737                         }
738
739                         if(startInfo.RedirectStandardInput==true) {
740                                 ret=MonoIO.CreatePipe(out stdin_rd,
741                                                       out stdin_wr);
742                         } else {
743                                 stdin_rd=MonoIO.ConsoleInput;
744                                 /* This is required to stop the
745                                  * &$*£ing stupid compiler moaning
746                                  * that stdin_wr is unassigned, below.
747                                  */
748                                 stdin_wr=(IntPtr)0;
749                         }
750
751                         if(startInfo.RedirectStandardOutput==true) {
752                                 ret=MonoIO.CreatePipe(out stdout_rd,
753                                                       out stdout_wr);
754                         } else {
755                                 stdout_rd=(IntPtr)0;
756                                 stdout_wr=MonoIO.ConsoleOutput;
757                         }
758
759                         if(startInfo.RedirectStandardError==true) {
760                                 ret=MonoIO.CreatePipe(out stderr_rd,
761                                                       out stderr_wr);
762                         } else {
763                                 stderr_rd=(IntPtr)0;
764                                 stderr_wr=MonoIO.ConsoleError;
765                         }
766                         
767                         string cmdline;
768                         string appname;
769                         if (startInfo.UseShellExecute) {
770                                 appname = null;
771                                 string args = startInfo.Arguments;
772                                 if (args == null || args.Trim () == "")
773                                         cmdline = startInfo.FileName;
774                                 else
775                                         cmdline = startInfo.FileName + " " + startInfo.Arguments.Trim ();
776                         } else {
777                                 appname = startInfo.FileName;
778                                 // FIXME: There seems something wrong in process.c. We should not require extraneous command line
779                                 if (Path.DirectorySeparatorChar == '\\')
780                                         cmdline = appname + " " + startInfo.Arguments.Trim ();
781                                 else
782                                         cmdline = startInfo.Arguments.Trim ();
783                         }
784
785                         ret=Start_internal(appname,
786                                            cmdline,
787                                            startInfo.WorkingDirectory,
788                                            stdin_rd, stdout_wr, stderr_wr,
789                                            ref proc_info);
790
791                         MonoIOError error;
792                         
793                         if (!ret) {
794                                 if (startInfo.RedirectStandardInput == true)
795                                         MonoIO.Close (stdin_rd, out error);
796
797                                 if (startInfo.RedirectStandardOutput == true)
798                                         MonoIO.Close (stdout_wr, out error);
799
800                                 if (startInfo.RedirectStandardError == true)
801                                         MonoIO.Close (stderr_wr, out error);
802
803                                 throw new Win32Exception (-proc_info.pid);
804                         }
805
806                         if (process.enableRaisingEvents && process.Exited != null) {
807                                 WaitOrTimerCallback cb = new WaitOrTimerCallback (CBOnExit);
808                                 ProcessWaitHandle h = new ProcessWaitHandle (proc_info.process_handle);
809                                 ThreadPool.RegisterWaitForSingleObject (h, cb, process, -1, true);
810                         }
811                         
812                         process.process_handle=proc_info.process_handle;
813                         process.pid=proc_info.pid;
814                         
815                         if(startInfo.RedirectStandardInput==true) {
816                                 MonoIO.Close(stdin_rd, out error);
817                                 process.input_stream=new StreamWriter(new FileStream(stdin_wr, FileAccess.Write, true));
818                                 process.input_stream.AutoFlush=true;
819                         }
820
821                         if(startInfo.RedirectStandardOutput==true) {
822                                 MonoIO.Close(stdout_wr, out error);
823                                 process.output_stream=new StreamReader(new FileStream(stdout_rd, FileAccess.Read, true));
824                         }
825
826                         if(startInfo.RedirectStandardError==true) {
827                                 MonoIO.Close(stderr_wr, out error);
828                                 process.error_stream=new StreamReader(new FileStream(stderr_rd, FileAccess.Read, true));
829                         }
830
831                         return(ret);
832                 }
833                 
834                 public bool Start() {
835                         bool ret;
836                         
837                         ret=Start_common(start_info, this);
838                         
839                         return(ret);
840                 }
841
842                 public static Process Start(ProcessStartInfo startInfo) {
843                         Process process=new Process();
844                         bool ret;
845
846                         ret=Start_common(startInfo, process);
847                         
848                         if(ret==true) {
849                                 return(process);
850                         } else {
851                                 return(null);
852                         }
853                 }
854
855                 public static Process Start(string fileName) {
856                        return Start(new ProcessStartInfo(fileName));
857                 }
858
859                 public static Process Start(string fileName,
860                                             string arguments) {
861                        return Start(new ProcessStartInfo(fileName, arguments));
862                 }
863
864                 public override string ToString() {
865                         return(base.ToString() +
866                                " (" + this.ProcessName + ")");
867                 }
868
869                 /* Waits up to ms milliseconds for process 'handle' to
870                  * exit.  ms can be <0 to mean wait forever.
871                  */
872                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
873                 private extern bool WaitForExit_internal(IntPtr handle,
874                                                          int ms);
875
876                 public void WaitForExit() {
877                         WaitForExit_internal(process_handle, -1);
878                 }
879
880                 public bool WaitForExit(int milliseconds) {
881                         if (milliseconds == int.MaxValue)
882                                 milliseconds = -1;
883
884                         return WaitForExit_internal (process_handle, milliseconds);
885                 }
886
887                 [MonoTODO]
888                 public bool WaitForInputIdle() {
889                         return(false);
890                 }
891
892                 [MonoTODO]
893                 public bool WaitForInputIdle(int milliseconds) {
894                         return(false);
895                 }
896
897                 [Category ()]
898                 [MonitoringDescription ("Raised when this process exits.")]
899                 public event EventHandler Exited;
900
901                 // Closes the system process handle
902                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
903                 private extern void Process_free_internal(IntPtr handle);
904                 
905                 private bool disposed = false;
906                 
907                 protected override void Dispose(bool disposing) {
908                         // Check to see if Dispose has already been called.
909                         if(this.disposed == false) {
910                                 this.disposed=true;
911                                 // If this is a call to Dispose,
912                                 // dispose all managed resources.
913                                 if(disposing) {
914                                         // Do stuff here
915                                 }
916                                 
917                                 // Release unmanaged resources
918
919                                 lock(this) {
920                                         if(process_handle!=IntPtr.Zero) {
921                                                 
922                                                 Process_free_internal(process_handle);
923                                                 process_handle=IntPtr.Zero;
924                                         }
925                                 }
926                         }
927                         base.Dispose (disposing);
928                 }
929
930                 static void CBOnExit (object state, bool unused)
931                 {
932                         Process p = (Process) state;
933                         p.OnExited ();
934                 }
935
936                 protected void OnExited() 
937                 {
938                         if (Exited == null)
939                                 return;
940
941                         if (synchronizingObject == null) {
942                                 Exited (this, EventArgs.Empty);
943                                 return;
944                         }
945                         
946                         object [] args = new object [] {this, EventArgs.Empty};
947                         synchronizingObject.BeginInvoke (Exited, args);
948                 }
949
950                 class ProcessWaitHandle : WaitHandle
951                 {
952                         public ProcessWaitHandle (IntPtr handle)
953                         {
954                                 Handle = handle;
955                         }
956
957                         protected override void Dispose (bool explicitDisposing)
958                         {
959                                 // Do nothing, we don't own the handle and we won't close it.
960                         }
961                 }
962         }
963 }
964