8e12397d70
Former-commit-id: 7f59f7e792705db773f1caecdaa823092f4e2927
910 lines
29 KiB
C#
910 lines
29 KiB
C#
//
|
|
// 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.Collections;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.Design;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
using System.Runtime.Remoting.Messaging;
|
|
using System.Security.Permissions;
|
|
using System.Collections.Generic;
|
|
using System.Security;
|
|
using System.Threading;
|
|
using Microsoft.Win32;
|
|
using Microsoft.Win32.SafeHandles;
|
|
|
|
namespace System.Diagnostics
|
|
{
|
|
public partial 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 int pid; // Contains -GetLastError () on failure.
|
|
public string[] envVariables;
|
|
public string UserName;
|
|
public string Domain;
|
|
public IntPtr Password;
|
|
public bool LoadUserProfile;
|
|
};
|
|
|
|
string process_name;
|
|
|
|
static ProcessModule current_main_module;
|
|
|
|
/* Private constructor called from other methods */
|
|
private Process (SafeProcessHandle handle, int id) {
|
|
SetProcessHandle (handle);
|
|
SetProcessId (id);
|
|
}
|
|
|
|
[MonoTODO]
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
[MonitoringDescription ("Base process priority.")]
|
|
public int BasePriority {
|
|
get { return 0; }
|
|
}
|
|
|
|
[MonoTODO]
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
[MonitoringDescription ("Handles for this process.")]
|
|
public int HandleCount {
|
|
get {
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
|
|
[MonitoringDescription ("The main module of the process.")]
|
|
public ProcessModule MainModule {
|
|
get {
|
|
/* Optimize Process.GetCurrentProcess ().MainModule */
|
|
if (processId == NativeMethods.GetCurrentProcessId ()) {
|
|
if (current_main_module == null)
|
|
current_main_module = this.Modules [0];
|
|
return current_main_module;
|
|
} else {
|
|
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");
|
|
}
|
|
}
|
|
|
|
/* Returns the list of process modules. The main module is
|
|
* element 0.
|
|
*/
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
private extern ProcessModule[] GetModules_internal(IntPtr handle);
|
|
|
|
ProcessModule[] GetModules_internal (SafeProcessHandle handle)
|
|
{
|
|
bool release = false;
|
|
try {
|
|
handle.DangerousAddRef (ref release);
|
|
return GetModules_internal (handle.DangerousGetHandle ());
|
|
} finally {
|
|
if (release)
|
|
handle.DangerousRelease ();
|
|
}
|
|
}
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden), Browsable (false)]
|
|
[MonitoringDescription ("The modules that are loaded as part of this process.")]
|
|
public ProcessModuleCollection Modules {
|
|
get {
|
|
if (modules == null) {
|
|
SafeProcessHandle handle = null;
|
|
try {
|
|
handle = GetProcessHandle (NativeMethods.PROCESS_QUERY_INFORMATION);
|
|
modules = new ProcessModuleCollection (GetModules_internal (handle));
|
|
} finally {
|
|
ReleaseProcessHandle (handle);
|
|
}
|
|
}
|
|
|
|
return modules;
|
|
}
|
|
}
|
|
|
|
/* 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 {
|
|
return(int)PagedMemorySize64;
|
|
}
|
|
}
|
|
|
|
[Obsolete ("Use PagedSystemMemorySize64")]
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
[MonitoringDescription ("The amount of paged system memory in bytes.")]
|
|
public int PagedSystemMemorySize {
|
|
get {
|
|
return(int)PagedMemorySize64;
|
|
}
|
|
}
|
|
|
|
[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 (processId, 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 (processId, 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 {
|
|
int error;
|
|
return GetProcessData (processId, 12, out error);
|
|
}
|
|
}
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
[MonitoringDescription ("The amount of paged system memory in bytes.")]
|
|
[ComVisible (false)]
|
|
public long PagedSystemMemorySize64 {
|
|
get {
|
|
return PagedMemorySize64;
|
|
}
|
|
}
|
|
|
|
[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 (processId, 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 (processId, 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 {
|
|
}
|
|
}
|
|
|
|
[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 (processId, 6, out error);
|
|
}
|
|
}
|
|
|
|
[MonoNotSupported ("")]
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
[MonitoringDescription ("The session ID for this process.")]
|
|
public int SessionId {
|
|
get { return 0; }
|
|
}
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
private extern static string ProcessName_internal(IntPtr handle);
|
|
|
|
static string ProcessName_internal(SafeProcessHandle handle)
|
|
{
|
|
bool release = false;
|
|
try {
|
|
handle.DangerousAddRef (ref release);
|
|
return ProcessName_internal (handle.DangerousGetHandle ());
|
|
} finally {
|
|
if (release)
|
|
handle.DangerousRelease ();
|
|
}
|
|
}
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
[MonitoringDescription ("The name of this process.")]
|
|
public string ProcessName {
|
|
get {
|
|
if (process_name == null) {
|
|
SafeProcessHandle handle = null;
|
|
try {
|
|
handle = GetProcessHandle (NativeMethods.PROCESS_QUERY_INFORMATION);
|
|
|
|
process_name = ProcessName_internal (handle);
|
|
|
|
/* If process_name is _still_ null, assume the process has exited or is inaccessible */
|
|
if (process_name == null)
|
|
throw new InvalidOperationException ("Process has exited or is inaccessible, 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);
|
|
} finally {
|
|
ReleaseProcessHandle (handle);
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
#if !MONO_FEATURE_PROCESS_START
|
|
[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."); }
|
|
}
|
|
|
|
[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."); }
|
|
}
|
|
#endif // !MONO_FEATURE_PROCESS_START
|
|
|
|
[MonoTODO]
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
[MonitoringDescription ("The number of threads of this process.")]
|
|
public ProcessThreadCollection Threads {
|
|
get {
|
|
if (threads == null) {
|
|
int error;
|
|
// This'll return a correctly-sized array of empty ProcessThreads for now.
|
|
threads = new ProcessThreadCollection(new ProcessThread [GetProcessData (processId, 0, out error)]);
|
|
}
|
|
|
|
return threads;
|
|
}
|
|
}
|
|
|
|
[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 (processId, 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 (processId, 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 (processId, 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 (processId, 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 (processId, 4, out error);
|
|
}
|
|
}
|
|
|
|
public bool CloseMainWindow ()
|
|
{
|
|
SafeProcessHandle handle = null;
|
|
try {
|
|
handle = GetProcessHandle (NativeMethods.PROCESS_TERMINATE);
|
|
return NativeMethods.TerminateProcess(handle, -2);
|
|
} finally {
|
|
ReleaseProcessHandle(handle);
|
|
}
|
|
}
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
private extern static IntPtr GetProcess_internal(int pid);
|
|
|
|
[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 ();
|
|
|
|
IntPtr proc = GetProcess_internal(processId);
|
|
|
|
if (proc == IntPtr.Zero)
|
|
throw new ArgumentException ("Can't find process with ID " + processId.ToString ());
|
|
|
|
/* The handle returned by GetProcess_internal is owned by its caller, so we must pass true to SafeProcessHandle */
|
|
return (new Process (new SafeProcessHandle (proc, true), processId));
|
|
}
|
|
|
|
public static Process[] GetProcessesByName(string processName, string machineName)
|
|
{
|
|
if (machineName == null)
|
|
throw new ArgumentNullException ("machineName");
|
|
|
|
if (!IsLocalMachine (machineName))
|
|
throw new NotImplementedException ();
|
|
|
|
Process[] processes = GetProcesses ();
|
|
if (processes.Length == 0)
|
|
return processes;
|
|
|
|
int size = 0;
|
|
|
|
for (int i = 0; i < processes.Length; i++) {
|
|
var process = processes[i];
|
|
try {
|
|
if (String.Compare (processName, process.ProcessName, true) == 0)
|
|
processes [size++] = process;
|
|
else
|
|
process.Dispose();
|
|
} catch (SystemException) {
|
|
/* The process might exit between GetProcesses_internal and GetProcessById */
|
|
}
|
|
}
|
|
|
|
Array.Resize<Process> (ref processes, size);
|
|
|
|
return processes;
|
|
}
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
private extern static int[] GetProcesses_internal();
|
|
|
|
[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 ();
|
|
|
|
int [] pids = GetProcesses_internal ();
|
|
if (pids == null)
|
|
return new Process [0];
|
|
|
|
var proclist = new List<Process> (pids.Length);
|
|
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
|
|
*/
|
|
}
|
|
}
|
|
|
|
return proclist.ToArray ();
|
|
}
|
|
|
|
private static bool IsLocalMachine (string machineName)
|
|
{
|
|
if (machineName == "." || machineName.Length == 0)
|
|
return true;
|
|
|
|
return (string.Compare (machineName, Environment.MachineName, true) == 0);
|
|
}
|
|
|
|
#if MONO_FEATURE_PROCESS_START
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
private extern static bool ShellExecuteEx_internal(ProcessStartInfo startInfo, ref ProcInfo procInfo);
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
private extern static bool CreateProcess_internal(ProcessStartInfo startInfo, IntPtr stdin, IntPtr stdout, IntPtr stderr, ref ProcInfo procInfo);
|
|
|
|
bool StartWithShellExecuteEx (ProcessStartInfo startInfo)
|
|
{
|
|
if (this.disposed)
|
|
throw new ObjectDisposedException (GetType ().Name);
|
|
|
|
if (!String.IsNullOrEmpty(startInfo.UserName) || (startInfo.Password != null))
|
|
throw new InvalidOperationException(SR.GetString(SR.CantStartAsUser));
|
|
|
|
if (startInfo.RedirectStandardInput || startInfo.RedirectStandardOutput || startInfo.RedirectStandardError)
|
|
throw new InvalidOperationException(SR.GetString(SR.CantRedirectStreams));
|
|
|
|
if (startInfo.StandardErrorEncoding != null)
|
|
throw new InvalidOperationException(SR.GetString(SR.StandardErrorEncodingNotAllowed));
|
|
|
|
if (startInfo.StandardOutputEncoding != null)
|
|
throw new InvalidOperationException(SR.GetString(SR.StandardOutputEncodingNotAllowed));
|
|
|
|
// can't set env vars with ShellExecuteEx...
|
|
if (startInfo.environmentVariables != null)
|
|
throw new InvalidOperationException(SR.GetString(SR.CantUseEnvVars));
|
|
|
|
ProcInfo procInfo = new ProcInfo();
|
|
bool ret;
|
|
|
|
FillUserInfo (startInfo, ref procInfo);
|
|
try {
|
|
ret = ShellExecuteEx_internal (startInfo, ref procInfo);
|
|
} finally {
|
|
if (procInfo.Password != IntPtr.Zero)
|
|
Marshal.ZeroFreeBSTR (procInfo.Password);
|
|
procInfo.Password = IntPtr.Zero;
|
|
}
|
|
if (!ret) {
|
|
throw new Win32Exception (-procInfo.pid);
|
|
}
|
|
|
|
SetProcessHandle (new SafeProcessHandle (procInfo.process_handle, true));
|
|
SetProcessId (procInfo.pid);
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Creates a pipe with read and write descriptors
|
|
//
|
|
static void CreatePipe (out IntPtr read, out IntPtr write, bool writeDirection)
|
|
{
|
|
MonoIOError error;
|
|
|
|
//
|
|
// 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 IsWindows
|
|
{
|
|
get
|
|
{
|
|
PlatformID platform = Environment.OSVersion.Platform;
|
|
if (platform == PlatformID.Win32S ||
|
|
platform == PlatformID.Win32Windows ||
|
|
platform == PlatformID.Win32NT ||
|
|
platform == PlatformID.WinCE) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool StartWithCreateProcess (ProcessStartInfo startInfo)
|
|
{
|
|
if (startInfo.StandardOutputEncoding != null && !startInfo.RedirectStandardOutput)
|
|
throw new InvalidOperationException (SR.GetString(SR.StandardOutputEncodingNotAllowed));
|
|
|
|
if (startInfo.StandardErrorEncoding != null && !startInfo.RedirectStandardError)
|
|
throw new InvalidOperationException (SR.GetString(SR.StandardErrorEncodingNotAllowed));
|
|
|
|
if (this.disposed)
|
|
throw new ObjectDisposedException (GetType ().Name);
|
|
|
|
var procInfo = new ProcInfo ();
|
|
|
|
if (startInfo.HaveEnvVars) {
|
|
List<string> envVariables = new List<string> ();
|
|
|
|
foreach (DictionaryEntry de in startInfo.EnvironmentVariables) {
|
|
if (de.Value == null)
|
|
continue;
|
|
|
|
envVariables.Add (string.Concat (
|
|
(string) de.Key,
|
|
"=",
|
|
(string) de.Value));
|
|
}
|
|
|
|
procInfo.envVariables = envVariables.ToArray ();
|
|
}
|
|
|
|
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;
|
|
|
|
try {
|
|
if (startInfo.RedirectStandardInput) {
|
|
CreatePipe (out stdin_read, out stdin_write, true);
|
|
} else {
|
|
stdin_read = MonoIO.ConsoleInput;
|
|
stdin_write = IntPtr.Zero;
|
|
}
|
|
|
|
if (startInfo.RedirectStandardOutput) {
|
|
CreatePipe (out stdout_read, out stdout_write, false);
|
|
} else {
|
|
stdout_read = IntPtr.Zero;
|
|
stdout_write = MonoIO.ConsoleOutput;
|
|
}
|
|
|
|
if (startInfo.RedirectStandardError) {
|
|
CreatePipe (out stderr_read, out stderr_write, false);
|
|
} else {
|
|
stderr_read = IntPtr.Zero;
|
|
stderr_write = MonoIO.ConsoleError;
|
|
}
|
|
|
|
FillUserInfo (startInfo, ref procInfo);
|
|
|
|
//
|
|
// 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 procInfo)) {
|
|
throw new Win32Exception (-procInfo.pid, "ApplicationName='" + startInfo.FileName + "', CommandLine='" + startInfo.Arguments +
|
|
"', CurrentDirectory='" + startInfo.WorkingDirectory + "', Native error= " + Win32Exception.GetErrorMessage (-procInfo.pid));
|
|
}
|
|
} 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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
throw;
|
|
} finally {
|
|
if (procInfo.Password != IntPtr.Zero) {
|
|
Marshal.ZeroFreeBSTR (procInfo.Password);
|
|
procInfo.Password = IntPtr.Zero;
|
|
}
|
|
}
|
|
|
|
SetProcessHandle (new SafeProcessHandle (procInfo.process_handle, true));
|
|
SetProcessId (procInfo.pid);
|
|
|
|
#pragma warning disable 618
|
|
|
|
if (startInfo.RedirectStandardInput) {
|
|
MonoIO.Close (stdin_read, out error);
|
|
|
|
#if MOBILE
|
|
var stdinEncoding = Encoding.Default;
|
|
#else
|
|
var stdinEncoding = Console.InputEncoding;
|
|
#endif
|
|
standardInput = new StreamWriter (new FileStream (stdin_write, FileAccess.Write, true, 8192), stdinEncoding) {
|
|
AutoFlush = true
|
|
};
|
|
}
|
|
|
|
if (startInfo.RedirectStandardOutput) {
|
|
MonoIO.Close (stdout_write, out error);
|
|
|
|
Encoding stdoutEncoding = startInfo.StandardOutputEncoding ?? Console.OutputEncoding;
|
|
|
|
standardOutput = new StreamReader (new FileStream (stdout_read, FileAccess.Read, true, 8192), stdoutEncoding, true);
|
|
}
|
|
|
|
if (startInfo.RedirectStandardError) {
|
|
MonoIO.Close (stderr_write, out error);
|
|
|
|
Encoding stderrEncoding = startInfo.StandardErrorEncoding ?? Console.OutputEncoding;
|
|
|
|
standardError = new StreamReader (new FileStream (stderr_read, FileAccess.Read, true, 8192), stderrEncoding, true);
|
|
}
|
|
#pragma warning restore
|
|
|
|
return true;
|
|
}
|
|
|
|
// Note that ProcInfo.Password must be freed.
|
|
private static void FillUserInfo (ProcessStartInfo startInfo, ref ProcInfo procInfo)
|
|
{
|
|
if (startInfo.UserName.Length != 0) {
|
|
procInfo.UserName = startInfo.UserName;
|
|
procInfo.Domain = startInfo.Domain;
|
|
if (startInfo.Password != null)
|
|
procInfo.Password = Marshal.SecureStringToBSTR (startInfo.Password);
|
|
else
|
|
procInfo.Password = IntPtr.Zero;
|
|
procInfo.LoadUserProfile = startInfo.LoadUserProfile;
|
|
}
|
|
}
|
|
#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
|
|
|
|
#if !MONO_FEATURE_PROCESS_START
|
|
[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
|
|
|
|
/// <devdoc>
|
|
/// Raise the Exited event, but make sure we don't do it more than once.
|
|
/// </devdoc>
|
|
/// <internalonly/>
|
|
void RaiseOnExited() {
|
|
if (!watchForExit)
|
|
return;
|
|
if (!raisedOnExited) {
|
|
lock (this) {
|
|
if (!raisedOnExited) {
|
|
raisedOnExited = true;
|
|
OnExited();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|