Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
PowerShellProcessInstance.cs285 linesDownload Raw Back to hostifaces
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.ComponentModel;5using System.Diagnostics;6using System.Globalization;7using System.IO;8using System.Management.Automation.Remoting;9using System.Text;10 11namespace System.Management.Automation.Runspaces12{13    /// <summary>14    /// This class represents a PowerShell process that is used for an out-of-process remote Runspace.15    /// </summary>16    public sealed class PowerShellProcessInstance : IDisposable17    {18        #region Fields19 20        private readonly ProcessStartInfo _startInfo;21        private RunspacePool _runspacePool;22        private readonly object _syncObject = new object();23        private bool _started;24        private bool _isDisposed;25        private bool _processExited;26 27        internal static readonly string PwshExePath;28        internal static readonly string WinPwshExePath;29 30        #endregion Fields31 32        #region Constructors33 34        static PowerShellProcessInstance()35        {36#if UNIX37            PwshExePath = Path.Combine(Utils.DefaultPowerShellAppBase, "pwsh");38#else39            PwshExePath = Path.Combine(Utils.DefaultPowerShellAppBase, "pwsh.exe");40            var winPowerShellDir = Utils.GetApplicationBaseFromRegistry(Utils.DefaultPowerShellShellID);41            WinPwshExePath = string.IsNullOrEmpty(winPowerShellDir) ? null : Path.Combine(winPowerShellDir, "powershell.exe");42#endif43        }44 45        /// <summary>46        /// Initializes a new instance of the <see cref="PowerShellProcessInstance"/> class. Initializes the underlying dotnet process class.47        /// </summary>48        /// <param name="powerShellVersion">Specifies the version of powershell.</param>49        /// <param name="credential">Specifies a user account credentials.</param>50        /// <param name="initializationScript">Specifies a script that will be executed when the powershell process is initialized.</param>51        /// <param name="useWow64">Specifies if the powershell process will be 32-bit.</param>52        /// <param name="workingDirectory">Specifies the initial working directory for the new powershell process.</param>53        public PowerShellProcessInstance(Version powerShellVersion, PSCredential credential, ScriptBlock initializationScript, bool useWow64, string workingDirectory)54        {55            string exePath = PwshExePath;56            bool startingWindowsPowerShell51 = false;57#if !UNIX58            // if requested PS version was "5.1" then we start Windows PS instead of PS Core59            startingWindowsPowerShell51 = (powerShellVersion != null) && (powerShellVersion.Major == 5) && (powerShellVersion.Minor == 1);60            if (startingWindowsPowerShell51)61            {62                if (WinPwshExePath == null)63                {64                    throw new PSInvalidOperationException(RemotingErrorIdStrings.WindowsPowerShellNotPresent);65                }66 67                exePath = WinPwshExePath;68 69                if (useWow64)70                {71                    string procArch = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");72 73                    if ((!string.IsNullOrEmpty(procArch)) && (procArch.Equals("amd64", StringComparison.OrdinalIgnoreCase) ||74                        procArch.Equals("ia64", StringComparison.OrdinalIgnoreCase)))75                    {76                        exePath = WinPwshExePath.ToLowerInvariant().Replace("\\system32\\", "\\syswow64\\");77 78                        if (!File.Exists(exePath))79                        {80                            string message = PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.WowComponentNotPresent, exePath);81                            throw new PSInvalidOperationException(message);82                        }83                    }84                }85            }86#endif87            // 'WindowStyle' is used only if 'UseShellExecute' is 'true'. Since 'UseShellExecute' is set88            // to 'false' in our use, we can ignore the 'WindowStyle' setting in the initialization below.89            _startInfo = new ProcessStartInfo90            {91                FileName = exePath,92                UseShellExecute = false,93                RedirectStandardInput = true,94                RedirectStandardOutput = true,95                RedirectStandardError = true,96                CreateNoWindow = true,97#if !UNIX98                LoadUserProfile = true,99#endif100            };101#if !UNIX102            if (startingWindowsPowerShell51)103            {104                _startInfo.ArgumentList.Add("-Version");105                _startInfo.ArgumentList.Add("5.1");106 107                // if starting Windows PowerShell, need to remove PowerShell specific segments of PSModulePath108                _startInfo.Environment["PSModulePath"] = ModuleIntrinsics.GetWindowsPowerShellModulePath();109            }110#endif111            _startInfo.ArgumentList.Add("-s");112            _startInfo.ArgumentList.Add("-NoLogo");113            _startInfo.ArgumentList.Add("-NoProfile");114 115            if (!string.IsNullOrWhiteSpace(workingDirectory) && !startingWindowsPowerShell51)116            {117                _startInfo.ArgumentList.Add("-wd");118                _startInfo.ArgumentList.Add(workingDirectory);119            }120 121            if (initializationScript != null)122            {123                var scriptBlockString = initializationScript.ToString();124                if (!string.IsNullOrEmpty(scriptBlockString))125                {126                    var encodedCommand = Convert.ToBase64String(Encoding.Unicode.GetBytes(scriptBlockString));127                    _startInfo.ArgumentList.Add("-EncodedCommand");128                    _startInfo.ArgumentList.Add(encodedCommand);129                }130            }131 132            if (credential != null)133            {134                Net.NetworkCredential netCredential = credential.GetNetworkCredential();135 136                _startInfo.UserName = netCredential.UserName;137                _startInfo.Domain = string.IsNullOrEmpty(netCredential.Domain) ? "." : netCredential.Domain;138                _startInfo.Password = credential.Password;139            }140 141            Process = new Process { StartInfo = _startInfo, EnableRaisingEvents = true };142        }143 144        /// <summary>145        /// Initializes a new instance of the <see cref="PowerShellProcessInstance"/> class. Initializes the underlying dotnet process class.146        /// </summary>147        /// <param name="powerShellVersion">Specifies the version of powershell.</param>148        /// <param name="credential">Specifies a user account credentials.</param>149        /// <param name="initializationScript">Specifies a script that will be executed when the powershell process is initialized.</param>150        /// <param name="useWow64">Specifies if the powershell process will be 32-bit.</param>151        public PowerShellProcessInstance(Version powerShellVersion, PSCredential credential, ScriptBlock initializationScript, bool useWow64) : this(powerShellVersion, credential, initializationScript, useWow64, workingDirectory: null)152        {153        }154 155        /// <summary>156        /// Initializes a new instance of the <see cref="PowerShellProcessInstance"/> class. Default initializes the underlying dotnet process class.157        /// </summary>158        public PowerShellProcessInstance() : this(powerShellVersion: null, credential: null, initializationScript: null, useWow64: false, workingDirectory: null)159        {160        }161 162        /// <summary>163        /// Gets a value indicating whether the associated process has been terminated.164        /// true if the operating system process referenced by the Process component has terminated; otherwise, false.165        /// </summary>166        public bool HasExited167        {168            get169            {170                // When process is exited, there is some delay in receiving ProcessExited event and HasExited property on process object.171                // Using HasExited property on started process object to determine if powershell process has exited.172                //173                return _processExited || (_started && Process != null && Process.HasExited);174            }175        }176 177        #endregion Constructors178 179        #region Dispose180 181        /// <summary>182        /// Release all resources.183        /// </summary>184        public void Dispose()185        {186            if (_isDisposed)187            {188                return;189            }190 191            lock (_syncObject)192            {193                if (_isDisposed)194                {195                    return;196                }197 198                _isDisposed = true;199            }200            201            try202            {203                if (Process != null && !Process.HasExited)204                    Process.Kill();205            }206            catch (InvalidOperationException)207            {208            }209            catch (Win32Exception)210            {211            }212            catch (NotSupportedException)213            {214            }215        }216 217        #endregion Dispose218 219        #region Public Properties220 221        /// <summary>222        /// Gets the process object of the remote target.223        /// </summary>224        public Process Process { get; }225 226        #endregion Public Properties227 228        #region Internal Members229 230        internal RunspacePool RunspacePool231        {232            get233            {234                lock (_syncObject)235                {236                    return _runspacePool;237                }238            }239 240            set241            {242                lock (_syncObject)243                {244                    _runspacePool = value;245                }246            }247        }248 249        internal OutOfProcessTextWriter StdInWriter { get; set; }250 251        internal void Start()252        {253            // To fix the deadlock, we should not call Process.HasExited by holding the sync lock as Process.HasExited can raise ProcessExited event254            //255            if (HasExited)256            {257                throw new InvalidOperationException();258            }259 260            lock (_syncObject)261            {262                if (_started)263                {264                    return;265                }266 267                _started = true;268                Process.Exited += ProcessExited;269            }270 271            Process.Start();272        }273 274        private void ProcessExited(object sender, EventArgs e)275        {276            lock (_syncObject)277            {278                _processExited = true;279            }280        }281 282        #endregion Internal Members283    }284}285