File: Microsoft\Win32\SafeHandles\SafeProcessHandle.cs
Web Access
Project: src\src\libraries\System.Diagnostics.Process\src\System.Diagnostics.Process.csproj (System.Diagnostics.Process)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
/*============================================================
**
** Class:  SafeProcessHandle
**
** A wrapper for a process handle
**
**
===========================================================*/
 
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Runtime.InteropServices;
 
namespace Microsoft.Win32.SafeHandles
{
    public sealed partial class SafeProcessHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        internal static readonly SafeProcessHandle InvalidHandle = new SafeProcessHandle();
 
        // Allows for StartWithShellExecute (and its dependencies) to be trimmed when UseShellExecute is not being used.
        // s_startWithShellExecute is defined in platform-specific partial files with OS-appropriate delegate signatures.
        internal static void EnsureShellExecuteFunc() =>
            s_startWithShellExecute ??= StartWithShellExecute;
 
        /// <summary>
        /// Gets the process ID.
        /// </summary>
        public int ProcessId
        {
            get
            {
                Validate();
 
                if (field == -1)
                {
                    field = GetProcessIdCore();
                }
 
                return field;
            }
            private set;
        } = -1;
 
        /// <summary>
        /// Creates a <see cref="T:Microsoft.Win32.SafeHandles.SafeHandle" />.
        /// </summary>
        public SafeProcessHandle()
            : this(IntPtr.Zero)
        {
        }
 
        internal SafeProcessHandle(IntPtr handle)
            : this(handle, true)
        {
        }
 
        /// <summary>
        /// Creates a <see cref="T:Microsoft.Win32.SafeHandles.SafeHandle" /> around a process handle.
        /// </summary>
        /// <param name="existingHandle">Handle to wrap</param>
        /// <param name="ownsHandle">Whether to control the handle lifetime</param>
        public SafeProcessHandle(IntPtr existingHandle, bool ownsHandle)
            : base(ownsHandle)
        {
            SetHandle(existingHandle);
        }
 
        /// <summary>
        /// Starts a process using the specified <see cref="ProcessStartInfo"/>.
        /// </summary>
        /// <param name="startInfo">The process start information.</param>
        /// <returns>A <see cref="SafeProcessHandle"/> representing the started process.</returns>
        /// <remarks>
        /// On Windows, when <see cref="ProcessStartInfo.UseShellExecute"/> is <see langword="true"/>,
        /// the process is started using ShellExecuteEx. In some cases, such as when execution
        /// is satisfied through a DDE conversation, the returned handle will be invalid.
        /// </remarks>
        [UnsupportedOSPlatform("ios")]
        [UnsupportedOSPlatform("tvos")]
        [SupportedOSPlatform("maccatalyst")]
        public static SafeProcessHandle Start(ProcessStartInfo startInfo)
        {
            ArgumentNullException.ThrowIfNull(startInfo);
 
            return Start(startInfo, fallbackToNull: startInfo.StartDetached);
        }
 
        [UnsupportedOSPlatform("ios")]
        [UnsupportedOSPlatform("tvos")]
        [SupportedOSPlatform("maccatalyst")]
        internal static SafeProcessHandle Start(ProcessStartInfo startInfo, bool fallbackToNull)
        {
            startInfo.ThrowIfInvalid(out bool anyRedirection, out SafeHandle[]? inheritedHandles);
 
            if (anyRedirection)
            {
                // Process has .StandardInput, .StandardOutput, or .StandardError APIs that can express
                // redirection of streams, but SafeProcessHandle doesn't.
                // The caller can provide handles via the StandardInputHandle, StandardOutputHandle,
                // and StandardErrorHandle properties.
                throw new InvalidOperationException(SR.CantSetRedirectForSafeProcessHandleStart);
            }
 
            if (!ProcessUtils.PlatformSupportsProcessStartAndKill)
            {
                throw new PlatformNotSupportedException();
            }
 
            SerializationGuard.ThrowIfDeserializationInProgress("AllowProcessCreation", ref ProcessUtils.s_cachedSerializationSwitch);
 
            SafeFileHandle? childInputHandle = startInfo.StandardInputHandle;
            SafeFileHandle? childOutputHandle = startInfo.StandardOutputHandle;
            SafeFileHandle? childErrorHandle = startInfo.StandardErrorHandle;
 
            using SafeFileHandle? nullDeviceHandle = fallbackToNull
                && (childInputHandle is null || childOutputHandle is null || childErrorHandle is null)
                ? File.OpenNullHandle()
                : null;
 
            if (!startInfo.UseShellExecute)
            {
                childInputHandle ??= nullDeviceHandle ?? (ProcessUtils.PlatformSupportsConsole ? Console.OpenStandardInputHandle() : null);
                childOutputHandle ??= nullDeviceHandle ?? (ProcessUtils.PlatformSupportsConsole ? Console.OpenStandardOutputHandle() : null);
                childErrorHandle ??= nullDeviceHandle ?? (ProcessUtils.PlatformSupportsConsole ? Console.OpenStandardErrorHandle() : null);
 
                ProcessStartInfo.ValidateInheritedHandles(childInputHandle, childOutputHandle, childErrorHandle, inheritedHandles);
            }
 
            return StartCore(startInfo, childInputHandle, childOutputHandle, childErrorHandle, inheritedHandles);
        }
 
        /// <summary>
        /// Sends a request to the OS to terminate the process.
        /// </summary>
        /// <remarks>
        /// This method does not throw if the process has already exited.
        /// On Windows, the handle must have <c>PROCESS_TERMINATE</c> access.
        /// </remarks>
        /// <exception cref="InvalidOperationException">The handle is invalid.</exception>
        /// <exception cref="Win32Exception">The process could not be terminated.</exception>
        [UnsupportedOSPlatform("ios")]
        [UnsupportedOSPlatform("tvos")]
        [SupportedOSPlatform("maccatalyst")]
        public void Kill()
        {
            Validate();
            SignalCore(PosixSignal.SIGKILL);
        }
 
        /// <summary>
        /// Sends a signal to the process.
        /// </summary>
        /// <param name="signal">The signal to send.</param>
        /// <returns>
        /// <see langword="true"/> if the signal was sent successfully;
        /// <see langword="false"/> if the process has already exited (or never existed) and the signal was not delivered.
        /// </returns>
        /// <remarks>
        /// On Windows, only <see cref="PosixSignal.SIGKILL"/> is supported and is mapped to <see cref="Kill"/>.
        /// On Windows, the handle must have <c>PROCESS_TERMINATE</c> access.
        /// </remarks>
        /// <exception cref="InvalidOperationException">The handle is invalid.</exception>
        /// <exception cref="PlatformNotSupportedException">The specified signal is not supported on this platform.</exception>
        /// <exception cref="Win32Exception">The signal could not be sent.</exception>
        [UnsupportedOSPlatform("ios")]
        [UnsupportedOSPlatform("tvos")]
        [SupportedOSPlatform("maccatalyst")]
        public bool Signal(PosixSignal signal)
        {
            Validate();
            return SignalCore(signal);
        }
 
        private void Validate()
        {
            if (IsInvalid)
            {
                throw new InvalidOperationException(SR.InvalidProcessHandle);
            }
        }
    }
}