2004-05-19 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / System / System.Diagnostics / Process.cs
index 5084af506a423f4ab2b124080dfcc8577d8e9f5d..eb7830ce64668f387faa4b16befbae1c48eb7662 100755 (executable)
@@ -2,11 +2,13 @@
 // System.Diagnostics.Process.cs
 //
 // Authors:
-//   Dick Porter (dick@ximian.com)
-//   Andreas Nahr (ClassDevelopment@A-SoftTech.com)
+//     Dick Porter (dick@ximian.com)
+//     Andreas Nahr (ClassDevelopment@A-SoftTech.com)
+//     Gonzalo Paniagua Javier (gonzalo@ximian.com)
 //
 // (C) 2002 Ximian, Inc.
 // (C) 2003 Andreas Nahr
+// (c) 2004 Novell, Inc. (http://www.novell.com)
 //
 
 using System;
@@ -16,15 +18,11 @@ using System.ComponentModel.Design;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Collections;
+using System.Threading;
 
 namespace System.Diagnostics {
-       [DefaultEvent ("Exited"), DefaultProperty ("StartInfo"), DesignerCategory ("Component")]
-       #if (NET_1_0)
-               [Designer ("System.Diagnostics.Design.ProcessDesigner, System.Design, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof (IDesigner))]
-       #endif
-       #if (NET_1_1)
-               [Designer ("System.Diagnostics.Design.ProcessDesigner, System.Design, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof (IDesigner))]
-       #endif
+       [DefaultEvent ("Exited"), DefaultProperty ("StartInfo")]
+       [Designer ("System.Diagnostics.Design.ProcessDesigner, " + Consts.AssemblySystem_Design, typeof (IDesigner))]
        public class Process : Component 
        {
                [StructLayout(LayoutKind.Sequential)]
@@ -32,12 +30,17 @@ namespace System.Diagnostics {
                {
                        public IntPtr process_handle;
                        public IntPtr thread_handle;
-                       public int pid;
+                       public int pid; // Contains -GetLastError () on failure.
                        public int tid;
+                       public string [] envKeys;
+                       public string [] envValues;
+                       public bool useShellExecute;
                };
                
                IntPtr process_handle;
                int pid;
+               bool enableRaisingEvents;
+               ISynchronizeInvoke synchronizingObject;
                
                /* Private constructor called from other methods */
                private Process(IntPtr handle, int id) {
@@ -58,15 +61,19 @@ namespace System.Diagnostics {
                        }
                }
 
-               [MonoTODO]
                [DefaultValue (false), Browsable (false)]
                [MonitoringDescription ("Check for exiting of the process to raise the apropriate event.")]
                public bool EnableRaisingEvents {
                        get {
-                               return(false);
+                               return enableRaisingEvents;
                        }
-                       set {
+                       set { 
+                               if (process_handle != IntPtr.Zero)
+                                       throw new InvalidOperationException ("The process is already started.");
+
+                               enableRaisingEvents = value;
                        }
+
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
@@ -76,7 +83,15 @@ namespace System.Diagnostics {
                [MonitoringDescription ("The exit code of the process.")]
                public int ExitCode {
                        get {
-                               return(ExitCode_internal(process_handle));
+                               if (process_handle == IntPtr.Zero)
+                                       throw new InvalidOperationException ("Process has not been started.");
+
+                               int code = ExitCode_internal (process_handle);
+                               if (code == 259)
+                                       throw new InvalidOperationException ("The process must exit before " +
+                                                                       "getting the requested information.");
+
+                               return code;
                        }
                }
 
@@ -90,6 +105,13 @@ namespace System.Diagnostics {
                [MonitoringDescription ("The exit time of the process.")]
                public DateTime ExitTime {
                        get {
+                               if (process_handle == IntPtr.Zero)
+                                       throw new InvalidOperationException ("Process has not been started.");
+
+                               if (!HasExited)
+                                       throw new InvalidOperationException ("The process must exit before " +
+                                                                       "getting the requested information.");
+
                                return(DateTime.FromFileTime(ExitTime_internal(process_handle)));
                        }
                }
@@ -115,7 +137,7 @@ namespace System.Diagnostics {
                [MonitoringDescription ("Determines if the process is still running.")]
                public bool HasExited {
                        get {
-                               int exitcode=ExitCode;
+                               int exitcode = ExitCode_internal (process_handle);
 
                                if(exitcode==259) {
                                        /* STILL_ACTIVE */
@@ -340,7 +362,7 @@ namespace System.Diagnostics {
 
                [MonoTODO]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
-               [MonitoringDescription ("The amount of processing time spent is the OS core for this process.")]
+               [MonitoringDescription ("The amount of processing time spent in the OS core for this process.")]
                public TimeSpan PrivilegedProcessorTime {
                        get {
                                return(new TimeSpan(0));
@@ -468,15 +490,11 @@ namespace System.Diagnostics {
                        }
                }
 
-               [MonoTODO]
-               [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
+               [DefaultValue (null), Browsable (false)]
                [MonitoringDescription ("The object that is used to synchronize event handler calls for this process.")]
                public ISynchronizeInvoke SynchronizingObject {
-                       get {
-                               return(null);
-                       }
-                       set {
-                       }
+                       get { return synchronizingObject; }
+                       set { synchronizingObject = value; }
                }
 
                [MonoTODO]
@@ -524,13 +542,30 @@ namespace System.Diagnostics {
                        }
                }
 
-               [MonoTODO]
-               public void Close() {
+               public void Close()
+               {
+                       Dispose (true);
                }
 
-               [MonoTODO]
-               public bool CloseMainWindow() {
-                       return(false);
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               extern static bool Kill_internal (IntPtr handle, int signo);
+
+               /* int kill -> 1 KILL, 2 CloseMainWindow */
+               bool Close (int signo)
+               {
+                       if (process_handle == IntPtr.Zero)
+                               throw new SystemException ("No process to kill.");
+
+                       int exitcode = ExitCode_internal (process_handle);
+                       if (exitcode != 259)
+                               throw new InvalidOperationException ("The process already finished.");
+
+                       return Kill_internal (process_handle, signo);
+               }
+
+               public bool CloseMainWindow ()
+               {
+                       return Close (2);
                }
 
                [MonoTODO]
@@ -617,8 +652,9 @@ namespace System.Diagnostics {
                        throw new NotImplementedException();
                }
 
-               [MonoTODO]
-               public void Kill() {
+               public void Kill ()
+               {
+                       Close (1);
                }
 
                [MonoTODO]
@@ -645,10 +681,31 @@ namespace System.Diagnostics {
                        IntPtr stderr_rd, stderr_wr;
                        bool ret;
                        
-                       if(startInfo.FileName == "") {
+                       if(startInfo.FileName == null || startInfo.FileName == "") {
                                throw new InvalidOperationException("File name has not been set");
                        }
                        
+                       proc_info.useShellExecute = startInfo.UseShellExecute;
+                       if (proc_info.useShellExecute && (startInfo.RedirectStandardInput ||
+                           startInfo.RedirectStandardOutput || startInfo.RedirectStandardError)) {
+                               throw new InvalidOperationException ("UseShellExecute must be false when " +
+                                                                    "redirecting I/O.");
+                       }
+
+                       if (startInfo.HaveEnvVars) {
+                               if (startInfo.UseShellExecute)
+                                       throw new InvalidOperationException ("UseShellExecute must be false in order " +
+                                                                            "to use environment variables.");
+
+                               string [] strs = new string [startInfo.EnvironmentVariables.Count];
+                               startInfo.EnvironmentVariables.Keys.CopyTo (strs, 0);
+                               proc_info.envKeys = strs;
+
+                               strs = new string [startInfo.EnvironmentVariables.Count];
+                               startInfo.EnvironmentVariables.Values.CopyTo (strs, 0);
+                               proc_info.envValues = strs;
+                       }
+
                        if(startInfo.RedirectStandardInput==true) {
                                ret=MonoIO.CreatePipe(out stdin_rd,
                                                      out stdin_wr);
@@ -695,7 +752,13 @@ namespace System.Diagnostics {
                                if (startInfo.RedirectStandardError == true)
                                        MonoIO.Close (stderr_wr, out error);
 
-                               throw new Win32Exception ();
+                               throw new Win32Exception (-proc_info.pid);
+                       }
+
+                       if (process.enableRaisingEvents && process.Exited != null) {
+                               WaitOrTimerCallback cb = new WaitOrTimerCallback (CBOnExit);
+                               ProcessWaitHandle h = new ProcessWaitHandle (proc_info.process_handle);
+                               ThreadPool.RegisterWaitForSingleObject (h, cb, process, -1, true);
                        }
                        
                        process.process_handle=proc_info.process_handle;
@@ -767,8 +830,10 @@ namespace System.Diagnostics {
                }
 
                public bool WaitForExit(int milliseconds) {
-                       return(WaitForExit_internal(process_handle,
-                                                   milliseconds));
+                       if (milliseconds == int.MaxValue)
+                               milliseconds = -1;
+
+                       return WaitForExit_internal (process_handle, milliseconds);
                }
 
                [MonoTODO]
@@ -814,10 +879,37 @@ namespace System.Diagnostics {
                        base.Dispose (disposing);
                }
 
+               static void CBOnExit (object state, bool unused)
+               {
+                       Process p = (Process) state;
+                       p.OnExited ();
+               }
+
                protected void OnExited() 
                {
-                       if (Exited != null)
+                       if (Exited == null)
+                               return;
+
+                       if (synchronizingObject == null) {
                                Exited (this, EventArgs.Empty);
+                               return;
+                       }
+                       
+                       object [] args = new object [] {this, EventArgs.Empty};
+                       synchronizingObject.BeginInvoke (Exited, args);
+               }
+
+               class ProcessWaitHandle : WaitHandle
+               {
+                       public ProcessWaitHandle (IntPtr handle)
+                       {
+                               Handle = handle;
+                       }
+
+                       protected override void Dispose (bool explicitDisposing)
+                       {
+                               // Do nothing, we don't own the handle and we won't close it.
+                       }
                }
        }
 }