2014-08-13 10:39:27 +01:00
|
|
|
//
|
|
|
|
// System.Diagnostics.Process.cs
|
|
|
|
//
|
|
|
|
// Authors:
|
|
|
|
// 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,2005,2006 Novell, Inc. (http://www.novell.com)
|
|
|
|
//
|
|
|
|
|
|
|
|
//
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
// a copy of this software and associated documentation files (the
|
|
|
|
// "Software"), to deal in the Software without restriction, including
|
|
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
// the following conditions:
|
|
|
|
//
|
|
|
|
// The above copyright notice and this permission notice shall be
|
|
|
|
// included in all copies or substantial portions of the Software.
|
|
|
|
//
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
//
|
|
|
|
|
|
|
|
using System.IO;
|
|
|
|
using System.Text;
|
|
|
|
using System.ComponentModel;
|
|
|
|
using System.ComponentModel.Design;
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
using System.Runtime.InteropServices;
|
2015-08-26 07:17:56 -04:00
|
|
|
using System.Runtime.Remoting.Messaging;
|
2014-08-13 10:39:27 +01:00
|
|
|
using System.Security.Permissions;
|
2015-01-13 10:44:36 +00:00
|
|
|
using System.Collections.Generic;
|
2014-08-13 10:39:27 +01:00
|
|
|
using System.Security;
|
|
|
|
using System.Threading;
|
2016-02-22 11:00:01 -05:00
|
|
|
using Microsoft.Win32.SafeHandles;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
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)]
|
|
|
|
[MonitoringDescription ("Represents a system process")]
|
|
|
|
public class Process : Component
|
|
|
|
{
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
|
|
private struct ProcInfo
|
|
|
|
{
|
|
|
|
public IntPtr process_handle;
|
|
|
|
/* If thread_handle is ever needed for
|
|
|
|
* something, take out the CloseHandle() in
|
|
|
|
* the Start_internal icall in
|
|
|
|
* mono/metadata/process.c
|
|
|
|
*/
|
|
|
|
public IntPtr thread_handle;
|
|
|
|
public int pid; // Contains -GetLastError () on failure.
|
|
|
|
public int tid;
|
|
|
|
public string [] envKeys;
|
|
|
|
public string [] envValues;
|
|
|
|
public string UserName;
|
|
|
|
public string Domain;
|
|
|
|
public IntPtr Password;
|
|
|
|
public bool LoadUserProfile;
|
|
|
|
};
|
2016-02-22 11:00:01 -05:00
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
IntPtr process_handle;
|
|
|
|
int pid;
|
2016-02-22 11:00:01 -05:00
|
|
|
int enable_raising_events;
|
|
|
|
Thread background_wait_for_exit_thread;
|
2014-08-13 10:39:27 +01:00
|
|
|
ISynchronizeInvoke synchronizingObject;
|
|
|
|
EventHandler exited_event;
|
2015-08-26 07:17:56 -04:00
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
/* Private constructor called from other methods */
|
|
|
|
private Process(IntPtr handle, int id) {
|
2016-02-22 11:00:01 -05:00
|
|
|
process_handle = handle;
|
2014-08-13 10:39:27 +01:00
|
|
|
pid=id;
|
|
|
|
}
|
2016-02-22 11:00:01 -05:00
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
public Process ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
[MonoTODO]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("Base process priority.")]
|
|
|
|
public int BasePriority {
|
2016-02-22 11:00:01 -05:00
|
|
|
get { return 0; }
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
[DefaultValue (false), Browsable (false)]
|
|
|
|
[MonitoringDescription ("Check for exiting of the process to raise the apropriate event.")]
|
|
|
|
public bool EnableRaisingEvents {
|
|
|
|
get {
|
2016-02-22 11:00:01 -05:00
|
|
|
return enable_raising_events == 1;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
2016-02-22 11:00:01 -05:00
|
|
|
set {
|
|
|
|
if (value && Interlocked.Exchange (ref enable_raising_events, 1) == 0)
|
|
|
|
StartBackgroundWaitForExit ();
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
|
|
private extern static int ExitCode_internal(IntPtr handle);
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
|
|
|
|
[MonitoringDescription ("The exit code of the process.")]
|
|
|
|
public int ExitCode {
|
|
|
|
get {
|
|
|
|
if (process_handle == IntPtr.Zero)
|
|
|
|
throw new InvalidOperationException ("Process has not been started.");
|
|
|
|
|
|
|
|
int code = ExitCode_internal (process_handle);
|
|
|
|
if (code == 259)
|
2016-02-22 11:00:01 -05:00
|
|
|
throw new InvalidOperationException ("The process must exit before getting the requested information.");
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns the process start time in Windows file
|
|
|
|
* times (ticks from DateTime(1/1/1601 00:00 GMT))
|
|
|
|
*/
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
|
|
private extern static long ExitTime_internal(IntPtr handle);
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
|
|
|
|
[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)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
|
|
|
|
[MonitoringDescription ("Handle for this process.")]
|
|
|
|
public IntPtr Handle {
|
|
|
|
get {
|
|
|
|
if (process_handle == IntPtr.Zero)
|
|
|
|
throw new InvalidOperationException ("No process is associated with this object.");
|
|
|
|
return(process_handle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[MonoTODO]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("Handles for this process.")]
|
|
|
|
public int HandleCount {
|
|
|
|
get {
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
|
|
|
|
[MonitoringDescription ("Determines if the process is still running.")]
|
|
|
|
public bool HasExited {
|
|
|
|
get {
|
|
|
|
if (process_handle == IntPtr.Zero)
|
|
|
|
throw new InvalidOperationException ("Process has not been started.");
|
|
|
|
|
|
|
|
int exitcode = ExitCode_internal (process_handle);
|
|
|
|
|
|
|
|
if(exitcode==259) {
|
|
|
|
/* STILL_ACTIVE */
|
|
|
|
return(false);
|
|
|
|
} else {
|
|
|
|
return(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("Process identifier.")]
|
|
|
|
public int Id {
|
|
|
|
get {
|
|
|
|
if (pid == 0)
|
|
|
|
throw new InvalidOperationException ("Process ID has not been set.");
|
|
|
|
|
|
|
|
return(pid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[MonoTODO]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
|
|
|
|
[MonitoringDescription ("The name of the computer running the process.")]
|
|
|
|
public string MachineName {
|
|
|
|
get {
|
|
|
|
return("localhost");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
|
|
|
|
[MonitoringDescription ("The main module of the process.")]
|
|
|
|
public ProcessModule MainModule {
|
|
|
|
get {
|
|
|
|
return(this.Modules[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[MonoTODO]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The handle of the main window of the process.")]
|
|
|
|
public IntPtr MainWindowHandle {
|
|
|
|
get {
|
|
|
|
return((IntPtr)0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[MonoTODO]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The title of the main window of the process.")]
|
|
|
|
public string MainWindowTitle {
|
|
|
|
get {
|
|
|
|
return("null");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
|
|
private extern static bool GetWorkingSet_internal(IntPtr handle, out int min, out int max);
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
|
|
private extern static bool SetWorkingSet_internal(IntPtr handle, int min, int max, bool use_min);
|
|
|
|
|
|
|
|
/* LAMESPEC: why is this an IntPtr not a plain int? */
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The maximum working set for this process.")]
|
|
|
|
public IntPtr MaxWorkingSet {
|
|
|
|
get {
|
|
|
|
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) {
|
|
|
|
throw new Win32Exception();
|
|
|
|
}
|
|
|
|
|
|
|
|
return((IntPtr)max);
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
if(HasExited) {
|
|
|
|
throw new InvalidOperationException("The process " + ProcessName + " (ID " + Id + ") has exited");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ok=SetWorkingSet_internal(process_handle, 0, value.ToInt32(), false);
|
|
|
|
if(ok==false) {
|
|
|
|
throw new Win32Exception();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The minimum working set for this process.")]
|
|
|
|
public IntPtr MinWorkingSet {
|
|
|
|
get {
|
|
|
|
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)
|
|
|
|
throw new Win32Exception();
|
|
|
|
return ((IntPtr) min);
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
if(HasExited)
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
"The process " + ProcessName +
|
|
|
|
" (ID " + Id + ") has exited");
|
|
|
|
|
|
|
|
bool ok = SetWorkingSet_internal (process_handle, value.ToInt32(), 0, true);
|
|
|
|
if (!ok)
|
|
|
|
throw new Win32Exception();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns the list of process modules. The main module is
|
|
|
|
* element 0.
|
|
|
|
*/
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
|
|
private extern ProcessModule[] GetModules_internal(IntPtr handle);
|
|
|
|
|
|
|
|
private ProcessModuleCollection module_collection;
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
|
|
|
|
[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 (process_handle));
|
|
|
|
return(module_collection);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* data type is from the MonoProcessData enum in mono-proclib.h in the runtime */
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
|
|
private extern static long GetProcessData (int pid, int data_type, out int error);
|
|
|
|
|
|
|
|
[MonoTODO]
|
|
|
|
[Obsolete ("Use NonpagedSystemMemorySize64")]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The number of bytes that are not pageable.")]
|
|
|
|
public int NonpagedSystemMemorySize {
|
|
|
|
get {
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Use PagedMemorySize64")]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The number of bytes that are paged.")]
|
|
|
|
public int PagedMemorySize {
|
|
|
|
get {
|
2015-04-07 09:35:12 +01:00
|
|
|
return(int)PagedMemorySize64;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Use PagedSystemMemorySize64")]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The amount of paged system memory in bytes.")]
|
|
|
|
public int PagedSystemMemorySize {
|
|
|
|
get {
|
2015-04-07 09:35:12 +01:00
|
|
|
return(int)PagedMemorySize64;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[MonoTODO]
|
|
|
|
[Obsolete ("Use PeakPagedMemorySize64")]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The maximum amount of paged memory used by this process.")]
|
|
|
|
public int PeakPagedMemorySize {
|
|
|
|
get {
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Use PeakVirtualMemorySize64")]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The maximum amount of virtual memory used by this process.")]
|
|
|
|
public int PeakVirtualMemorySize {
|
|
|
|
get {
|
|
|
|
int error;
|
|
|
|
return (int)GetProcessData (pid, 8, out error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Use PeakWorkingSet64")]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The maximum amount of system memory used by this process.")]
|
|
|
|
public int PeakWorkingSet {
|
|
|
|
get {
|
|
|
|
int error;
|
|
|
|
return (int)GetProcessData (pid, 5, out error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[MonoTODO]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The number of bytes that are not pageable.")]
|
|
|
|
[ComVisible (false)]
|
|
|
|
public long NonpagedSystemMemorySize64 {
|
|
|
|
get {
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The number of bytes that are paged.")]
|
|
|
|
[ComVisible (false)]
|
|
|
|
public long PagedMemorySize64 {
|
|
|
|
get {
|
2015-04-07 09:35:12 +01:00
|
|
|
int error;
|
|
|
|
return GetProcessData (pid, 12, out error);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The amount of paged system memory in bytes.")]
|
|
|
|
[ComVisible (false)]
|
|
|
|
public long PagedSystemMemorySize64 {
|
|
|
|
get {
|
2015-04-07 09:35:12 +01:00
|
|
|
return PagedMemorySize64;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[MonoTODO]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The maximum amount of paged memory used by this process.")]
|
|
|
|
[ComVisible (false)]
|
|
|
|
public long PeakPagedMemorySize64 {
|
|
|
|
get {
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The maximum amount of virtual memory used by this process.")]
|
|
|
|
[ComVisible (false)]
|
|
|
|
public long PeakVirtualMemorySize64 {
|
|
|
|
get {
|
|
|
|
int error;
|
|
|
|
return GetProcessData (pid, 8, out error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The maximum amount of system memory used by this process.")]
|
|
|
|
[ComVisible (false)]
|
|
|
|
public long PeakWorkingSet64 {
|
|
|
|
get {
|
|
|
|
int error;
|
|
|
|
return GetProcessData (pid, 5, out error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[MonoTODO]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("Process will be of higher priority while it is actively used.")]
|
|
|
|
public bool PriorityBoostEnabled {
|
|
|
|
get {
|
|
|
|
return(false);
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[MonoLimitation ("Under Unix, only root is allowed to raise the priority.")]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The relative process priority.")]
|
|
|
|
public ProcessPriorityClass PriorityClass {
|
|
|
|
get {
|
|
|
|
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)) {
|
|
|
|
CheckExited ();
|
|
|
|
throw new Win32Exception (error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckExited () {
|
|
|
|
if (HasExited)
|
|
|
|
throw new InvalidOperationException (String.Format ("Cannot process request because the process ({0}) has exited.", Id));
|
|
|
|
}
|
|
|
|
|
|
|
|
[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);
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The amount of memory exclusively used by this process.")]
|
|
|
|
[Obsolete ("Use PrivateMemorySize64")]
|
|
|
|
public int PrivateMemorySize {
|
|
|
|
get {
|
|
|
|
int error;
|
|
|
|
return (int)GetProcessData (pid, 6, out error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[MonoNotSupported ("")]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The session ID for this process.")]
|
|
|
|
public int SessionId {
|
|
|
|
get { throw new NotImplementedException (); }
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 (Times (process_handle, 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
|
|
private extern static string ProcessName_internal(IntPtr handle);
|
|
|
|
|
|
|
|
private string process_name=null;
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The name of this process.")]
|
|
|
|
public string ProcessName {
|
|
|
|
get {
|
|
|
|
if(process_name==null) {
|
|
|
|
|
|
|
|
if (process_handle == IntPtr.Zero)
|
|
|
|
throw new InvalidOperationException ("No process is associated with this object.");
|
|
|
|
|
|
|
|
process_name=ProcessName_internal(process_handle);
|
|
|
|
/* If process_name is _still_
|
|
|
|
* null, assume the process
|
|
|
|
* has exited
|
|
|
|
*/
|
|
|
|
if (process_name == null)
|
|
|
|
throw new InvalidOperationException ("Process has exited, so the requested information is not available.");
|
|
|
|
|
|
|
|
/* Strip the suffix (if it
|
|
|
|
* exists) simplistically
|
|
|
|
* instead of removing any
|
|
|
|
* trailing \.???, so we dont
|
|
|
|
* get stupid results on sane
|
|
|
|
* systems
|
|
|
|
*/
|
|
|
|
if(process_name.EndsWith(".exe") ||
|
|
|
|
process_name.EndsWith(".bat") ||
|
|
|
|
process_name.EndsWith(".com")) {
|
|
|
|
process_name=process_name.Substring(0, process_name.Length-4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(process_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[MonoTODO]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("Allowed processor that can be used by this process.")]
|
|
|
|
public IntPtr ProcessorAffinity {
|
|
|
|
get {
|
|
|
|
return((IntPtr)0);
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[MonoTODO]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("Is this process responsive.")]
|
|
|
|
public bool Responding {
|
|
|
|
get {
|
|
|
|
return(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
#if MONO_FEATURE_PROCESS_START
|
2014-08-13 10:39:27 +01:00
|
|
|
private StreamReader error_stream=null;
|
2015-07-20 03:39:00 -04:00
|
|
|
bool error_stream_exposed;
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
|
|
|
|
[MonitoringDescription ("The standard error stream of this process.")]
|
|
|
|
public StreamReader StandardError {
|
|
|
|
get {
|
|
|
|
if (error_stream == null)
|
|
|
|
throw new InvalidOperationException("Standard error has not been redirected");
|
|
|
|
|
|
|
|
if ((async_mode & AsyncModes.AsyncError) != 0)
|
|
|
|
throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
|
|
|
|
|
|
|
|
async_mode |= AsyncModes.SyncError;
|
|
|
|
|
2015-07-20 03:39:00 -04:00
|
|
|
error_stream_exposed = true;
|
2014-08-13 10:39:27 +01:00
|
|
|
return(error_stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private StreamWriter input_stream=null;
|
2015-07-20 03:39:00 -04:00
|
|
|
bool input_stream_exposed;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
|
|
|
|
[MonitoringDescription ("The standard input stream of this process.")]
|
|
|
|
public StreamWriter StandardInput {
|
|
|
|
get {
|
|
|
|
if (input_stream == null)
|
|
|
|
throw new InvalidOperationException("Standard input has not been redirected");
|
|
|
|
|
2015-07-20 03:39:00 -04:00
|
|
|
input_stream_exposed = true;
|
2014-08-13 10:39:27 +01:00
|
|
|
return(input_stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private StreamReader output_stream=null;
|
2015-07-20 03:39:00 -04:00
|
|
|
bool output_stream_exposed;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
|
|
|
|
[MonitoringDescription ("The standard output stream of this process.")]
|
|
|
|
public StreamReader StandardOutput {
|
|
|
|
get {
|
|
|
|
if (output_stream == null)
|
|
|
|
throw new InvalidOperationException("Standard output has not been redirected");
|
|
|
|
|
|
|
|
if ((async_mode & AsyncModes.AsyncOutput) != 0)
|
|
|
|
throw new InvalidOperationException ("Cannot mix asynchronous and synchonous reads.");
|
|
|
|
|
|
|
|
async_mode |= AsyncModes.SyncOutput;
|
|
|
|
|
2015-07-20 03:39:00 -04:00
|
|
|
output_stream_exposed = true;
|
2014-08-13 10:39:27 +01:00
|
|
|
return(output_stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private ProcessStartInfo start_info=null;
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Content), Browsable (false)]
|
|
|
|
[MonitoringDescription ("Information for the start of this process.")]
|
|
|
|
public ProcessStartInfo StartInfo {
|
|
|
|
get {
|
|
|
|
if (start_info == null)
|
|
|
|
start_info = new ProcessStartInfo();
|
|
|
|
return start_info;
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
if (value == null)
|
|
|
|
throw new ArgumentNullException("value");
|
|
|
|
start_info = value;
|
|
|
|
}
|
|
|
|
}
|
2016-02-22 11:00:01 -05:00
|
|
|
#else
|
|
|
|
[Obsolete ("Process.StandardError is not supported on the current platform.", true)]
|
|
|
|
public StreamReader StandardError {
|
|
|
|
get { throw new PlatformNotSupportedException ("Process.StandardError is not supported on the current platform."); }
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Process.StandardInput is not supported on the current platform.", true)]
|
|
|
|
public StreamWriter StandardInput {
|
|
|
|
get { throw new PlatformNotSupportedException ("Process.StandardInput is not supported on the current platform."); }
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Process.StandardOutput is not supported on the current platform.", true)]
|
|
|
|
public StreamReader StandardOutput {
|
|
|
|
get { throw new PlatformNotSupportedException ("Process.StandardOutput is not supported on the current platform."); }
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Process.StartInfo is not supported on the current platform.", true)]
|
|
|
|
public ProcessStartInfo StartInfo {
|
|
|
|
get { throw new PlatformNotSupportedException ("Process.StartInfo is not supported on the current platform."); }
|
|
|
|
set { throw new PlatformNotSupportedException ("Process.StartInfo is not supported on the current platform."); }
|
|
|
|
}
|
|
|
|
#endif // MONO_FEATURE_PROCESS_START
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
/* Returns the process start time in Windows file
|
|
|
|
* times (ticks from DateTime(1/1/1601 00:00 GMT))
|
|
|
|
*/
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
|
|
private extern static long StartTime_internal(IntPtr handle);
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The time this process started.")]
|
|
|
|
public DateTime StartTime {
|
|
|
|
get {
|
|
|
|
return(DateTime.FromFileTime(StartTime_internal(process_handle)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DefaultValue (null), Browsable (false)]
|
|
|
|
[MonitoringDescription ("The object that is used to synchronize event handler calls for this process.")]
|
|
|
|
public ISynchronizeInvoke SynchronizingObject {
|
|
|
|
get { return synchronizingObject; }
|
|
|
|
set { synchronizingObject = value; }
|
|
|
|
}
|
|
|
|
|
|
|
|
[MonoTODO]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The number of threads of this process.")]
|
|
|
|
public ProcessThreadCollection Threads {
|
|
|
|
get {
|
|
|
|
// This'll return a correctly-sized array of empty ProcessThreads for now.
|
|
|
|
int error;
|
|
|
|
return new ProcessThreadCollection(new ProcessThread[GetProcessData (pid, 0, out error)]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The total CPU time spent for this process.")]
|
|
|
|
public TimeSpan TotalProcessorTime {
|
|
|
|
get {
|
|
|
|
return new TimeSpan (Times (process_handle, 2));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The CPU time spent for this process in user mode.")]
|
|
|
|
public TimeSpan UserProcessorTime {
|
|
|
|
get {
|
|
|
|
return new TimeSpan (Times (process_handle, 0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Use VirtualMemorySize64")]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The amount of virtual memory currently used for this process.")]
|
|
|
|
public int VirtualMemorySize {
|
|
|
|
get {
|
|
|
|
int error;
|
|
|
|
return (int)GetProcessData (pid, 7, out error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Use WorkingSet64")]
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The amount of physical memory currently used for this process.")]
|
|
|
|
public int WorkingSet {
|
|
|
|
get {
|
|
|
|
int error;
|
|
|
|
return (int)GetProcessData (pid, 4, out error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The amount of memory exclusively used by this process.")]
|
|
|
|
[ComVisible (false)]
|
|
|
|
public long PrivateMemorySize64 {
|
|
|
|
get {
|
|
|
|
int error;
|
|
|
|
return GetProcessData (pid, 6, out error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The amount of virtual memory currently used for this process.")]
|
|
|
|
[ComVisible (false)]
|
|
|
|
public long VirtualMemorySize64 {
|
|
|
|
get {
|
|
|
|
int error;
|
|
|
|
return GetProcessData (pid, 7, out error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
|
|
[MonitoringDescription ("The amount of physical memory currently used for this process.")]
|
|
|
|
[ComVisible (false)]
|
|
|
|
public long WorkingSet64 {
|
|
|
|
get {
|
|
|
|
int error;
|
|
|
|
return GetProcessData (pid, 4, out error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Close()
|
|
|
|
{
|
|
|
|
Dispose (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
[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]
|
|
|
|
public static void EnterDebugMode() {
|
|
|
|
}
|
|
|
|
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
|
|
private extern static IntPtr GetProcess_internal(int pid);
|
|
|
|
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
|
|
private extern static int GetPid_internal();
|
|
|
|
|
|
|
|
public static Process GetCurrentProcess()
|
|
|
|
{
|
|
|
|
int pid = GetPid_internal();
|
|
|
|
IntPtr proc = GetProcess_internal(pid);
|
|
|
|
|
|
|
|
if (proc == IntPtr.Zero)
|
|
|
|
throw new SystemException("Can't find current process");
|
|
|
|
|
|
|
|
return (new Process (proc, pid));
|
|
|
|
}
|
|
|
|
|
|
|
|
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 ());
|
|
|
|
|
|
|
|
return (new Process (proc, processId));
|
|
|
|
}
|
|
|
|
|
|
|
|
[MonoTODO ("There is no support for retrieving process information from a remote machine")]
|
|
|
|
public static Process GetProcessById(int processId, string machineName) {
|
|
|
|
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();
|
|
|
|
|
2015-01-13 10:44:36 +00:00
|
|
|
public static Process[] GetProcesses ()
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
|
|
|
int [] pids = GetProcesses_internal ();
|
|
|
|
if (pids == null)
|
|
|
|
return new Process [0];
|
|
|
|
|
2015-01-13 10:44:36 +00:00
|
|
|
var proclist = new List<Process> (pids.Length);
|
2014-08-13 10:39:27 +01:00
|
|
|
for (int i = 0; i < pids.Length; i++) {
|
|
|
|
try {
|
|
|
|
proclist.Add (GetProcessById (pids [i]));
|
|
|
|
} catch (SystemException) {
|
|
|
|
/* The process might exit
|
|
|
|
* between
|
|
|
|
* GetProcesses_internal and
|
|
|
|
* GetProcessById
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-13 10:44:36 +00:00
|
|
|
return proclist.ToArray ();
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
[MonoTODO ("There is no support for retrieving process information from a remote machine")]
|
|
|
|
public static Process[] GetProcesses(string machineName) {
|
|
|
|
if (machineName == null)
|
|
|
|
throw new ArgumentNullException ("machineName");
|
|
|
|
|
|
|
|
if (!IsLocalMachine (machineName))
|
|
|
|
throw new NotImplementedException ();
|
|
|
|
|
|
|
|
return GetProcesses ();
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Process[] GetProcessesByName(string processName)
|
|
|
|
{
|
|
|
|
int [] pids = GetProcesses_internal ();
|
|
|
|
if (pids == null)
|
|
|
|
return new Process [0];
|
|
|
|
|
2015-01-13 10:44:36 +00:00
|
|
|
var proclist = new List<Process> (pids.Length);
|
2014-08-13 10:39:27 +01:00
|
|
|
for (int i = 0; i < pids.Length; i++) {
|
|
|
|
try {
|
|
|
|
Process p = GetProcessById (pids [i]);
|
|
|
|
if (String.Compare (processName, p.ProcessName, true) == 0)
|
|
|
|
proclist.Add (p);
|
|
|
|
} catch (SystemException) {
|
|
|
|
/* The process might exit
|
|
|
|
* between
|
|
|
|
* GetProcesses_internal and
|
|
|
|
* GetProcessById
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-13 10:44:36 +00:00
|
|
|
return proclist.ToArray ();
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
[MonoTODO]
|
|
|
|
public static Process[] GetProcessesByName(string processName, string machineName) {
|
|
|
|
throw new NotImplementedException();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Kill ()
|
|
|
|
{
|
|
|
|
Close (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
[MonoTODO]
|
|
|
|
public static void LeaveDebugMode() {
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Refresh ()
|
|
|
|
{
|
|
|
|
// FIXME: should refresh any cached data we might have about
|
|
|
|
// the process (currently we have none).
|
|
|
|
}
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
#if MONO_FEATURE_PROCESS_START
|
2014-08-13 10:39:27 +01:00
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
|
|
private extern static bool ShellExecuteEx_internal(ProcessStartInfo startInfo,
|
|
|
|
ref ProcInfo proc_info);
|
|
|
|
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
|
|
private extern static bool CreateProcess_internal(ProcessStartInfo startInfo,
|
|
|
|
IntPtr stdin,
|
|
|
|
IntPtr stdout,
|
|
|
|
IntPtr stderr,
|
|
|
|
ref ProcInfo proc_info);
|
|
|
|
|
|
|
|
private static bool Start_shell (ProcessStartInfo startInfo, Process process)
|
|
|
|
{
|
|
|
|
ProcInfo proc_info=new ProcInfo();
|
|
|
|
bool ret;
|
|
|
|
|
|
|
|
if (startInfo.RedirectStandardInput ||
|
|
|
|
startInfo.RedirectStandardOutput ||
|
|
|
|
startInfo.RedirectStandardError) {
|
|
|
|
throw new InvalidOperationException ("UseShellExecute must be false when redirecting I/O.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (startInfo.HaveEnvVars)
|
|
|
|
throw new InvalidOperationException ("UseShellExecute must be false in order to use environment variables.");
|
|
|
|
|
|
|
|
FillUserInfo (startInfo, ref proc_info);
|
|
|
|
try {
|
|
|
|
ret = ShellExecuteEx_internal (startInfo,
|
|
|
|
ref proc_info);
|
|
|
|
} finally {
|
|
|
|
if (proc_info.Password != IntPtr.Zero)
|
2015-01-13 10:44:36 +00:00
|
|
|
Marshal.ZeroFreeBSTR (proc_info.Password);
|
2014-08-13 10:39:27 +01:00
|
|
|
proc_info.Password = IntPtr.Zero;
|
|
|
|
}
|
|
|
|
if (!ret) {
|
|
|
|
throw new Win32Exception (-proc_info.pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
process.process_handle = proc_info.process_handle;
|
|
|
|
process.pid = proc_info.pid;
|
2016-02-22 11:00:01 -05:00
|
|
|
process.StartBackgroundWaitForExit ();
|
2014-08-13 10:39:27 +01:00
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
//
|
|
|
|
// Creates a pipe with read and write descriptors
|
|
|
|
//
|
|
|
|
static void CreatePipe (out IntPtr read, out IntPtr write, bool writeDirection)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
|
|
|
MonoIOError error;
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
//
|
|
|
|
// Creates read/write pipe from parent -> child perspective
|
|
|
|
// a child process uses same descriptors after fork. That's
|
|
|
|
// 4 descriptors in total where only 2. One in child, one in parent
|
|
|
|
// should be active and the other 2 closed. Which ones depends on
|
|
|
|
// comunication direction
|
|
|
|
//
|
|
|
|
// parent --------> child (parent can write, child can read)
|
|
|
|
//
|
|
|
|
// read: closed read: used
|
|
|
|
// write: used write: closed
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// parent <-------- child (parent can read, child can write)
|
|
|
|
//
|
|
|
|
// read: used read: closed
|
|
|
|
// write: closed write: used
|
|
|
|
//
|
|
|
|
// It can still be tricky for predefined descriptiors http://unixwiz.net/techtips/remap-pipe-fds.html
|
|
|
|
//
|
|
|
|
if (!MonoIO.CreatePipe (out read, out write, out error))
|
|
|
|
throw MonoIO.GetException (error);
|
|
|
|
|
|
|
|
if (IsWindows) {
|
|
|
|
const int DUPLICATE_SAME_ACCESS = 0x00000002;
|
|
|
|
var tmp = writeDirection ? write : read;
|
|
|
|
|
|
|
|
if (!MonoIO.DuplicateHandle (Process.GetCurrentProcess ().Handle, tmp, Process.GetCurrentProcess ().Handle, out tmp, 0, 0, DUPLICATE_SAME_ACCESS, out error))
|
|
|
|
throw MonoIO.GetException (error);
|
|
|
|
|
|
|
|
if (writeDirection) {
|
|
|
|
if (!MonoIO.Close (write, out error))
|
|
|
|
throw MonoIO.GetException (error);
|
|
|
|
write = tmp;
|
|
|
|
} else {
|
|
|
|
if (!MonoIO.Close (read, out error))
|
|
|
|
throw MonoIO.GetException (error);
|
|
|
|
read = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool Start_noshell (ProcessStartInfo startInfo, Process process)
|
|
|
|
{
|
|
|
|
var proc_info = new ProcInfo ();
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
if (startInfo.HaveEnvVars) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
MonoIOError error;
|
|
|
|
IntPtr stdin_read = IntPtr.Zero, stdin_write = IntPtr.Zero;
|
|
|
|
IntPtr stdout_read = IntPtr.Zero, stdout_write = IntPtr.Zero;
|
|
|
|
IntPtr stderr_read = IntPtr.Zero, stderr_write = IntPtr.Zero;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
try {
|
|
|
|
if (startInfo.RedirectStandardInput) {
|
|
|
|
CreatePipe (out stdin_read, out stdin_write, true);
|
|
|
|
} else {
|
|
|
|
stdin_read = MonoIO.ConsoleInput;
|
|
|
|
stdin_write = IntPtr.Zero;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
2016-02-22 11:00:01 -05:00
|
|
|
|
|
|
|
if (startInfo.RedirectStandardOutput) {
|
|
|
|
CreatePipe (out stdout_read, out stdout_write, false);
|
|
|
|
} else {
|
|
|
|
stdout_read = IntPtr.Zero;
|
|
|
|
stdout_write = MonoIO.ConsoleOutput;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
2016-02-22 11:00:01 -05:00
|
|
|
|
|
|
|
if (startInfo.RedirectStandardError) {
|
|
|
|
CreatePipe (out stderr_read, out stderr_write, false);
|
|
|
|
} else {
|
|
|
|
stderr_read = IntPtr.Zero;
|
|
|
|
stderr_write = MonoIO.ConsoleError;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
FillUserInfo (startInfo, ref proc_info);
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
//
|
|
|
|
// FIXME: For redirected pipes we need to send descriptors of
|
|
|
|
// stdin_write, stdout_read, stderr_read to child process and
|
|
|
|
// close them there (fork makes exact copy of parent's descriptors)
|
|
|
|
//
|
|
|
|
if (!CreateProcess_internal (startInfo, stdin_read, stdout_write, stderr_write, ref proc_info)) {
|
|
|
|
throw new Win32Exception (-proc_info.pid,
|
|
|
|
"ApplicationName='" + startInfo.FileName +
|
|
|
|
"', CommandLine='" + startInfo.Arguments +
|
|
|
|
"', CurrentDirectory='" + startInfo.WorkingDirectory +
|
|
|
|
"', Native error= " + Win32Exception.W32ErrorMessage (-proc_info.pid));
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
2016-02-22 11:00:01 -05:00
|
|
|
} catch {
|
|
|
|
if (startInfo.RedirectStandardInput) {
|
|
|
|
if (stdin_read != IntPtr.Zero)
|
|
|
|
MonoIO.Close (stdin_read, out error);
|
|
|
|
if (stdin_write != IntPtr.Zero)
|
|
|
|
MonoIO.Close (stdin_write, out error);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
2016-02-22 11:00:01 -05:00
|
|
|
|
|
|
|
if (startInfo.RedirectStandardOutput) {
|
|
|
|
if (stdout_read != IntPtr.Zero)
|
|
|
|
MonoIO.Close (stdout_read, out error);
|
|
|
|
if (stdout_write != IntPtr.Zero)
|
|
|
|
MonoIO.Close (stdout_write, out error);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
if (startInfo.RedirectStandardError) {
|
|
|
|
if (stderr_read != IntPtr.Zero)
|
|
|
|
MonoIO.Close (stderr_read, out error);
|
|
|
|
if (stderr_write != IntPtr.Zero)
|
|
|
|
MonoIO.Close (stderr_write, out error);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
throw;
|
2014-08-13 10:39:27 +01:00
|
|
|
} finally {
|
2016-02-22 11:00:01 -05:00
|
|
|
if (proc_info.Password != IntPtr.Zero) {
|
2015-01-13 10:44:36 +00:00
|
|
|
Marshal.ZeroFreeBSTR (proc_info.Password);
|
2016-02-22 11:00:01 -05:00
|
|
|
proc_info.Password = IntPtr.Zero;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
process.process_handle = proc_info.process_handle;
|
|
|
|
process.pid = proc_info.pid;
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
if (startInfo.RedirectStandardInput) {
|
|
|
|
//
|
|
|
|
// FIXME: The descriptor needs to be closed but due to wapi io-layer
|
|
|
|
// not coping with duplicated descriptors any StandardInput write fails
|
|
|
|
//
|
|
|
|
// MonoIO.Close (stdin_read, out error);
|
|
|
|
|
|
|
|
#if MOBILE
|
|
|
|
var stdinEncoding = Encoding.Default;
|
|
|
|
#else
|
|
|
|
var stdinEncoding = Console.InputEncoding;
|
|
|
|
#endif
|
|
|
|
process.input_stream = new StreamWriter (new FileStream (stdin_write, FileAccess.Write, true, 8192), stdinEncoding) {
|
|
|
|
AutoFlush = true
|
|
|
|
};
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
if (startInfo.RedirectStandardOutput) {
|
|
|
|
MonoIO.Close (stdout_write, out error);
|
|
|
|
|
|
|
|
Encoding stdoutEncoding = startInfo.StandardOutputEncoding ?? Console.Out.Encoding;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
process.output_stream = new StreamReader (new FileStream (stdout_read, FileAccess.Read, true, 8192), stdoutEncoding, true);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
if (startInfo.RedirectStandardError) {
|
|
|
|
MonoIO.Close (stderr_write, out error);
|
|
|
|
|
|
|
|
Encoding stderrEncoding = startInfo.StandardErrorEncoding ?? Console.Out.Encoding;
|
|
|
|
|
|
|
|
process.error_stream = new StreamReader (new FileStream (stderr_read, FileAccess.Read, true, 8192), stderrEncoding, true);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
process.StartBackgroundWaitForExit ();
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
return true;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Note that ProcInfo.Password must be freed.
|
|
|
|
private static void FillUserInfo (ProcessStartInfo startInfo, ref ProcInfo proc_info)
|
|
|
|
{
|
2015-04-07 09:35:12 +01:00
|
|
|
if (startInfo.UserName.Length != 0) {
|
2014-08-13 10:39:27 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static bool Start_common (ProcessStartInfo startInfo,
|
|
|
|
Process process)
|
|
|
|
{
|
2015-04-07 09:35:12 +01:00
|
|
|
if (startInfo.FileName.Length == 0)
|
2014-08-13 10:39:27 +01:00
|
|
|
throw new InvalidOperationException("File name has not been set");
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
|
|
|
if (startInfo.UseShellExecute) {
|
2015-04-07 09:35:12 +01:00
|
|
|
if (startInfo.UserName.Length != 0)
|
2014-08-13 10:39:27 +01:00
|
|
|
throw new InvalidOperationException ("UseShellExecute must be false if an explicit UserName is specified when starting a process");
|
|
|
|
return (Start_shell (startInfo, process));
|
|
|
|
} else {
|
|
|
|
return (Start_noshell (startInfo, process));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
if (startInfo == null)
|
|
|
|
throw new ArgumentNullException ("startInfo");
|
|
|
|
|
|
|
|
Process process = new Process();
|
|
|
|
process.StartInfo = startInfo;
|
|
|
|
if (Start_common(startInfo, process) && process.process_handle != IntPtr.Zero)
|
|
|
|
return process;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 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);
|
|
|
|
}
|
2016-02-22 11:00:01 -05:00
|
|
|
#else
|
|
|
|
[Obsolete ("Process.Start is not supported on the current platform.", true)]
|
|
|
|
public bool Start ()
|
|
|
|
{
|
|
|
|
throw new PlatformNotSupportedException ("Process.Start is not supported on the current platform.");
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Process.Start is not supported on the current platform.", true)]
|
|
|
|
public static Process Start (ProcessStartInfo startInfo)
|
|
|
|
{
|
|
|
|
throw new PlatformNotSupportedException ("Process.Start is not supported on the current platform.");
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Process.Start is not supported on the current platform.", true)]
|
|
|
|
public static Process Start (string fileName)
|
|
|
|
{
|
|
|
|
throw new PlatformNotSupportedException ("Process.Start is not supported on the current platform.");
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Process.Start is not supported on the current platform.", true)]
|
|
|
|
public static Process Start(string fileName, string arguments)
|
|
|
|
{
|
|
|
|
throw new PlatformNotSupportedException ("Process.Start is not supported on the current platform.");
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Process.Start is not supported on the current platform.", true)]
|
|
|
|
public static Process Start(string fileName, string username, SecureString password, string domain)
|
|
|
|
{
|
|
|
|
throw new PlatformNotSupportedException ("Process.Start is not supported on the current platform.");
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Process.Start is not supported on the current platform.", true)]
|
|
|
|
public static Process Start(string fileName, string arguments, string username, SecureString password, string domain)
|
|
|
|
{
|
|
|
|
throw new PlatformNotSupportedException ("Process.Start is not supported on the current platform.");
|
|
|
|
}
|
|
|
|
#endif // MONO_FEATURE_PROCESS_START
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
public void WaitForExit ()
|
|
|
|
{
|
|
|
|
WaitForExit (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool WaitForExit(int milliseconds) {
|
|
|
|
int ms = milliseconds;
|
|
|
|
if (ms == int.MaxValue)
|
|
|
|
ms = -1;
|
|
|
|
|
|
|
|
if (process_handle == IntPtr.Zero)
|
|
|
|
throw new InvalidOperationException ("No process is associated with this object.");
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
if (!WaitForExit_internal (process_handle, ms))
|
|
|
|
return false;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
#if MONO_FEATURE_PROCESS_START
|
|
|
|
if (async_output != null)
|
|
|
|
async_output.WaitUtilEOF ();
|
2015-08-26 07:17:56 -04:00
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
if (async_error != null)
|
|
|
|
async_error.WaitUtilEOF ();
|
|
|
|
#endif // MONO_FEATURE_PROCESS_START
|
2015-08-26 07:17:56 -04:00
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
if (EnableRaisingEvents)
|
2015-08-26 07:17:56 -04:00
|
|
|
OnExited ();
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
return true;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Waits up to ms milliseconds for process 'handle' to
|
|
|
|
* wait for input. ms can be <0 to mean wait forever.
|
|
|
|
*/
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
|
|
private extern bool WaitForInputIdle_internal(IntPtr handle, int ms);
|
|
|
|
|
|
|
|
// The internal call is only implemented properly on Windows.
|
|
|
|
[MonoTODO]
|
|
|
|
public bool WaitForInputIdle() {
|
|
|
|
return WaitForInputIdle (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The internal call is only implemented properly on Windows.
|
|
|
|
[MonoTODO]
|
|
|
|
public bool WaitForInputIdle(int milliseconds) {
|
|
|
|
return WaitForInputIdle_internal (process_handle, milliseconds);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static bool IsLocalMachine (string machineName)
|
|
|
|
{
|
|
|
|
if (machineName == "." || machineName.Length == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return (string.Compare (machineName, Environment.MachineName, true) == 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;
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
#if MONO_FEATURE_PROCESS_START
|
2014-08-13 10:39:27 +01:00
|
|
|
[Flags]
|
|
|
|
enum AsyncModes {
|
|
|
|
NoneYet = 0,
|
|
|
|
SyncOutput = 1,
|
|
|
|
SyncError = 1 << 1,
|
|
|
|
AsyncOutput = 1 << 2,
|
|
|
|
AsyncError = 1 << 3
|
|
|
|
}
|
|
|
|
|
|
|
|
AsyncModes async_mode;
|
2016-02-22 11:00:01 -05:00
|
|
|
AsyncStreamReader async_output;
|
|
|
|
AsyncStreamReader async_error;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
[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.");
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
if ((async_mode & AsyncModes.AsyncOutput) != 0)
|
|
|
|
throw new InvalidOperationException ("An async read operation has already been started on the stream.");
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
async_mode |= AsyncModes.AsyncOutput;
|
2016-02-22 11:00:01 -05:00
|
|
|
|
|
|
|
if (async_output == null)
|
|
|
|
async_output = new AsyncStreamReader (this, output_stream.BaseStream, new UserCallBack(this.OutputReadNotifyUser), output_stream.CurrentEncoding);
|
|
|
|
|
|
|
|
async_output.BeginReadLine ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void OutputReadNotifyUser (String data)
|
|
|
|
{
|
|
|
|
// To avoid ---- between remove handler and raising the event
|
|
|
|
DataReceivedEventHandler outputDataReceived = OutputDataReceived;
|
|
|
|
if (outputDataReceived != null) {
|
|
|
|
if (SynchronizingObject != null && SynchronizingObject.InvokeRequired)
|
|
|
|
SynchronizingObject.Invoke (outputDataReceived, new object[] { this, new DataReceivedEventArgs (data) });
|
|
|
|
else
|
|
|
|
outputDataReceived (this, new DataReceivedEventArgs (data)); // Call back to user informing data is available.
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[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.");
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
async_output.CancelOperation ();
|
|
|
|
|
|
|
|
async_mode &= ~AsyncModes.AsyncOutput;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
[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.");
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
if ((async_mode & AsyncModes.AsyncError) != 0)
|
|
|
|
throw new InvalidOperationException ("An async read operation has already been started on the stream.");
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
async_mode |= AsyncModes.AsyncError;
|
2016-02-22 11:00:01 -05:00
|
|
|
|
|
|
|
if (async_error == null)
|
|
|
|
async_error = new AsyncStreamReader (this, error_stream.BaseStream, new UserCallBack(this.ErrorReadNotifyUser), error_stream.CurrentEncoding);
|
|
|
|
|
|
|
|
async_error.BeginReadLine ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ErrorReadNotifyUser (String data)
|
|
|
|
{
|
|
|
|
// To avoid ---- between remove handler and raising the event
|
|
|
|
DataReceivedEventHandler errorDataReceived = ErrorDataReceived;
|
|
|
|
if (errorDataReceived != null) {
|
|
|
|
if (SynchronizingObject != null && SynchronizingObject.InvokeRequired)
|
|
|
|
SynchronizingObject.Invoke (errorDataReceived, new object[] { this, new DataReceivedEventArgs (data) });
|
|
|
|
else
|
|
|
|
errorDataReceived (this, new DataReceivedEventArgs (data)); // Call back to user informing data is available.
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[ComVisibleAttribute(false)]
|
|
|
|
public void CancelErrorRead ()
|
|
|
|
{
|
|
|
|
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.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.");
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
async_error.CancelOperation ();
|
|
|
|
|
|
|
|
async_mode &= ~AsyncModes.AsyncError;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
2016-02-22 11:00:01 -05:00
|
|
|
#else
|
|
|
|
[Obsolete ("Process.BeginOutputReadLine is not supported on the current platform.", true)]
|
|
|
|
public void BeginOutputReadLine ()
|
|
|
|
{
|
|
|
|
throw new PlatformNotSupportedException ("Process.BeginOutputReadLine is not supported on the current platform.");
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Process.BeginOutputReadLine is not supported on the current platform.", true)]
|
|
|
|
public void CancelOutputRead ()
|
|
|
|
{
|
|
|
|
throw new PlatformNotSupportedException ("Process.BeginOutputReadLine is not supported on the current platform.");
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Process.BeginOutputReadLine is not supported on the current platform.", true)]
|
|
|
|
public void BeginErrorReadLine ()
|
|
|
|
{
|
|
|
|
throw new PlatformNotSupportedException ("Process.BeginOutputReadLine is not supported on the current platform.");
|
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete ("Process.BeginOutputReadLine is not supported on the current platform.", true)]
|
|
|
|
public void CancelErrorRead ()
|
|
|
|
{
|
|
|
|
throw new PlatformNotSupportedException ("Process.BeginOutputReadLine is not supported on the current platform.");
|
|
|
|
}
|
|
|
|
#endif // MONO_FEATURE_PROCESS_START
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
[Category ("Behavior")]
|
|
|
|
[MonitoringDescription ("Raised when this process exits.")]
|
|
|
|
public event EventHandler Exited {
|
|
|
|
add {
|
|
|
|
if (process_handle != IntPtr.Zero && HasExited) {
|
|
|
|
value.BeginInvoke (null, null, null, null);
|
|
|
|
} else {
|
2016-02-22 11:00:01 -05:00
|
|
|
exited_event += value;
|
2014-08-13 10:39:27 +01:00
|
|
|
if (exited_event != null)
|
2016-02-22 11:00:01 -05:00
|
|
|
StartBackgroundWaitForExit ();
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
remove {
|
2016-02-22 11:00:01 -05:00
|
|
|
exited_event -= value;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Closes the system process handle
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
|
|
private extern void Process_free_internal(IntPtr handle);
|
2016-02-22 11:00:01 -05:00
|
|
|
|
|
|
|
int disposed;
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
protected override void Dispose(bool disposing) {
|
|
|
|
// Check to see if Dispose has already been called.
|
2016-02-22 11:00:01 -05:00
|
|
|
if (disposed != 0 || Interlocked.CompareExchange (ref disposed, 1, 0) != 0)
|
|
|
|
return;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
// If this is a call to Dispose,
|
|
|
|
// dispose all managed resources.
|
|
|
|
if (disposing) {
|
|
|
|
#if MONO_FEATURE_PROCESS_START
|
|
|
|
async_output = null;
|
|
|
|
async_error = null;
|
|
|
|
|
|
|
|
if (input_stream != null) {
|
|
|
|
if (!input_stream_exposed)
|
|
|
|
input_stream.Close ();
|
|
|
|
input_stream = null;
|
|
|
|
}
|
|
|
|
if (output_stream != null) {
|
|
|
|
if (!output_stream_exposed)
|
|
|
|
output_stream.Close ();
|
|
|
|
output_stream = null;
|
|
|
|
}
|
|
|
|
if (error_stream != null) {
|
|
|
|
if (!error_stream_exposed)
|
|
|
|
error_stream.Close ();
|
|
|
|
error_stream = null;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
2016-02-22 11:00:01 -05:00
|
|
|
#endif // MONO_FEATURE_PROCESS_START
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
2016-02-22 11:00:01 -05:00
|
|
|
|
|
|
|
// Release unmanaged resources
|
|
|
|
|
|
|
|
if (process_handle!=IntPtr.Zero) {
|
|
|
|
Process_free_internal (process_handle);
|
|
|
|
process_handle = IntPtr.Zero;
|
|
|
|
}
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
base.Dispose (disposing);
|
|
|
|
}
|
|
|
|
|
|
|
|
~Process ()
|
|
|
|
{
|
|
|
|
Dispose (false);
|
|
|
|
}
|
|
|
|
|
2015-08-26 07:17:56 -04:00
|
|
|
int on_exited_called = 0;
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
protected void OnExited()
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2015-08-26 07:17:56 -04:00
|
|
|
if (on_exited_called != 0 || Interlocked.CompareExchange (ref on_exited_called, 1, 0) != 0)
|
|
|
|
return;
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
var cb = exited_event;
|
|
|
|
if (cb == null)
|
|
|
|
return;
|
2015-08-26 07:17:56 -04:00
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
if (synchronizingObject != null) {
|
|
|
|
synchronizingObject.BeginInvoke (cb, new object [] { this, EventArgs.Empty });
|
|
|
|
} else {
|
|
|
|
foreach (EventHandler d in cb.GetInvocationList ()) {
|
2014-08-13 10:39:27 +01:00
|
|
|
try {
|
|
|
|
d (this, EventArgs.Empty);
|
2016-02-22 11:00:01 -05:00
|
|
|
} catch {
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool IsWindows
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
PlatformID platform = Environment.OSVersion.Platform;
|
|
|
|
if (platform == PlatformID.Win32S ||
|
|
|
|
platform == PlatformID.Win32Windows ||
|
|
|
|
platform == PlatformID.Win32NT ||
|
|
|
|
platform == PlatformID.WinCE) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-22 11:00:01 -05:00
|
|
|
void StartBackgroundWaitForExit ()
|
|
|
|
{
|
|
|
|
IntPtr handle = process_handle;
|
|
|
|
|
|
|
|
if (enable_raising_events == 0)
|
|
|
|
return;
|
|
|
|
if (exited_event == null)
|
|
|
|
return;
|
|
|
|
if (handle == IntPtr.Zero)
|
|
|
|
return;
|
|
|
|
if (background_wait_for_exit_thread != null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Thread t = new Thread (_ => {
|
|
|
|
if (!WaitForExit_internal (handle, -1))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (EnableRaisingEvents)
|
|
|
|
OnExited ();
|
|
|
|
});
|
|
|
|
|
|
|
|
t.IsBackground = true;
|
|
|
|
|
|
|
|
if (Interlocked.CompareExchange (ref background_wait_for_exit_thread, t, null) == null)
|
|
|
|
t.Start ();
|
|
|
|
}
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
class ProcessWaitHandle : WaitHandle
|
|
|
|
{
|
|
|
|
[MethodImplAttribute (MethodImplOptions.InternalCall)]
|
|
|
|
private extern static IntPtr ProcessHandle_duplicate (IntPtr handle);
|
2016-02-22 11:00:01 -05:00
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
public ProcessWaitHandle (IntPtr handle)
|
|
|
|
{
|
|
|
|
// Need to keep a reference to this handle,
|
|
|
|
// in case the Process object is collected
|
|
|
|
Handle = ProcessHandle_duplicate (handle);
|
|
|
|
|
|
|
|
// When the wait handle is disposed, the duplicated handle will be
|
|
|
|
// closed, so no need to override dispose (bug #464628).
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|