MegaBites-AI/Windows-powershell
0308
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections;6using System.Collections.Generic;7using System.Dynamic;8using System.Globalization;9using System.Linq.Expressions;10using System.Management.Automation;11using System.Management.Automation.Internal;12using System.Management.Automation.Language;13using System.Management.Automation.PSTasks;14using System.Management.Automation.Security;15using System.Runtime.CompilerServices;16using System.Text;17using System.Threading;18 19using CommonParamSet = System.Management.Automation.Internal.CommonParameters;20using Dbg = System.Management.Automation.Diagnostics;21using NotNullWhen = System.Diagnostics.CodeAnalysis.NotNullWhenAttribute;22 23namespace Microsoft.PowerShell.Commands24{25 /// <summary>26 /// A thin wrapper over a property-getting Callsite, to allow reuse when possible.27 /// </summary>28 internal struct DynamicPropertyGetter29 {30 private CallSite<Func<CallSite, object, object>> _getValueDynamicSite;31 32 // For the wildcard case, lets us know if we can reuse the callsite:33 private string _lastUsedPropertyName;34 35 public object GetValue(PSObject inputObject, string propertyName)36 {37 Dbg.Assert(!WildcardPattern.ContainsWildcardCharacters(propertyName), "propertyName should be pre-resolved by caller");38 39 // If wildcards are involved, the resolved property name could potentially40 // be different on every object... but probably not, so we'll attempt to41 // reuse the callsite if possible.42 if (!propertyName.Equals(_lastUsedPropertyName, StringComparison.OrdinalIgnoreCase))43 {44 _lastUsedPropertyName = propertyName;45 _getValueDynamicSite = CallSite<Func<CallSite, object, object>>.Create(46 PSGetMemberBinder.Get(47 propertyName,48 classScope: (Type)null,49 @static: false));50 }51 52 return _getValueDynamicSite.Target.Invoke(_getValueDynamicSite, inputObject);53 }54 }55 56 #region Built-in cmdlets that are used by or require direct access to the engine.57 58 /// <summary>59 /// Implements a cmdlet that applies a script block60 /// to each element of the pipeline.61 /// </summary>62 [Cmdlet("ForEach", "Object", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Low,63 DefaultParameterSetName = ForEachObjectCommand.ScriptBlockSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096867",64 RemotingCapability = RemotingCapability.None)]65 public sealed class ForEachObjectCommand : PSCmdlet, IDisposable66 {67 #region Private Members68 69 private const string ParallelParameterSet = "ParallelParameterSet";70 private const string ScriptBlockSet = "ScriptBlockSet";71 private const string PropertyAndMethodSet = "PropertyAndMethodSet";72 73 #endregion74 75 #region Common Parameters76 77 /// <summary>78 /// This parameter specifies the current pipeline object.79 /// </summary>80 [Parameter(ValueFromPipeline = true, ParameterSetName = ForEachObjectCommand.ScriptBlockSet)]81 [Parameter(ValueFromPipeline = true, ParameterSetName = ForEachObjectCommand.PropertyAndMethodSet)]82 [Parameter(ValueFromPipeline = true, ParameterSetName = ForEachObjectCommand.ParallelParameterSet)]83 public PSObject InputObject84 {85 get { return _inputObject; }86 87 set { _inputObject = value; }88 }89 90 private PSObject _inputObject = AutomationNull.Value;91 92 #endregion93 94 #region ScriptBlockSet95 96 private readonly List<ScriptBlock> _scripts = new List<ScriptBlock>();97 98 /// <summary>99 /// Gets or sets the script block to apply in begin processing.100 /// </summary>101 [Parameter(ParameterSetName = ForEachObjectCommand.ScriptBlockSet)]102 public ScriptBlock Begin103 {104 get105 {106 return null;107 }108 109 set110 {111 _scripts.Insert(0, value);112 }113 }114 115 /// <summary>116 /// Gets or sets the script block to apply.117 /// </summary>118 [Parameter(Mandatory = true, Position = 0, ParameterSetName = ForEachObjectCommand.ScriptBlockSet)]119 [AllowNull]120 [AllowEmptyCollection]121 public ScriptBlock[] Process122 {123 get124 {125 return null;126 }127 128 set129 {130 if (value == null)131 {132 _scripts.Add(null);133 }134 else135 {136 _scripts.AddRange(value);137 }138 }139 }140 141 private ScriptBlock _endScript;142 private bool _setEndScript;143 144 /// <summary>145 /// Gets or sets the script block to apply in complete processing.146 /// </summary>147 [Parameter(ParameterSetName = ForEachObjectCommand.ScriptBlockSet)]148 public ScriptBlock End149 {150 get151 {152 return _endScript;153 }154 155 set156 {157 _endScript = value;158 _setEndScript = true;159 }160 }161 162 /// <summary>163 /// Gets or sets the remaining script blocks to apply.164 /// </summary>165 [Parameter(ParameterSetName = ForEachObjectCommand.ScriptBlockSet, ValueFromRemainingArguments = true)]166 [AllowNull]167 [AllowEmptyCollection]168 public ScriptBlock[] RemainingScripts169 {170 get171 {172 return null;173 }174 175 set176 {177 if (value == null)178 {179 _scripts.Add(null);180 }181 else182 {183 _scripts.AddRange(value);184 }185 }186 }187 188 private int _start, _end;189 190 #endregion ScriptBlockSet191 192 #region PropertyAndMethodSet193 194 /// <summary>195 /// Gets or sets the property or method name.196 /// </summary>197 [Parameter(Mandatory = true, Position = 0, ParameterSetName = ForEachObjectCommand.PropertyAndMethodSet)]198 [ValidateTrustedData]199 [ValidateNotNullOrEmpty]200 public string MemberName201 {202 get203 {204 return _propertyOrMethodName;205 }206 207 set208 {209 _propertyOrMethodName = value;210 }211 }212 213 private string _propertyOrMethodName;214 private string _targetString;215 private DynamicPropertyGetter _propGetter;216 217 /// <summary>218 /// The arguments passed to a method invocation.219 /// </summary>220 [Parameter(ParameterSetName = ForEachObjectCommand.PropertyAndMethodSet, ValueFromRemainingArguments = true)]221 [ValidateTrustedData]222 [Alias("Args")]223 public object[] ArgumentList224 {225 get { return _arguments; }226 227 set { _arguments = value; }228 }229 230 private object[] _arguments;231 232 #endregion PropertyAndMethodSet233 234 #region ParallelParameterSet235 236 /// <summary>237 /// Gets or sets a script block to run in parallel for each pipeline object.238 /// </summary>239 [Parameter(Mandatory = true, ParameterSetName = ForEachObjectCommand.ParallelParameterSet)]240 public ScriptBlock Parallel { get; set; }241 242 /// <summary>243 /// Gets or sets the maximum number of concurrently running scriptblocks on separate threads.244 /// The default number is 5.245 /// </summary>246 [Parameter(ParameterSetName = ForEachObjectCommand.ParallelParameterSet)]247 [ValidateRange(1, Int32.MaxValue)]248 public int ThrottleLimit { get; set; } = 5;249 250 /// <summary>251 /// Gets or sets a timeout time in seconds, after which the parallel running scripts will be stopped252 /// The default value is 0, indicating no timeout.253 /// </summary>254 [Parameter(ParameterSetName = ForEachObjectCommand.ParallelParameterSet)]255 [ValidateRange(0, (Int32.MaxValue / 1000))]256 public int TimeoutSeconds { get; set; }257 258 /// <summary>259 /// Gets or sets a flag that returns a job object immediately for the parallel operation, instead of returning after260 /// all foreach processing is completed.261 /// </summary>262 [Parameter(ParameterSetName = ForEachObjectCommand.ParallelParameterSet)]263 public SwitchParameter AsJob { get; set; }264 265 /// <summary>266 /// Gets or sets a flag so that a new runspace object is created for each loop iteration, instead of reusing objects267 /// from the runspace pool.268 /// By default, runspaces are reused from a runspace pool.269 /// </summary>270 [Parameter(ParameterSetName = ForEachObjectCommand.ParallelParameterSet)]271 public SwitchParameter UseNewRunspace { get; set; }272 273 #endregion274 275 #region Overrides276 277 /// <summary>278 /// Execute the begin scriptblock at the start of processing.279 /// </summary>280 /// <exception cref="ParseException">Could not parse script.</exception>281 /// <exception cref="RuntimeException">See Pipeline.Invoke.</exception>282 /// <exception cref="ParameterBindingException">See Pipeline.Invoke.</exception>283 protected override void BeginProcessing()284 {285 switch (ParameterSetName)286 {287 case ForEachObjectCommand.ScriptBlockSet:288 InitScriptBlockParameterSet();289 break;290 291 case ForEachObjectCommand.ParallelParameterSet:292 InitParallelParameterSet();293 break;294 }295 }296 297 /// <summary>298 /// Execute the processing script blocks on the current pipeline object299 /// which is passed as it's only parameter.300 /// </summary>301 /// <exception cref="ParseException">Could not parse script.</exception>302 /// <exception cref="RuntimeException">See Pipeline.Invoke.</exception>303 /// <exception cref="ParameterBindingException">See Pipeline.Invoke.</exception>304 protected override void ProcessRecord()305 {306 switch (ParameterSetName)307 {308 case ForEachObjectCommand.ScriptBlockSet:309 ProcessScriptBlockParameterSet();310 break;311 312 case ForEachObjectCommand.PropertyAndMethodSet:313 ProcessPropertyAndMethodParameterSet();314 break;315 316 case ForEachObjectCommand.ParallelParameterSet:317 ProcessParallelParameterSet();318 break;319 }320 }321 322 /// <summary>323 /// Execute the end scriptblock when the pipeline is complete.324 /// </summary>325 /// <exception cref="ParseException">Could not parse script.</exception>326 /// <exception cref="RuntimeException">See Pipeline.Invoke.</exception>327 /// <exception cref="ParameterBindingException">See Pipeline.Invoke.</exception>328 protected override void EndProcessing()329 {330 switch (ParameterSetName)331 {332 case ForEachObjectCommand.ScriptBlockSet:333 EndBlockParameterSet();334 break;335 336 case ForEachObjectCommand.ParallelParameterSet:337 EndParallelParameterSet();338 break;339 }340 }341 342 /// <summary>343 /// Handle pipeline stop signal.344 /// </summary>345 protected override void StopProcessing()346 {347 switch (ParameterSetName)348 {349 case ForEachObjectCommand.ParallelParameterSet:350 StopParallelProcessing();351 break;352 }353 }354 355 #endregion356 357 #region IDisposable358 359 /// <summary>360 /// Dispose cmdlet instance.361 /// </summary>362 public void Dispose()363 {364 // Ensure all parallel task objects are disposed365 _taskTimer?.Dispose();366 _taskDataStreamWriter?.Dispose();367 _taskPool?.Dispose();368 _taskCollection?.Dispose();369 }370 371 #endregion372 373 #region Private Methods374 375 #region PSTasks376 377 private PSTaskPool _taskPool;378 private PSTaskDataStreamWriter _taskDataStreamWriter;379 private Dictionary<string, object> _usingValuesMap;380 private Timer _taskTimer;381 private PSTaskJob _taskJob;382 private PSDataCollection<System.Management.Automation.PSTasks.PSTask> _taskCollection;383 private Exception _taskCollectionException;384 private string _currentLocationPath;385 386 private void InitParallelParameterSet()387 {388 // The following common parameters are not (yet) supported in this parameter set.389 // ErrorAction, WarningAction, InformationAction, ProgressAction, PipelineVariable.390 if (MyInvocation.BoundParameters.ContainsKey(nameof(CommonParamSet.ErrorAction)) ||391 MyInvocation.BoundParameters.ContainsKey(nameof(CommonParamSet.WarningAction)) ||392 MyInvocation.BoundParameters.ContainsKey(nameof(CommonParamSet.InformationAction)) ||393 MyInvocation.BoundParameters.ContainsKey(nameof(CommonParamSet.ProgressAction)) ||394 MyInvocation.BoundParameters.ContainsKey(nameof(CommonParamSet.PipelineVariable)))395 {396 ThrowTerminatingError(397 new ErrorRecord(398 new PSNotSupportedException(InternalCommandStrings.ParallelCommonParametersNotSupported),399 "ParallelCommonParametersNotSupported",400 ErrorCategory.NotImplemented,401 this));402 }403 404 // Get the current working directory location, if available.405 try406 {407 _currentLocationPath = SessionState.Internal.CurrentLocation.Path;408 }409 catch (PSInvalidOperationException)410 {411 }412 413 var allowUsingExpression = this.Context.SessionState.LanguageMode != PSLanguageMode.NoLanguage;414 _usingValuesMap = ScriptBlockToPowerShellConverter.GetUsingValuesForEachParallel(415 scriptBlock: Parallel,416 isTrustedInput: allowUsingExpression,417 context: this.Context);418 419 // Validate using values map, which is a map of '$using:' variables referenced in the script.420 // Script block variables are not allowed since their behavior is undefined outside the runspace421 // in which they were created.422 foreach (object item in _usingValuesMap.Values)423 {424 if (item is ScriptBlock or PSObject { BaseObject: ScriptBlock })425 {426 ThrowTerminatingError(427 new ErrorRecord(428 new PSArgumentException(InternalCommandStrings.ParallelUsingVariableCannotBeScriptBlock),429 "ParallelUsingVariableCannotBeScriptBlock",430 ErrorCategory.InvalidType,431 this));432 }433 }434 435 if (AsJob)436 {437 // Set up for returning a job object.438 if (MyInvocation.BoundParameters.ContainsKey(nameof(TimeoutSeconds)))439 {440 ThrowTerminatingError(441 new ErrorRecord(442 new PSArgumentException(InternalCommandStrings.ParallelCannotUseTimeoutWithJob),443 "ParallelCannotUseTimeoutWithJob",444 ErrorCategory.InvalidOperation,445 this));446 }447 448 _taskJob = new PSTaskJob(449 Parallel.ToString(),450 ThrottleLimit,451 UseNewRunspace);452 453 return;454 }455 456 // Set up for synchronous processing and data streaming.457 _taskCollection = new PSDataCollection<System.Management.Automation.PSTasks.PSTask>();458 _taskDataStreamWriter = new PSTaskDataStreamWriter(this);459 _taskPool = new PSTaskPool(ThrottleLimit, UseNewRunspace);460 _taskPool.PoolComplete += (sender, args) => _taskDataStreamWriter.Close();461 462 // Create timeout timer if requested.463 if (TimeoutSeconds != 0)464 {465 _taskTimer = new Timer(466 callback: (_) => { _taskCollection.Complete(); _taskPool.StopAll(); },467 state: null,468 dueTime: TimeoutSeconds * 1000,469 period: Timeout.Infinite);470 }471 472 // Task collection handler.473 System.Threading.ThreadPool.QueueUserWorkItem(474 (_) =>475 {476 // As piped input are converted to PSTasks and added to the _taskCollection,477 // transfer the task to the _taskPool on this dedicated thread.478 // The _taskPool will block this thread when it is full, and allow more tasks to479 // be added only when a currently running task completes and makes space in the pool.480 // Continue adding any tasks appearing in _taskCollection until the collection is closed.481 while (true)482 {483 // This handle will unblock the thread when a new task is available or the _taskCollection484 // is closed.485 _taskCollection.WaitHandle.WaitOne();486 487 // Task collection open state is volatile.488 // Record current task collection open state here, to be checked after processing.489 bool isOpen = _taskCollection.IsOpen;490 491 try492 {493 // Read all tasks in the collection.494 foreach (var task in _taskCollection.ReadAll())495 {496 // This _taskPool method will block if the pool is full and will unblock497 // only after a task completes making more space.498 _taskPool.Add(task);499 }500 }501 catch (Exception ex)502 {503 _taskCollection.Complete();504 _taskCollectionException = ex;505 _taskDataStreamWriter.Close();506 507 break;508 }509 510 // Loop is exited only when task collection is closed and all task511 // collection tasks are processed.512 if (!isOpen)513 {514 break;515 }516 }517 518 // We are done adding tasks and can close the task pool.519 _taskPool.Close();520 });521 }522 523 private void ProcessParallelParameterSet()524 {525 // Validate piped InputObject526 if (_inputObject != null &&527 _inputObject.BaseObject is ScriptBlock)528 {529 WriteError(530 new ErrorRecord(531 new PSArgumentException(InternalCommandStrings.ParallelPipedInputObjectCannotBeScriptBlock),532 "ParallelPipedInputObjectCannotBeScriptBlock",533 ErrorCategory.InvalidType,534 this));535 536 return;537 }538 539 if (AsJob)540 {541 // Add child task job.542 var taskChildJob = new PSTaskChildJob(543 Parallel,544 _usingValuesMap,545 InputObject,546 _currentLocationPath);547 548 _taskJob.AddJob(taskChildJob);549 550 return;551 }552 553 // Write any streaming data554 _taskDataStreamWriter.WriteImmediate();555 556 // Add to task collection for processing.557 if (_taskCollection.IsOpen)558 {559 try560 {561 // Create a PSTask based on this piped input and add it to the task collection.562 // A dedicated thread will add it to the PSTask pool in a performant manner.563 _taskCollection.Add(564 new System.Management.Automation.PSTasks.PSTask(565 Parallel,566 _usingValuesMap,567 InputObject,568 _currentLocationPath,569 _taskDataStreamWriter));570 }571 catch (InvalidOperationException)572 {573 // This exception is thrown if the task collection is closed, which should not happen.574 Dbg.Assert(false, "Should not add to a closed PSTask collection");575 }576 }577 }578 579 private void EndParallelParameterSet()580 {581 if (AsJob)582 {583 // Start and return parent job object.584 _taskJob.Start();585 JobRepository.Add(_taskJob);586 WriteObject(_taskJob);587 588 return;589 }590 591 // Close task collection and wait for processing to complete while streaming data.592 _taskDataStreamWriter.WriteImmediate();593 _taskCollection.Complete();594 _taskDataStreamWriter.WaitAndWrite();595 596 // Check for an unexpected error from the _taskCollection handler thread and report here.597 var ex = _taskCollectionException;598 if (ex != null)599 {600 var msg = string.Format(CultureInfo.InvariantCulture, InternalCommandStrings.ParallelPipedInputProcessingError, ex);601 WriteError(602 new ErrorRecord(603 exception: new InvalidOperationException(msg),604 errorId: "ParallelPipedInputProcessingError",605 errorCategory: ErrorCategory.InvalidOperation,606 targetObject: this));607 }608 }609 610 private void StopParallelProcessing()611 {612 _taskCollection?.Complete();613 _taskPool?.StopAll();614 }615 616 #endregion617 618 private void EndBlockParameterSet()619 {620 if (_endScript == null)621 {622 return;623 }624 625 var emptyArray = Array.Empty<object>();626 _endScript.InvokeUsingCmdlet(627 contextCmdlet: this,628 useLocalScope: false,629 errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe,630 dollarUnder: AutomationNull.Value,631 input: emptyArray,632 scriptThis: AutomationNull.Value,633 args: emptyArray);634 }635 636 private void ProcessPropertyAndMethodParameterSet()637 {638 _targetString = string.Format(CultureInfo.InvariantCulture, InternalCommandStrings.ForEachObjectTarget, GetStringRepresentation(InputObject));639 640 if (LanguagePrimitives.IsNull(InputObject))641 {642 if (_arguments != null && _arguments.Length > 0)643 {644 WriteError(GenerateNameParameterError("InputObject", ParserStrings.InvokeMethodOnNull,645 "InvokeMethodOnNull", _inputObject));646 }647 else648 {649 // should process650 string propertyAction = string.Format(CultureInfo.InvariantCulture,651 InternalCommandStrings.ForEachObjectPropertyAction, _propertyOrMethodName);652 653 if (ShouldProcess(_targetString, propertyAction))654 {655 if (Context.IsStrictVersion(2))656 {657 WriteError(GenerateNameParameterError("InputObject", InternalCommandStrings.InputObjectIsNull,658 "InputObjectIsNull", _inputObject));659 }660 else661 {662 // we write null out because:663 // PS C:\> $null | ForEach-object {$_.aa} | ForEach-Object {$_ + 3}664 // 3665 // so we also want666 // PS C:\> $null | ForEach-object aa | ForEach-Object {$_ + 3}667 // 3668 // But if we don't write anything to the pipeline when _inputObject is null,669 // the result 3 will not be generated.670 WriteObject(null);671 }672 }673 }674 675 return;676 }677 678 ErrorRecord errorRecord = null;679 680 // if args exist, this is explicitly a method invocation681 if (_arguments != null && _arguments.Length > 0)682 {683 MethodCallWithArguments();684 }685 // no arg provided686 else687 {688 // if inputObject is of IDictionary, get the value689 if (GetValueFromIDictionaryInput())690 {691 return;692 }693 694 PSMemberInfo member = null;695 if (WildcardPattern.ContainsWildcardCharacters(_propertyOrMethodName))696 {697 // get the matched member(s)698 ReadOnlyPSMemberInfoCollection<PSMemberInfo> members =699 _inputObject.Members.Match(_propertyOrMethodName, PSMemberTypes.All);700 Dbg.Assert(members != null, "The return value of Members.Match should never be null");701 702 if (members.Count > 1)703 {704 // write error record: property method ambiguous705 StringBuilder possibleMatches = new StringBuilder();706 foreach (PSMemberInfo item in members)707 {708 possibleMatches.Append(CultureInfo.InvariantCulture, $" {item.Name}");709 }710 711 WriteError(GenerateNameParameterError("Name", InternalCommandStrings.AmbiguousPropertyOrMethodName,712 "AmbiguousPropertyOrMethodName", _inputObject,713 _propertyOrMethodName, possibleMatches));714 return;715 }716 717 if (members.Count == 1)718 {719 member = members[0];720 }721 }722 else723 {724 member = _inputObject.Members[_propertyOrMethodName];725 }726 727 // member is a method728 if (member is PSMethodInfo)729 {730 // first we check if the member is a ParameterizedProperty731 PSParameterizedProperty targetParameterizedProperty = member as PSParameterizedProperty;732 if (targetParameterizedProperty != null)733 {734 // should process735 string propertyAction = string.Format(CultureInfo.InvariantCulture,736 InternalCommandStrings.ForEachObjectPropertyAction, targetParameterizedProperty.Name);737 738 // ParameterizedProperty always take parameters, so we output the member.Value directly739 if (ShouldProcess(_targetString, propertyAction))740 {741 WriteObject(member.Value);742 }743 744 return;745 }746 747 PSMethodInfo targetMethod = member as PSMethodInfo;748 Dbg.Assert(targetMethod != null, "targetMethod should not be null here.");749 try750 {751 // should process752 string methodAction = string.Format(CultureInfo.InvariantCulture,753 InternalCommandStrings.ForEachObjectMethodActionWithoutArguments, targetMethod.Name);754 755 if (ShouldProcess(_targetString, methodAction))756 {757 if (!BlockMethodInLanguageMode(InputObject))758 {759 object result = targetMethod.Invoke(Array.Empty<object>());760 WriteToPipelineWithUnrolling(result);761 }762 }763 }764 catch (PipelineStoppedException)765 {766 // PipelineStoppedException can be caused by select-object767 throw;768 }769 catch (Exception ex)770 {771 MethodException mex = ex as MethodException;772 if (mex != null && mex.ErrorRecord != null && mex.ErrorRecord.FullyQualifiedErrorId == "MethodCountCouldNotFindBest")773 {774 WriteObject(targetMethod.Value);775 }776 else777 {778 WriteError(new ErrorRecord(ex, "MethodInvocationError", ErrorCategory.InvalidOperation, _inputObject));779 }780 }781 }782 else783 {784 string resolvedPropertyName = null;785 bool isBlindDynamicAccess = false;786 if (member == null)787 {788 if ((_inputObject.BaseObject is IDynamicMetaObjectProvider) &&789 !WildcardPattern.ContainsWildcardCharacters(_propertyOrMethodName))790 {791 // Let's just try a dynamic property access. Note that if it792 // comes to depending on dynamic access, we are assuming it is a793 // property; we don't have ETS info to tell us up front if it794 // even exists or not, let alone if it is a method or something795 // else.796 //797 // Note that this is "truly blind"--the name did not show up in798 // GetDynamicMemberNames(), else it would show up as a dynamic799 // member.800 801 resolvedPropertyName = _propertyOrMethodName;802 isBlindDynamicAccess = true;803 }804 else805 {806 errorRecord = GenerateNameParameterError("Name", InternalCommandStrings.PropertyOrMethodNotFound,807 "PropertyOrMethodNotFound", _inputObject,808 _propertyOrMethodName);809 }810 }811 else812 {813 // member is [presumably] a property (note that it could be a814 // dynamic property, if it shows up in GetDynamicMemberNames())815 resolvedPropertyName = member.Name;816 }817 818 if (!string.IsNullOrEmpty(resolvedPropertyName))819 {820 // should process821 string propertyAction = string.Format(CultureInfo.InvariantCulture,822 InternalCommandStrings.ForEachObjectPropertyAction, resolvedPropertyName);823 824 if (ShouldProcess(_targetString, propertyAction))825 {826 try827 {828 WriteToPipelineWithUnrolling(_propGetter.GetValue(InputObject, resolvedPropertyName));829 }830 catch (TerminateException) // The debugger is terminating the execution831 {832 throw;833 }834 catch (MethodException)835 {836 throw;837 }838 catch (PipelineStoppedException)839 {840 // PipelineStoppedException can be caused by select-object841 throw;842 }843 catch (Exception ex)844 {845 // For normal property accesses, we do not generate an error846 // here. The problem for truly blind dynamic accesses (the847 // member did not show up in GetDynamicMemberNames) is that848 // we can't tell the difference between "it failed because849 // the property does not exist" (let's call this case 1) and850 // "it failed because accessing it actually threw some851 // exception" (let's call that case 2).852 //853 // PowerShell behavior for normal (non-dynamic) properties854 // is different for these two cases: case 1 gets an error855 // (which is possible because the ETS tells us up front if856 // the property exists or not), and case 2 does not. (For857 // normal properties, this catch block /is/ case 2.)858 //859 // For IDMOPs, we have the chance to attempt a "blind"860 // access, but the cost is that we must have the same861 // response to both cases (because we cannot distinguish862 // between the two). So we have to make a choice: we can863 // either swallow ALL errors (including "The property864 // 'Blarg' does not exist"), or expose them all.865 //866 // Here, for truly blind dynamic access, we choose to867 // preserve the behavior of showing "The property 'Blarg'868 // does not exist" (case 1) errors than to suppress869 // "FooException thrown when accessing Bloop property" (case870 // 2) errors.871 872 if (isBlindDynamicAccess)873 {874 errorRecord = new ErrorRecord(ex,875 "DynamicPropertyAccessFailed_" + _propertyOrMethodName,876 ErrorCategory.InvalidOperation,877 InputObject);878 }879 else880 {881 // When the property is not gettable or it throws an exception.882 // e.g. when trying to access an assembly's location property, since dynamic assemblies are not backed up by a file,883 // an exception will be thrown when accessing its location property. In this case, return null.884 WriteObject(null);885 }886 }887 }888 }889 }890 }891 892 if (errorRecord != null)893 {894 string propertyAction = string.Format(CultureInfo.InvariantCulture,895 InternalCommandStrings.ForEachObjectPropertyAction, _propertyOrMethodName);896 897 if (ShouldProcess(_targetString, propertyAction))898 {899 if (Context.IsStrictVersion(2))900 {901 WriteError(errorRecord);902 }903 else904 {905 // we write null out because:906 // PS C:\> "string" | ForEach-Object {$_.aa} | ForEach-Object {$_ + 3}907 // 3908 // so we also want909 // PS C:\> "string" | ForEach-Object aa | ForEach-Object {$_ + 3}910 // 3911 // But if we don't write anything to the pipeline when no member is found,912 // the result 3 will not be generated.913 WriteObject(null);914 }915 }916 }917 }918 919 private void ProcessScriptBlockParameterSet()920 {921 for (int i = _start; i < _end; i++)922 {923 // Only execute scripts that aren't null. This isn't treated as an error924 // because it allows you to parameterize a command - for example you might allow925 // for actions before and after the main processing script. They could be null926 // by default and therefore ignored then filled in later...927 _scripts[i]?.InvokeUsingCmdlet(928 contextCmdlet: this,929 useLocalScope: false,930 errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe,931 dollarUnder: InputObject,932 input: new object[] { InputObject },933 scriptThis: AutomationNull.Value,934 args: Array.Empty<object>());935 }936 }937 938 private void InitScriptBlockParameterSet()939 {940 // Win8: 176403: ScriptCmdlets sets the global WhatIf and Confirm preferences941 // This effects the new W8 foreach-object cmdlet with -whatif and -confirm942 // implemented. -whatif and -confirm needed only for PropertyAndMethodSet943 // parameter set. So erring out in cases where these are used with ScriptBlockSet.944 // Not using MshCommandRuntime, as those variables will be affected by ScriptCmdlet945 // infrastructure (wherein ScriptCmdlet modifies the global preferences).946 Dictionary<string, object> psBoundParameters = this.MyInvocation.BoundParameters;947 if (psBoundParameters != null)948 {949 SwitchParameter whatIf = false;950 SwitchParameter confirm = false;951 952 object argument;953 if (psBoundParameters.TryGetValue("whatif", out argument))954 {955 whatIf = (SwitchParameter)argument;956 }957 958 if (psBoundParameters.TryGetValue("confirm", out argument))959 {960 confirm = (SwitchParameter)argument;961 }962 963 if (whatIf || confirm)964 {965 string message = InternalCommandStrings.NoShouldProcessForScriptBlockSet;966 967 ErrorRecord errorRecord = new ErrorRecord(968 new InvalidOperationException(message),969 "NoShouldProcessForScriptBlockSet",970 ErrorCategory.InvalidOperation,971 null);972 ThrowTerminatingError(errorRecord);973 }974 }975 976 // Calculate the start and end indexes for the processRecord script blocks977 _end = _scripts.Count;978 _start = _scripts.Count > 1 ? 1 : 0;979 980 // and set the end script if it wasn't explicitly set with a named parameter.981 if (!_setEndScript)982 {983 if (_scripts.Count > 2)984 {985 _end = _scripts.Count - 1;986 _endScript = _scripts[_end];987 }988 }989 990 // only process the start script if there is more than one script...991 if (_end < 2)992 return;993 994 if (_scripts[0] == null)995 return;996 997 var emptyArray = Array.Empty<object>();998 _scripts[0].InvokeUsingCmdlet(999 contextCmdlet: this,1000 useLocalScope: false,1001 errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe,1002 dollarUnder: AutomationNull.Value,1003 input: emptyArray,1004 scriptThis: AutomationNull.Value,1005 args: emptyArray);1006 }1007 1008 /// <summary>1009 /// Do method invocation with arguments.1010 /// </summary>1011 private void MethodCallWithArguments()1012 {1013 // resolve the name1014 ReadOnlyPSMemberInfoCollection<PSMemberInfo> methods =1015 _inputObject.Members.Match(1016 _propertyOrMethodName,1017 PSMemberTypes.Methods | PSMemberTypes.ParameterizedProperty);1018 1019 Dbg.Assert(methods != null, "The return value of Members.Match should never be null.");1020 if (methods.Count > 1)1021 {1022 // write error record: method ambiguous1023 StringBuilder possibleMatches = new StringBuilder();1024 foreach (PSMemberInfo item in methods)1025 {1026 possibleMatches.Append(CultureInfo.InvariantCulture, $" {item.Name}");1027 }1028 1029 WriteError(GenerateNameParameterError(1030 "Name",1031 InternalCommandStrings.AmbiguousMethodName,1032 "AmbiguousMethodName",1033 _inputObject,1034 _propertyOrMethodName,1035 possibleMatches));1036 }1037 else if (methods.Count == 0 || methods[0] is not PSMethodInfo)1038 {1039 // write error record: method no found1040 WriteError(GenerateNameParameterError(1041 "Name",1042 InternalCommandStrings.MethodNotFound,1043 "MethodNotFound",1044 _inputObject,1045 _propertyOrMethodName));1046 }1047 else1048 {1049 PSMethodInfo targetMethod = methods[0] as PSMethodInfo;1050 Dbg.Assert(targetMethod != null, "targetMethod should not be null here.");1051 1052 // should process1053 StringBuilder arglist = new StringBuilder(GetStringRepresentation(_arguments[0]));1054 for (int i = 1; i < _arguments.Length; i++)1055 {1056 arglist.Append(CultureInfo.InvariantCulture, $", {GetStringRepresentation(_arguments[i])}");1057 }1058 1059 string methodAction = string.Format(CultureInfo.InvariantCulture,1060 InternalCommandStrings.ForEachObjectMethodActionWithArguments,1061 targetMethod.Name, arglist);1062 1063 try1064 {1065 if (ShouldProcess(_targetString, methodAction))1066 {1067 if (!BlockMethodInLanguageMode(InputObject))1068 {1069 object result = targetMethod.Invoke(_arguments);1070 WriteToPipelineWithUnrolling(result);1071 }1072 }1073 }1074 catch (PipelineStoppedException)1075 {1076 // PipelineStoppedException can be caused by select-object1077 throw;1078 }1079 catch (Exception ex)1080 {1081 WriteError(new ErrorRecord(ex, "MethodInvocationError", ErrorCategory.InvalidOperation, _inputObject));1082 }1083 }1084 }1085 1086 /// <summary>1087 /// Get the string representation of the passed-in object.1088 /// </summary>1089 /// <param name="obj">Source object.</param>1090 /// <returns>String representation of the source object.</returns>1091 private static string GetStringRepresentation(object obj)1092 {1093 string objInString;1094 try1095 {1096 // The "ToString()" method could throw an exception1097 objInString = LanguagePrimitives.IsNull(obj) ? "null" : obj.ToString();1098 }1099 catch (Exception)1100 {1101 objInString = null;1102 }1103 1104 if (string.IsNullOrEmpty(objInString))1105 {1106 var psobj = obj as PSObject;1107 objInString = psobj != null ? psobj.BaseObject.GetType().FullName : obj.GetType().FullName;1108 }1109 1110 return objInString;1111 }1112 1113 /// <summary>1114 /// Get the value by taking _propertyOrMethodName as the key, if the1115 /// input object is a IDictionary.1116 /// </summary>1117 /// <returns>True if success.</returns>1118 private bool GetValueFromIDictionaryInput()1119 {1120 object target = PSObject.Base(_inputObject);1121 IDictionary hash = target as IDictionary;1122 1123 try1124 {1125 if (hash != null && hash.Contains(_propertyOrMethodName))1126 {1127 string keyAction = string.Format(1128 CultureInfo.InvariantCulture,1129 InternalCommandStrings.ForEachObjectKeyAction,1130 _propertyOrMethodName);1131 if (ShouldProcess(_targetString, keyAction))1132 {1133 object result = hash[_propertyOrMethodName];1134 WriteToPipelineWithUnrolling(result);1135 }1136 1137 return true;1138 }1139 }1140 catch (InvalidOperationException)1141 {1142 // Ignore invalid operation exception, it can happen if the dictionary1143 // has keys that can't be compared to property.1144 }1145 1146 return false;1147 }1148 1149 /// <summary>1150 /// Unroll the object to be output. If it's of type IEnumerator, unroll and output it1151 /// by calling WriteOutIEnumerator. If it's not, unroll and output it by calling WriteObject(obj, true)1152 /// </summary>1153 /// <param name="obj">Source object.</param>1154 private void WriteToPipelineWithUnrolling(object obj)1155 {1156 IEnumerator objAsEnumerator = LanguagePrimitives.GetEnumerator(obj);1157 if (objAsEnumerator != null)1158 {1159 WriteOutIEnumerator(objAsEnumerator);1160 }1161 else1162 {1163 WriteObject(obj, true);1164 }1165 }1166 1167 /// <summary>1168 /// Unroll an IEnumerator and output all entries.1169 /// </summary>1170 /// <param name="list">Source list.</param>1171 private void WriteOutIEnumerator(IEnumerator list)1172 {1173 if (list != null)1174 {1175 while (ParserOps.MoveNext(this.Context, null, list))1176 {1177 object val = ParserOps.Current(null, list);1178 1179 if (val != AutomationNull.Value)1180 {1181 WriteObject(val);1182 }1183 }1184 }1185 }1186 1187 /// <summary>1188 /// Check if the language mode is the restrictedLanguageMode before invoking a method.1189 /// Write out error message and return true if we are in restrictedLanguageMode.1190 /// </summary>1191 /// <param name="inputObject">Source object.</param>1192 /// <returns>True if we are in restrictedLanguageMode.</returns>1193 private bool BlockMethodInLanguageMode(object inputObject)1194 {1195 // Cannot invoke a method in RestrictedLanguage mode1196 if (Context.LanguageMode == PSLanguageMode.RestrictedLanguage)1197 {1198 PSInvalidOperationException exception =1199 new PSInvalidOperationException(InternalCommandStrings.NoMethodInvocationInRestrictedLanguageMode);1200 