for TARGET_J2EE only:
[mono.git] / mcs / class / System / System.Diagnostics / Process.cs
index 816f94fbe76ad50be47a9954cf9e15cfcbe863b6..07ed17f498eeea26807abc464fa7e9ef98c0c6ad 100644 (file)
@@ -8,7 +8,7 @@
 //
 // (C) 2002 Ximian, Inc.
 // (C) 2003 Andreas Nahr
-// (c) 2004 Novell, Inc. (http://www.novell.com)
+// (c) 2004,2005,2006 Novell, Inc. (http://www.novell.com)
 //
 
 //
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System;
 using System.IO;
+using System.Text;
 using System.ComponentModel;
 using System.ComponentModel.Design;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
+using System.Security.Permissions;
 using System.Collections;
+using System.Security;
 using System.Threading;
 
 namespace System.Diagnostics {
+
        [DefaultEvent ("Exited"), DefaultProperty ("StartInfo")]
        [Designer ("System.Diagnostics.Design.ProcessDesigner, " + Consts.AssemblySystem_Design)]
+       [PermissionSet (SecurityAction.LinkDemand, Unrestricted = true)]
+       [PermissionSet (SecurityAction.InheritanceDemand, Unrestricted = true)]
+#if NET_2_0
+       [MonitoringDescription ("Represents a system process")]
+#endif
        public class Process : Component 
        {
                [StructLayout(LayoutKind.Sequential)]
@@ -60,6 +68,10 @@ namespace System.Diagnostics {
                        public int tid;
                        public string [] envKeys;
                        public string [] envValues;
+                       public string UserName;
+                       public string Domain;
+                       public IntPtr Password;
+                       public bool LoadUserProfile;
                };
                
                IntPtr process_handle;
@@ -68,6 +80,8 @@ namespace System.Diagnostics {
                bool already_waiting;
                ISynchronizeInvoke synchronizingObject;
                EventHandler exited_event;
+               IntPtr stdout_rd;
+               IntPtr stderr_rd;
                
                /* Private constructor called from other methods */
                private Process(IntPtr handle, int id) {
@@ -90,6 +104,7 @@ namespace System.Diagnostics {
 
                void StartExitCallbackIfNeeded ()
                {
+#if !NET_2_1
                        bool start = (!already_waiting && enableRaisingEvents && exited_event != null);
                        if (start && process_handle != IntPtr.Zero && !HasExited) {
                                WaitOrTimerCallback cb = new WaitOrTimerCallback (CBOnExit);
@@ -97,6 +112,7 @@ namespace System.Diagnostics {
                                ThreadPool.RegisterWaitForSingleObject (h, cb, this, -1, true);
                                already_waiting = true;
                        }
+#endif
                }
 
                [DefaultValue (false), Browsable (false)]
@@ -193,9 +209,8 @@ namespace System.Diagnostics {
                [MonitoringDescription ("Process identifier.")]
                public int Id {
                        get {
-                               if (pid == 0) {
+                               if (pid == 0)
                                        throw new InvalidOperationException ("Process ID has not been set.");
-                               }
 
                                return(pid);
                        }
@@ -246,9 +261,10 @@ namespace System.Diagnostics {
                [MonitoringDescription ("The maximum working set for this process.")]
                public IntPtr MaxWorkingSet {
                        get {
-                               if(HasExited) {
-                                       throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
-                               }
+                               if(HasExited)
+                                       throw new InvalidOperationException(
+                                               "The process " + ProcessName +
+                                               " (ID " + Id + ") has exited");
                                
                                int min;
                                int max;
@@ -275,28 +291,27 @@ namespace System.Diagnostics {
                [MonitoringDescription ("The minimum working set for this process.")]
                public IntPtr MinWorkingSet {
                        get {
-                               if(HasExited) {
-                                       throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
-                               }
+                               if(HasExited)
+                                       throw new InvalidOperationException(
+                                               "The process " + ProcessName +
+                                               " (ID " + Id + ") has exited");
                                
                                int min;
                                int max;
-                               bool ok=GetWorkingSet_internal(process_handle, out min, out max);
-                               if(ok==false) {
+                               bool ok= GetWorkingSet_internal (process_handle, out min, out max);
+                               if(!ok)
                                        throw new Win32Exception();
-                               }
-                               
-                               return((IntPtr)min);
+                               return ((IntPtr) min);
                        }
                        set {
-                               if(HasExited) {
-                                       throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
-                               }
+                               if(HasExited)
+                                       throw new InvalidOperationException(
+                                               "The process " + ProcessName +
+                                               " (ID " + Id + ") has exited");
                                
-                               bool ok=SetWorkingSet_internal(process_handle, value.ToInt32(), 0, true);
-                               if(ok==false) {
+                               bool ok = SetWorkingSet_internal (process_handle, value.ToInt32(), 0, true);
+                               if (!ok)
                                        throw new Win32Exception();
-                               }
                        }
                }
 
@@ -304,7 +319,7 @@ namespace System.Diagnostics {
                 * element 0.
                 */
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               private extern ProcessModule[] GetModules_internal();
+               private extern ProcessModule[] GetModules_internal(IntPtr handle);
 
                private ProcessModuleCollection module_collection;
                
@@ -312,10 +327,9 @@ namespace System.Diagnostics {
                [MonitoringDescription ("The modules that are loaded as part of this process.")]
                public ProcessModuleCollection Modules {
                        get {
-                               if(module_collection==null) {
-                                       module_collection=new ProcessModuleCollection(GetModules_internal());
-                               }
-
+                               if (module_collection == null)
+                                       module_collection = new ProcessModuleCollection(
+                                               GetModules_internal (process_handle));
                                return(module_collection);
                        }
                }
@@ -396,6 +410,7 @@ namespace System.Diagnostics {
                [MonoTODO]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [MonitoringDescription ("The number of bytes that are not pageable.")]
+               [ComVisible (false)]
                public long NonpagedSystemMemorySize64 {
                        get {
                                return(0);
@@ -405,6 +420,7 @@ namespace System.Diagnostics {
                [MonoTODO]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [MonitoringDescription ("The number of bytes that are paged.")]
+               [ComVisible (false)]
                public long PagedMemorySize64 {
                        get {
                                return(0);
@@ -414,6 +430,7 @@ namespace System.Diagnostics {
                [MonoTODO]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [MonitoringDescription ("The amount of paged system memory in bytes.")]
+               [ComVisible (false)]
                public long PagedSystemMemorySize64 {
                        get {
                                return(0);
@@ -423,6 +440,7 @@ namespace System.Diagnostics {
                [MonoTODO]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [MonitoringDescription ("The maximum amount of paged memory used by this process.")]
+               [ComVisible (false)]
                public long PeakPagedMemorySize64 {
                        get {
                                return(0);
@@ -432,6 +450,7 @@ namespace System.Diagnostics {
                [MonoTODO]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [MonitoringDescription ("The maximum amount of virtual memory used by this process.")]
+               [ComVisible (false)]
                public long PeakVirtualMemorySize64 {
                        get {
                                return(0);
@@ -441,6 +460,7 @@ namespace System.Diagnostics {
                [MonoTODO]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [MonitoringDescription ("The maximum amount of system memory used by this process.")]
+               [ComVisible (false)]
                public long PeakWorkingSet64 {
                        get {
                                return(0);
@@ -459,32 +479,71 @@ namespace System.Diagnostics {
                        }
                }
 
-               [MonoTODO]
+               [MonoLimitation ("Under Unix, only root is allowed to raise the priority.")]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [MonitoringDescription ("The relative process priority.")]
                public ProcessPriorityClass PriorityClass {
                        get {
-                               return(ProcessPriorityClass.Normal);
+                               if (process_handle == IntPtr.Zero)
+                                       throw new InvalidOperationException ("Process has not been started.");
+                               
+                               int error;
+                               int prio = GetPriorityClass (process_handle, out error);
+                               if (prio == 0)
+                                       throw new Win32Exception (error);
+                               return (ProcessPriorityClass) prio;
                        }
                        set {
+                               if (!Enum.IsDefined (typeof (ProcessPriorityClass), value))
+                                       throw new InvalidEnumArgumentException (
+                                               "value", (int) value,
+                                               typeof (ProcessPriorityClass));
+
+                               if (process_handle == IntPtr.Zero)
+                                       throw new InvalidOperationException ("Process has not been started.");
+                               
+                               int error;
+                               if (!SetPriorityClass (process_handle, (int) value, out error))
+                                       throw new Win32Exception (error);
                        }
                }
 
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               static extern int GetPriorityClass (IntPtr handle, out int error);
+
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               static extern bool SetPriorityClass (IntPtr handle, int priority, out int error);
+
                [MonoTODO]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [MonitoringDescription ("The amount of memory exclusively used by this process.")]
+#if NET_2_0
+               [Obsolete ("Use PrivateMemorySize64")]
+#endif
                public int PrivateMemorySize {
                        get {
                                return(0);
                        }
                }
 
-               [MonoTODO]
+#if NET_2_0
+               [MonoNotSupported ("")]
+               [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+               [MonitoringDescription ("The session ID for this process.")]
+               public int SessionId {
+                       get { throw new NotImplementedException (); }
+               }
+#endif
+
+               /* the meaning of type is as follows: 0: user, 1: system, 2: total */
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               private extern static long Times (IntPtr handle, int type);
+
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [MonitoringDescription ("The amount of processing time spent in the OS core for this process.")]
                public TimeSpan PrivilegedProcessorTime {
                        get {
-                               return(new TimeSpan(0));
+                               return new TimeSpan (Times (process_handle, 1));
                        }
                }
 
@@ -503,9 +562,8 @@ namespace System.Diagnostics {
                                         * null, assume the process
                                         * has exited
                                         */
-                                       if(process_name==null) {
+                                       if (process_name == null)
                                                throw new SystemException("The process has exited");
-                                       }
                                        
                                        /* Strip the suffix (if it
                                         * exists) simplistically
@@ -550,9 +608,15 @@ namespace System.Diagnostics {
                [MonitoringDescription ("The standard error stream of this process.")]
                public StreamReader StandardError {
                        get {
-                               if (error_stream == null) {
+                               if (error_stream == null)
                                        throw new InvalidOperationException("Standard error has not been redirected");
-                               }
+
+#if NET_2_0
+                               if ((async_mode & AsyncModes.AsyncError) != 0)
+                                       throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
+
+                               async_mode |= AsyncModes.SyncError;
+#endif
 
                                return(error_stream);
                        }
@@ -564,9 +628,8 @@ namespace System.Diagnostics {
                [MonitoringDescription ("The standard input stream of this process.")]
                public StreamWriter StandardInput {
                        get {
-                               if (input_stream == null) {
+                               if (input_stream == null)
                                        throw new InvalidOperationException("Standard input has not been redirected");
-                               }
 
                                return(input_stream);
                        }
@@ -578,9 +641,15 @@ namespace System.Diagnostics {
                [MonitoringDescription ("The standard output stream of this process.")]
                public StreamReader StandardOutput {
                        get {
-                               if (output_stream == null) {
+                               if (output_stream == null)
                                        throw new InvalidOperationException("Standard output has not been redirected");
-                               }
+
+#if NET_2_0
+                               if ((async_mode & AsyncModes.AsyncOutput) != 0)
+                                       throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
+
+                               async_mode |= AsyncModes.SyncOutput;
+#endif
 
                                return(output_stream);
                        }
@@ -592,18 +661,14 @@ namespace System.Diagnostics {
                [MonitoringDescription ("Information for the start of this process.")]
                public ProcessStartInfo StartInfo {
                        get {
-                               if(start_info==null) {
-                                       start_info=new ProcessStartInfo();
-                               }
-                               
-                               return(start_info);
+                               if (start_info == null)
+                                       start_info = new ProcessStartInfo();
+                               return start_info;
                        }
                        set {
-                               if(value==null) {
-                                       throw new ArgumentException("value is null");
-                               }
-                               
-                               start_info=value;
+                               if (value == null)
+                                       throw new ArgumentNullException("value");
+                               start_info = value;
                        }
                }
 
@@ -633,25 +698,23 @@ namespace System.Diagnostics {
                [MonitoringDescription ("The number of threads of this process.")]
                public ProcessThreadCollection Threads {
                        get {
-                               return(null);
+                               return ProcessThreadCollection.GetEmpty ();
                        }
                }
 
-               [MonoTODO]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [MonitoringDescription ("The total CPU time spent for this process.")]
                public TimeSpan TotalProcessorTime {
                        get {
-                               return(new TimeSpan(0));
+                               return new TimeSpan (Times (process_handle, 2));
                        }
                }
 
-               [MonoTODO]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [MonitoringDescription ("The CPU time spent for this process in user mode.")]
                public TimeSpan UserProcessorTime {
                        get {
-                               return(new TimeSpan(0));
+                               return new TimeSpan (Times (process_handle, 0));
                        }
                }
 
@@ -680,9 +743,20 @@ namespace System.Diagnostics {
                }
 
 #if NET_2_0
+               [MonoTODO]
+               [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+               [MonitoringDescription ("The amount of memory exclusively used by this process.")]
+               [ComVisible (false)]
+               public long PrivateMemorySize64 {
+                       get {
+                               return(0);
+                       }
+               }
+
                [MonoTODO]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [MonitoringDescription ("The amount of virtual memory currently used for this process.")]
+               [ComVisible (false)]
                public long VirtualMemorySize64 {
                        get {
                                return(0);
@@ -692,6 +766,7 @@ namespace System.Diagnostics {
                [MonoTODO]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [MonitoringDescription ("The amount of physical memory currently used for this process.")]
+               [ComVisible (false)]
                public long WorkingSet64 {
                        get {
                                return(0);
@@ -735,42 +810,49 @@ namespace System.Diagnostics {
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                private extern static int GetPid_internal();
 
-               public static Process GetCurrentProcess() {
-                       int pid=GetPid_internal();
-                       IntPtr proc=GetProcess_internal(pid);
+               public static Process GetCurrentProcess()
+               {
+                       int pid = GetPid_internal();
+                       IntPtr proc = GetProcess_internal(pid);
                        
-                       if(proc==IntPtr.Zero) {
+                       if (proc == IntPtr.Zero)
                                throw new SystemException("Can't find current process");
-                       }
 
-                       return(new Process(proc, pid));
+                       return (new Process (proc, pid));
                }
 
-               public static Process GetProcessById(int processId) {
-                       IntPtr proc=GetProcess_internal(processId);
+               public static Process GetProcessById(int processId)
+               {
+                       IntPtr proc = GetProcess_internal(processId);
                        
-                       if(proc==IntPtr.Zero) {
-                               throw new ArgumentException("Can't find process with ID " + processId.ToString());
-                       }
+                       if (proc == IntPtr.Zero)
+                               throw new ArgumentException ("Can't find process with ID " + processId.ToString ());
 
-                       return(new Process(proc, processId));
+                       return (new Process (proc, processId));
                }
 
-               [MonoTODO]
+               [MonoTODO ("There is no support for retrieving process information from a remote machine")]
                public static Process GetProcessById(int processId, string machineName) {
-                       throw new NotImplementedException();
+                       if (machineName == null)
+                               throw new ArgumentNullException ("machineName");
+
+                       if (!IsLocalMachine (machineName))
+                               throw new NotImplementedException ();
+
+                       return GetProcessById (processId);
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                private extern static int[] GetProcesses_internal();
 
-               public static Process[] GetProcesses() {
-                       int[] pids=GetProcesses_internal();
-                       ArrayList proclist=new ArrayList();
+               public static Process[] GetProcesses()
+               {
+                       int [] pids = GetProcesses_internal ();
+                       ArrayList proclist = new ArrayList ();
                        
-                       for(int i=0; i<pids.Length; i++) {
+                       for (int i = 0; i < pids.Length; i++) {
                                try {
-                                       proclist.Add(GetProcessById(pids[i]));
+                                       proclist.Add (GetProcessById (pids [i]));
                                } catch (SystemException) {
                                        /* The process might exit
                                         * between
@@ -780,28 +862,35 @@ namespace System.Diagnostics {
                                }
                        }
 
-                       return((Process[])proclist.ToArray(typeof(Process)));
+                       return ((Process []) proclist.ToArray (typeof (Process)));
                }
 
-               [MonoTODO]
+               [MonoTODO ("There is no support for retrieving process information from a remote machine")]
                public static Process[] GetProcesses(string machineName) {
-                       throw new NotImplementedException();
+                       if (machineName == null)
+                               throw new ArgumentNullException ("machineName");
+
+                       if (!IsLocalMachine (machineName))
+                               throw new NotImplementedException ();
+
+                       return GetProcesses ();
                }
 
-               public static Process[] GetProcessesByName(string processName) {
-                       Process[] procs=GetProcesses();
-                       ArrayList proclist=new ArrayList();
+               public static Process[] GetProcessesByName(string processName)
+               {
+                       Process [] procs = GetProcesses();
+                       ArrayList proclist = new ArrayList();
                        
-                       for(int i=0; i<procs.Length; i++) {
+                       for (int i = 0; i < procs.Length; i++) {
                                /* Ignore case */
-                               if(String.Compare(processName,
-                                                 procs[i].ProcessName,
-                                                 true)==0) {
-                                       proclist.Add(procs[i]);
+                               if (String.Compare (processName,
+                                                   procs [i].ProcessName,
+                                                   true) == 0) {
+                                       proclist.Add (procs [i]);
                                }
                        }
 
-                       return((Process[])proclist.ToArray(typeof(Process)));
+                       return ((Process[]) proclist.ToArray (typeof(Process)));
                }
 
                [MonoTODO]
@@ -818,8 +907,10 @@ namespace System.Diagnostics {
                public static void LeaveDebugMode() {
                }
 
-               [MonoTODO]
-               public void Refresh() {
+               public void Refresh ()
+               {
+                       // FIXME: should refresh any cached data we might have about
+                       // the process (currently we have none).
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
@@ -845,12 +936,18 @@ namespace System.Diagnostics {
                                throw new InvalidOperationException ("UseShellExecute must be false when redirecting I/O.");
                        }
 
-                       if (startInfo.HaveEnvVars) {
+                       if (startInfo.HaveEnvVars)
                                throw new InvalidOperationException ("UseShellExecute must be false in order to use environment variables.");
-                       }
 
-                       ret = ShellExecuteEx_internal (startInfo,
-                                                      ref proc_info);
+                       FillUserInfo (startInfo, ref proc_info);
+                       try {
+                               ret = ShellExecuteEx_internal (startInfo,
+                                                              ref proc_info);
+                       } finally {
+                               if (proc_info.Password != IntPtr.Zero)
+                                       Marshal.FreeBSTR (proc_info.Password);
+                               proc_info.Password = IntPtr.Zero;
+                       }
                        if (!ret) {
                                throw new Win32Exception (-proc_info.pid);
                        }
@@ -868,8 +965,8 @@ namespace System.Diagnostics {
                {
                        ProcInfo proc_info=new ProcInfo();
                        IntPtr stdin_rd, stdin_wr;
-                       IntPtr stdout_rd, stdout_wr;
-                       IntPtr stderr_rd, stderr_wr;
+                       IntPtr stdout_wr;
+                       IntPtr stderr_wr;
                        bool ret;
                        MonoIOError error;
 
@@ -892,15 +989,18 @@ namespace System.Diagnostics {
                        } else {
                                stdin_rd = MonoIO.ConsoleInput;
                                /* This is required to stop the
-                                * &$*£ing stupid compiler moaning
+                                * &$*£ing stupid compiler moaning
                                 * that stdin_wr is unassigned, below.
                                 */
                                stdin_wr = (IntPtr)0;
                        }
 
                        if (startInfo.RedirectStandardOutput == true) {
-                               ret = MonoIO.CreatePipe (out stdout_rd,
+                               IntPtr out_rd;
+                               ret = MonoIO.CreatePipe (out out_rd,
                                                         out stdout_wr);
+
+                               process.stdout_rd = out_rd;
                                if (ret == false) {
                                        if (startInfo.RedirectStandardInput == true) {
                                                MonoIO.Close (stdin_rd, out error);
@@ -910,33 +1010,43 @@ namespace System.Diagnostics {
                                        throw new IOException ("Error creating standard output pipe");
                                }
                        } else {
-                               stdout_rd = (IntPtr)0;
+                               process.stdout_rd = (IntPtr)0;
                                stdout_wr = MonoIO.ConsoleOutput;
                        }
 
                        if (startInfo.RedirectStandardError == true) {
-                               ret = MonoIO.CreatePipe (out stderr_rd,
+                               IntPtr err_rd;
+                               ret = MonoIO.CreatePipe (out err_rd,
                                                         out stderr_wr);
+
+                               process.stderr_rd = err_rd;
                                if (ret == false) {
                                        if (startInfo.RedirectStandardInput == true) {
                                                MonoIO.Close (stdin_rd, out error);
                                                MonoIO.Close (stdin_wr, out error);
                                        }
                                        if (startInfo.RedirectStandardOutput == true) {
-                                               MonoIO.Close (stdout_rd, out error);
+                                               MonoIO.Close (process.stdout_rd, out error);
                                                MonoIO.Close (stdout_wr, out error);
                                        }
                                        
                                        throw new IOException ("Error creating standard error pipe");
                                }
                        } else {
-                               stderr_rd = (IntPtr)0;
+                               process.stderr_rd = (IntPtr)0;
                                stderr_wr = MonoIO.ConsoleError;
                        }
-                       
-                       ret = CreateProcess_internal (startInfo,
-                                                     stdin_rd, stdout_wr, stderr_wr,
-                                                     ref proc_info);
+
+                       FillUserInfo (startInfo, ref proc_info);
+                       try {
+                               ret = CreateProcess_internal (startInfo,
+                                                             stdin_rd, stdout_wr, stderr_wr,
+                                                             ref proc_info);
+                       } finally {
+                               if (proc_info.Password != IntPtr.Zero)
+                                       Marshal.FreeBSTR (proc_info.Password);
+                               proc_info.Password = IntPtr.Zero;
+                       }
                        if (!ret) {
                                if (startInfo.RedirectStandardInput == true) {
                                        MonoIO.Close (stdin_rd, out error);
@@ -944,16 +1054,19 @@ namespace System.Diagnostics {
                                }
 
                                if (startInfo.RedirectStandardOutput == true) {
-                                       MonoIO.Close (stdout_rd, out error);
+                                       MonoIO.Close (process.stdout_rd, out error);
                                        MonoIO.Close (stdout_wr, out error);
                                }
 
                                if (startInfo.RedirectStandardError == true) {
-                                       MonoIO.Close (stderr_rd, out error);
+                                       MonoIO.Close (process.stderr_rd, out error);
                                        MonoIO.Close (stderr_wr, out error);
                                }
 
-                               throw new Win32Exception (-proc_info.pid);
+                               throw new Win32Exception (-proc_info.pid,
+                                       "ApplicationName='" + startInfo.FileName +
+                                       "', CommandLine='" + startInfo.Arguments +
+                                       "', CurrentDirectory='" + startInfo.WorkingDirectory + "'");
                        }
 
                        process.process_handle = proc_info.process_handle;
@@ -961,18 +1074,26 @@ namespace System.Diagnostics {
                        
                        if (startInfo.RedirectStandardInput == true) {
                                MonoIO.Close (stdin_rd, out error);
-                               process.input_stream = new StreamWriter (new FileStream (stdin_wr, FileAccess.Write, true));
+                               process.input_stream = new StreamWriter (new MonoSyncFileStream (stdin_wr, FileAccess.Write, true, 8192), Console.Out.Encoding);
                                process.input_stream.AutoFlush = true;
                        }
 
+#if NET_2_0
+                       Encoding stdoutEncoding = startInfo.StandardOutputEncoding ?? Console.Out.Encoding;
+                       Encoding stderrEncoding = startInfo.StandardErrorEncoding ?? Console.Out.Encoding;
+#else
+                       Encoding stdoutEncoding = Console.Out.Encoding;
+                       Encoding stderrEncoding = stdoutEncoding;
+#endif
+
                        if (startInfo.RedirectStandardOutput == true) {
                                MonoIO.Close (stdout_wr, out error);
-                               process.output_stream = new StreamReader (new FileStream (stdout_rd, FileAccess.Read, true));
+                               process.output_stream = new StreamReader (new MonoSyncFileStream (process.stdout_rd, FileAccess.Read, true, 8192), stdoutEncoding);
                        }
 
                        if (startInfo.RedirectStandardError == true) {
                                MonoIO.Close (stderr_wr, out error);
-                               process.error_stream = new StreamReader (new FileStream (stderr_rd, FileAccess.Read, true));
+                               process.error_stream = new StreamReader (new MonoSyncFileStream (process.stderr_rd, FileAccess.Read, true, 8192), stderrEncoding);
                        }
 
                        process.StartExitCallbackIfNeeded ();
@@ -980,72 +1101,140 @@ namespace System.Diagnostics {
                        return(ret);
                }
 
+               // Note that ProcInfo.Password must be freed.
+               private static void FillUserInfo (ProcessStartInfo startInfo, ref ProcInfo proc_info)
+               {
+#if NET_2_0
+                       if (startInfo.UserName != null) {
+                               proc_info.UserName = startInfo.UserName;
+                               proc_info.Domain = startInfo.Domain;
+                               if (startInfo.Password != null)
+                                       proc_info.Password = Marshal.SecureStringToBSTR (startInfo.Password);
+                               else
+                                       proc_info.Password = IntPtr.Zero;
+                               proc_info.LoadUserProfile = startInfo.LoadUserProfile;
+                       }
+#endif
+               }
+
                private static bool Start_common (ProcessStartInfo startInfo,
                                                  Process process)
                {
-                       if(startInfo.FileName == null ||
-                          startInfo.FileName == "") {
+                       if (startInfo.FileName == null || startInfo.FileName.Length == 0)
                                throw new InvalidOperationException("File name has not been set");
-                       }
+                       
+#if NET_2_0
+                       if (startInfo.StandardErrorEncoding != null && !startInfo.RedirectStandardError)
+                               throw new InvalidOperationException ("StandardErrorEncoding is only supported when standard error is redirected");
+                       if (startInfo.StandardOutputEncoding != null && !startInfo.RedirectStandardOutput)
+                               throw new InvalidOperationException ("StandardOutputEncoding is only supported when standard output is redirected");
+#endif
                        
                        if (startInfo.UseShellExecute) {
+#if NET_2_0
+                               if (startInfo.UserName != null)
+                                       throw new InvalidOperationException ("UserShellExecute must be false if an explicit UserName is specified when starting a process");
+#endif
                                return (Start_shell (startInfo, process));
                        } else {
                                return (Start_noshell (startInfo, process));
                        }
                }
                
-               public bool Start() {
-                       bool ret;
-                       
-                       ret=Start_common(start_info, this);
-                       
-                       return(ret);
+               public bool Start ()
+               {
+                       if (process_handle != IntPtr.Zero) {
+                               Process_free_internal (process_handle);
+                               process_handle = IntPtr.Zero;
+                       }
+                       return Start_common(start_info, this);
                }
 
-               public static Process Start(ProcessStartInfo startInfo) {
+               public static Process Start (ProcessStartInfo startInfo)
+               {
+                       if (startInfo == null)
+                               throw new ArgumentNullException ("startInfo");
+
                        Process process=new Process();
-                       bool ret;
+                       process.StartInfo = startInfo;
+                       if (Start_common(startInfo, process))
+                               return process;
+                       return null;
+               }
 
-                       ret=Start_common(startInfo, process);
-                       
-                       if(ret==true) {
-                               return(process);
-                       } else {
-                               return(null);
-                       }
+               public static Process Start (string fileName)
+               {
+                       return Start (new ProcessStartInfo (fileName));
                }
 
-               public static Process Start(string fileName) {
-                       return Start(new ProcessStartInfo(fileName));
+               public static Process Start(string fileName, string arguments)
+               {
+                       return Start (new ProcessStartInfo (fileName, arguments));
                }
 
-               public static Process Start(string fileName,
-                                           string arguments) {
-                       return Start(new ProcessStartInfo(fileName, arguments));
+#if NET_2_0
+               public static Process Start(string fileName, string username, SecureString password, string domain) {
+                       return Start(fileName, null, username, password, domain);
+               }
+
+               public static Process Start(string fileName, string arguments, string username, SecureString password, string domain) {
+                       ProcessStartInfo psi = new ProcessStartInfo(fileName, arguments);
+                       psi.UserName = username;
+                       psi.Password = password;
+                       psi.Domain = domain;
+                       psi.UseShellExecute = false;
+                       return Start(psi);
                }
+#endif
 
-               public override string ToString() {
-                       return(base.ToString() +
-                              " (" + this.ProcessName + ")");
+               public override string ToString()
+               {
+                       return(base.ToString() + " (" + this.ProcessName + ")");
                }
 
                /* Waits up to ms milliseconds for process 'handle' to
                 * exit.  ms can be <0 to mean wait forever.
                 */
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               private extern bool WaitForExit_internal(IntPtr handle,
-                                                        int ms);
+               private extern bool WaitForExit_internal(IntPtr handle, int ms);
 
-               public void WaitForExit() {
-                       WaitForExit_internal(process_handle, -1);
+               public void WaitForExit ()
+               {
+                       WaitForExit (-1);
                }
 
                public bool WaitForExit(int milliseconds) {
-                       if (milliseconds == int.MaxValue)
-                               milliseconds = -1;
+                       int ms = milliseconds;
+                       if (ms == int.MaxValue)
+                               ms = -1;
 
-                       return WaitForExit_internal (process_handle, milliseconds);
+#if NET_2_0
+                       DateTime start = DateTime.UtcNow;
+                       if (async_output != null && !async_output.IsCompleted) {
+                               if (false == async_output.WaitHandle.WaitOne (ms, false))
+                                       return false; // Timed out
+
+                               if (ms >= 0) {
+                                       DateTime now = DateTime.UtcNow;
+                                       ms -= (int) (now - start).TotalMilliseconds;
+                                       if (ms <= 0)
+                                               return false;
+                                       start = now;
+                               }
+                       }
+
+                       if (async_error != null && !async_error.IsCompleted) {
+                               if (false == async_error.WaitHandle.WaitOne (ms, false))
+                                       return false; // Timed out
+
+                               if (ms >= 0) {
+                                       ms -= (int) (DateTime.UtcNow - start).TotalMilliseconds;
+                                       if (ms <= 0)
+                                               return false;
+                               }
+                       }
+#endif
+                       return WaitForExit_internal (process_handle, ms);
                }
 
                [MonoTODO]
@@ -1058,6 +1247,237 @@ namespace System.Diagnostics {
                        return(false);
                }
 
+               private static bool IsLocalMachine (string machineName)
+               {
+                       if (machineName == "." || machineName.Length == 0)
+                               return true;
+
+                       return (string.Compare (machineName, Environment.MachineName, true) == 0);
+               }
+
+#if NET_2_0
+               [Browsable (true)]
+               [MonitoringDescription ("Raised when it receives output data")]
+               public event DataReceivedEventHandler OutputDataReceived;
+               [Browsable (true)]
+               [MonitoringDescription ("Raised when it receives error data")]
+               public event DataReceivedEventHandler ErrorDataReceived;
+
+               void OnOutputDataReceived (string str)
+               {
+                       if (OutputDataReceived != null)
+                               OutputDataReceived (this, new DataReceivedEventArgs (str));
+               }
+
+               void OnErrorDataReceived (string str)
+               {
+                       if (ErrorDataReceived != null)
+                               ErrorDataReceived (this, new DataReceivedEventArgs (str));
+               }
+
+               [Flags]
+               enum AsyncModes {
+                       NoneYet = 0,
+                       SyncOutput = 1,
+                       SyncError = 1 << 1,
+                       AsyncOutput = 1 << 2,
+                       AsyncError = 1 << 3
+               }
+
+               [StructLayout (LayoutKind.Sequential)]
+               sealed class ProcessAsyncReader
+               {
+                       /*
+                          The following fields match those of SocketAsyncResult.
+                          This is so that changes needed in the runtime to handle
+                          asynchronous reads are trivial
+                       */
+                       /* DON'T shuffle fields around. DON'T remove fields */
+                       public object Sock;
+                       public IntPtr handle;
+                       public object state;
+                       public AsyncCallback callback;
+                       public ManualResetEvent wait_handle;
+
+                       public Exception delayedException;
+
+                       public object EndPoint;
+                       byte [] buffer = new byte [4196];
+                       public int Offset;
+                       public int Size;
+                       public int SockFlags;
+
+                       public object acc_socket;
+                       public int total;
+                       public bool completed_sync;
+                       bool completed;
+                       bool err_out; // true -> stdout, false -> stderr
+                       internal int error;
+                       public int operation = 8; // MAGIC NUMBER: see Socket.cs:AsyncOperation
+                       public object ares;
+
+
+                       // These fields are not in SocketAsyncResult
+                       Process process;
+                       Stream stream;
+                       StringBuilder sb = new StringBuilder ();
+                       public AsyncReadHandler ReadHandler;
+
+                       public ProcessAsyncReader (Process process, IntPtr handle, bool err_out)
+                       {
+                               this.process = process;
+                               this.handle = handle;
+                               stream = new FileStream (handle, FileAccess.Read, false);
+                               this.ReadHandler = new AsyncReadHandler (AddInput);
+                               this.err_out = err_out;
+                       }
+
+                       public void AddInput ()
+                       {
+                               lock (this) {
+                                       int nread = stream.Read (buffer, 0, buffer.Length);
+                                       if (nread == 0) {
+                                               completed = true;
+                                               if (wait_handle != null)
+                                                       wait_handle.Set ();
+                                               Flush (true);
+                                               return;
+                                       }
+
+                                       try {
+                                               sb.Append (Encoding.Default.GetString (buffer, 0, nread));
+                                       } catch {
+                                               // Just in case the encoding fails...
+                                               for (int i = 0; i < nread; i++) {
+                                                       sb.Append ((char) buffer [i]);
+                                               }
+                                       }
+
+                                       Flush (false);
+                                       ReadHandler.BeginInvoke (null, this);
+                               }
+                       }
+
+                       void Flush (bool last)
+                       {
+                               if (sb.Length == 0 ||
+                                   (err_out && process.output_canceled) ||
+                                   (!err_out && process.error_canceled))
+                                       return;
+
+                               string total = sb.ToString ();
+                               sb.Length = 0;
+                               string [] strs = total.Split ('\n');
+                               int len = strs.Length;
+                               if (len == 0)
+                                       return;
+
+                               for (int i = 0; i < len - 1; i++) {
+                                       if (err_out)
+                                               process.OnOutputDataReceived (strs [i]);
+                                       else
+                                               process.OnErrorDataReceived (strs [i]);
+                               }
+
+                               string end = strs [len - 1];
+                               if (last || (len == 1 && end == "")) {
+                                       if (err_out) {
+                                               process.OnOutputDataReceived (end);
+                                       } else {
+                                               process.OnErrorDataReceived (end);
+                                       }
+                               } else {
+                                       sb.Append (end);
+                               }
+                       }
+
+                       public bool IsCompleted {
+                               get { return completed; }
+                       }
+
+                       public WaitHandle WaitHandle {
+                               get {
+                                       lock (this) {
+                                               if (wait_handle == null)
+                                                       wait_handle = new ManualResetEvent (completed);
+                                               return wait_handle;
+                                       }
+                               }
+                       }
+               }
+
+               AsyncModes async_mode;
+               bool output_canceled;
+               bool error_canceled;
+               ProcessAsyncReader async_output;
+               ProcessAsyncReader async_error;
+               delegate void AsyncReadHandler ();
+
+               [ComVisibleAttribute(false)] 
+               public void BeginOutputReadLine ()
+               {
+                       if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
+                               throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
+
+                       if ((async_mode & AsyncModes.SyncOutput) != 0)
+                               throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
+
+                       async_mode |= AsyncModes.AsyncOutput;
+                       output_canceled = false;
+                       if (async_output == null) {
+                               async_output = new ProcessAsyncReader (this, stdout_rd, true);
+                               async_output.ReadHandler.BeginInvoke (null, async_output);
+                       }
+               }
+
+               [ComVisibleAttribute(false)] 
+               public void CancelOutputRead ()
+               {
+                       if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
+                               throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
+
+                       if ((async_mode & AsyncModes.SyncOutput) != 0)
+                               throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
+
+                       if (async_output == null)
+                               throw new InvalidOperationException ("No async operation in progress.");
+
+                       output_canceled = true;
+               }
+
+               [ComVisibleAttribute(false)] 
+               public void BeginErrorReadLine ()
+               {
+                       if (process_handle == IntPtr.Zero || error_stream == null || StartInfo.RedirectStandardError == false)
+                               throw new InvalidOperationException ("Standard error has not been redirected or process has not been started.");
+
+                       if ((async_mode & AsyncModes.SyncError) != 0)
+                               throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
+
+                       async_mode |= AsyncModes.AsyncError;
+                       error_canceled = false;
+                       if (async_error == null) {
+                               async_error = new ProcessAsyncReader (this, stderr_rd, false);
+                               async_error.ReadHandler.BeginInvoke (null, async_error);
+                       }
+               }
+
+               [ComVisibleAttribute(false)] 
+               public void CancelErrorRead ()
+               {
+                       if (process_handle == IntPtr.Zero || output_stream == null || StartInfo.RedirectStandardOutput == false)
+                               throw new InvalidOperationException ("Standard output has not been redirected or process has not been started.");
+
+                       if ((async_mode & AsyncModes.SyncOutput) != 0)
+                               throw new InvalidOperationException ("OutputStream is not enabled for asynchronous read operations.");
+
+                       if (async_error == null)
+                               throw new InvalidOperationException ("No async operation in progress.");
+
+                       error_canceled = true;
+               }
+#endif
+
                [Category ("Behavior")]
                [MonitoringDescription ("Raised when this process exits.")]
                public event EventHandler Exited {
@@ -1119,6 +1539,11 @@ namespace System.Diagnostics {
                        base.Dispose (disposing);
                }
 
+               ~Process ()
+               {
+                       Dispose (false);
+               }
+
                static void CBOnExit (object state, bool unused)
                {
                        Process p = (Process) state;