X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem%2FSystem.Diagnostics%2FProcess.cs;h=07ed17f498eeea26807abc464fa7e9ef98c0c6ad;hb=c7166273ee53fb2f1751e6714fb60b66aa579e2d;hp=aaf9318a18fb9a568c37b750342016b7aab8f7f9;hpb=75bc0abfda491b5cde1d823224b85f10770a4533;p=mono.git diff --git a/mcs/class/System/System.Diagnostics/Process.cs b/mcs/class/System/System.Diagnostics/Process.cs index aaf9318a18f..07ed17f498e 100644 --- a/mcs/class/System/System.Diagnostics/Process.cs +++ b/mcs/class/System/System.Diagnostics/Process.cs @@ -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) // // @@ -32,18 +32,26 @@ // 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= 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] @@ -1064,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 {