// 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;
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)]
{
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) {
}
}
- [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)]
[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;
}
}
[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)));
}
}
[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 */
[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));
}
}
- [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]
}
}
- [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]
throw new NotImplementedException();
}
- [MonoTODO]
- public void Kill() {
+ public void Kill ()
+ {
+ Close (1);
}
[MonoTODO]
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);
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;
}
public bool WaitForExit(int milliseconds) {
- return(WaitForExit_internal(process_handle,
- milliseconds));
+ if (milliseconds == int.MaxValue)
+ milliseconds = -1;
+
+ return WaitForExit_internal (process_handle, milliseconds);
}
[MonoTODO]
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.
+ }
}
}
}