MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.Generic;6using System.Collections.ObjectModel;7using System.Diagnostics.CodeAnalysis;8using System.Globalization;9using System.Linq;10using System.Management.Automation.Host;11using System.Management.Automation.Internal;12using System.Management.Automation.Language;13using System.Management.Automation.Runspaces;14using System.Text;15 16namespace System.Management.Automation17{18 /// <summary>19 /// This is the interface between the CommandProcessor and the various20 /// parameter binders required to bind parameters to a cmdlet.21 /// </summary>22 internal class CmdletParameterBinderController : ParameterBinderController23 {24 #region tracer25 26 [TraceSource("ParameterBinderController", "Controls the interaction between the command processor and the parameter binder(s).")]27 private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("ParameterBinderController", "Controls the interaction between the command processor and the parameter binder(s).");28 29 #endregion tracer30 31 #region ctor32 33 /// <summary>34 /// Initializes the cmdlet parameter binder controller for35 /// the specified cmdlet and engine context.36 /// </summary>37 /// <param name="cmdlet">38 /// The cmdlet that the parameters will be bound to.39 /// </param>40 /// <param name="commandMetadata">41 /// The metadata about the cmdlet.42 /// </param>43 /// <param name="parameterBinder">44 /// The default parameter binder to use.45 /// </param>46 internal CmdletParameterBinderController(47 Cmdlet cmdlet,48 CommandMetadata commandMetadata,49 ParameterBinderBase parameterBinder)50 : base(51 cmdlet.MyInvocation,52 cmdlet.Context,53 parameterBinder)54 {55 if (cmdlet == null)56 {57 throw PSTraceSource.NewArgumentNullException(nameof(cmdlet));58 }59 60 if (commandMetadata == null)61 {62 throw PSTraceSource.NewArgumentNullException(nameof(commandMetadata));63 }64 65 this.Command = cmdlet;66 _commandRuntime = (MshCommandRuntime)cmdlet.CommandRuntime;67 _commandMetadata = commandMetadata;68 69 // Add the static parameter metadata to the bindable parameters70 // And add them to the unbound parameters list71 72 if (commandMetadata.ImplementsDynamicParameters)73 {74 // ReplaceMetadata makes a copy for us, so we can use that collection as is.75 this.UnboundParameters = this.BindableParameters.ReplaceMetadata(commandMetadata.StaticCommandParameterMetadata);76 }77 else78 {79 _bindableParameters = commandMetadata.StaticCommandParameterMetadata;80 81 // Must make a copy of the list because we'll modify it.82 this.UnboundParameters = new List<MergedCompiledCommandParameter>(_bindableParameters.BindableParameters.Values);83 }84 }85 86 #endregion ctor87 88 #region helper_methods89 90 /// <summary>91 /// Binds the specified command-line parameters to the target.92 /// </summary>93 /// <param name="arguments">94 /// Parameters to the command.95 /// </param>96 /// <exception cref="ParameterBindingException">97 /// If any parameters fail to bind,98 /// or99 /// If any mandatory parameters are missing.100 /// </exception>101 /// <exception cref="MetadataException">102 /// If there is an error generating the metadata for dynamic parameters.103 /// </exception>104 internal void BindCommandLineParameters(Collection<CommandParameterInternal> arguments)105 {106 s_tracer.WriteLine("Argument count: {0}", arguments.Count);107 108 BindCommandLineParametersNoValidation(arguments);109 110 // Is pipeline input expected?111 bool isPipelineInputExpected = !(_commandRuntime.IsClosed && _commandRuntime.InputPipe.Empty);112 113 int validParameterSetCount;114 115 if (!isPipelineInputExpected)116 {117 // Since pipeline input is not expected, ensure that we have a single118 // parameter set and that all the mandatory119 // parameters for the working parameter set are specified, or prompt120 121 validParameterSetCount = ValidateParameterSets(false, true);122 }123 else124 {125 // Use ValidateParameterSets to get the number of valid parameter126 // sets.127 128 // NTRAID#Windows Out Of Band Releases-2005/11/07-923917-JonN129 validParameterSetCount = ValidateParameterSets(true, false);130 }131 132 // If the parameter set is determined and the default parameters are not used133 // we try the default parameter binding again because it may contain some mandatory134 // parameters135 if (validParameterSetCount == 1 && !DefaultParameterBindingInUse)136 {137 ApplyDefaultParameterBinding("Mandatory Checking", false);138 }139 140 // If there are multiple valid parameter sets and we are expecting pipeline inputs,141 // we should filter out those parameter sets that cannot take pipeline inputs anymore.142 if (validParameterSetCount > 1 && isPipelineInputExpected)143 {144 uint filteredValidParameterSetFlags = FilterParameterSetsTakingNoPipelineInput();145 if (filteredValidParameterSetFlags != _currentParameterSetFlag)146 {147 _currentParameterSetFlag = filteredValidParameterSetFlags;148 // The valid parameter set flag is narrowed down, we get the new validParameterSetCount149 validParameterSetCount = ValidateParameterSets(true, false);150 }151 }152 153 using (ParameterBinderBase.bindingTracer.TraceScope(154 "MANDATORY PARAMETER CHECK on cmdlet [{0}]",155 _commandMetadata.Name))156 {157 try158 {159 // The missingMandatoryParameters out parameter is used for error reporting when binding from the pipeline.160 // We're not binding from the pipeline here, and if a mandatory non-pipeline parameter is missing, it will161 // be prompted for, or an exception will be raised, so we can ignore the missingMandatoryParameters out parameter.162 Collection<MergedCompiledCommandParameter> missingMandatoryParameters;163 164 // We shouldn't prompt for mandatory parameters if this command is private.165 bool promptForMandatoryParameters = (Command.CommandInfo.Visibility == SessionStateEntryVisibility.Public);166 HandleUnboundMandatoryParameters(validParameterSetCount, true, promptForMandatoryParameters, isPipelineInputExpected, out missingMandatoryParameters);167 168 if (DefaultParameterBinder is ScriptParameterBinder)169 {170 BindUnboundScriptParameters();171 }172 }173 catch (ParameterBindingException pbex)174 {175 if (!DefaultParameterBindingInUse)176 {177 throw;178 }179 180 ThrowElaboratedBindingException(pbex);181 }182 }183 184 // If there is no more expected input, ensure there is a single185 // parameter set selected186 187 if (!isPipelineInputExpected)188 {189 VerifyParameterSetSelected();190 }191 192 // Set the prepipeline parameter set flags so that they can be restored193 // between each pipeline object.194 195 _prePipelineProcessingParameterSetFlags = _currentParameterSetFlag;196 }197 198 /// <summary>199 /// Binds the unbound arguments to parameters but does not200 /// perform mandatory parameter validation or parameter set validation.201 /// </summary>202 internal void BindCommandLineParametersNoValidation(Collection<CommandParameterInternal> arguments)203 {204 var psCompiledScriptCmdlet = this.Command as PSScriptCmdlet;205 psCompiledScriptCmdlet?.PrepareForBinding(this.CommandLineParameters);206 207 InitUnboundArguments(arguments);208 CommandMetadata cmdletMetadata = _commandMetadata;209 // Clear the warningSet at the beginning.210 _warningSet.Clear();211 // Parse $PSDefaultParameterValues to get all valid <parameter, value> pairs212 _allDefaultParameterValuePairs = this.GetDefaultParameterValuePairs(true);213 // Set to false at the beginning214 DefaultParameterBindingInUse = false;215 // Clear the bound default parameters at the beginning216 BoundDefaultParameters.Clear();217 218 // Reparse the arguments based on the merged metadata219 ReparseUnboundArguments();220 221 using (ParameterBinderBase.bindingTracer.TraceScope(222 "BIND NAMED cmd line args [{0}]",223 _commandMetadata.Name))224 {225 // Bind the actual arguments226 UnboundArguments = BindNamedParameters(_currentParameterSetFlag, this.UnboundArguments);227 }228 229 ParameterBindingException reportedBindingException;230 ParameterBindingException currentBindingException;231 232 using (ParameterBinderBase.bindingTracer.TraceScope(233 "BIND POSITIONAL cmd line args [{0}]",234 _commandMetadata.Name))235 {236 // Now that we know the parameter set, bind the positional parameters237 UnboundArguments =238 BindPositionalParameters(239 UnboundArguments,240 _currentParameterSetFlag,241 cmdletMetadata.DefaultParameterSetFlag,242 out currentBindingException);243 244 reportedBindingException = currentBindingException;245 }246 247 // Try applying the default parameter binding after POSITIONAL BIND so that the default parameter248 // values can influence the parameter set selection earlier than the default parameter set.249 ApplyDefaultParameterBinding("POSITIONAL BIND", false);250 251 // We need to make sure there is at least one valid parameter set. Its252 // OK to allow more than one as long as one of them takes pipeline input.253 254 // NTRAID#Windows Out Of Band Releases-2006/02/14-928660-JonN255 // Pipeline input fails to bind to pipeline enabled parameter256 // second parameter changed from true to false257 ValidateParameterSets(true, false);258 259 // Always get the dynamic parameters as there may be mandatory parameters there260 261 // Now try binding the dynamic parameters262 HandleCommandLineDynamicParameters(out currentBindingException);263 264 // Try binding the default parameters again. After dynamic binding, new parameter metadata are265 // included, so it's possible a previously unsuccessful binding will succeed.266 ApplyDefaultParameterBinding("DYNAMIC BIND", true);267 268 // If this generated an exception (but we didn't have one from the non-dynamic269 // parameters, report on this one.270 reportedBindingException ??= currentBindingException;271 272 // If the cmdlet implements a ValueFromRemainingArguments parameter (VarArgs)273 // bind the unbound arguments to that parameter.274 HandleRemainingArguments();275 276 VerifyArgumentsProcessed(reportedBindingException);277 }278 279 /// <summary>280 /// Process all valid parameter sets, and filter out those that don't take any pipeline input.281 /// </summary>282 /// <returns>283 /// The new valid parameter set flags284 /// </returns>285 private uint FilterParameterSetsTakingNoPipelineInput()286 {287 uint parameterSetsTakingPipeInput = 0;288 bool findPipeParameterInAllSets = false;289 290 foreach (KeyValuePair<MergedCompiledCommandParameter, DelayedScriptBlockArgument> entry in _delayBindScriptBlocks)291 {292 parameterSetsTakingPipeInput |= entry.Key.Parameter.ParameterSetFlags;293 }294 295 foreach (MergedCompiledCommandParameter parameter in UnboundParameters)296 {297 // If a parameter doesn't take pipeline input at all, we can skip it298 if (!parameter.Parameter.IsPipelineParameterInSomeParameterSet)299 {300 continue;301 }302 303 var matchingParameterSetMetadata =304 parameter.Parameter.GetMatchingParameterSetData(_currentParameterSetFlag);305 306 foreach (ParameterSetSpecificMetadata parameterSetMetadata in matchingParameterSetMetadata)307 {308 if (parameterSetMetadata.ValueFromPipeline || parameterSetMetadata.ValueFromPipelineByPropertyName)309 {310 if (parameterSetMetadata.ParameterSetFlag == 0 && parameterSetMetadata.IsInAllSets)311 {312 // The parameter takes pipeline input and is in all sets, we don't change the _currentParameterSetFlag313 parameterSetsTakingPipeInput = 0;314 findPipeParameterInAllSets = true;315 break;316 }317 else318 {319 parameterSetsTakingPipeInput |= parameterSetMetadata.ParameterSetFlag;320 }321 }322 }323 324 if (findPipeParameterInAllSets)325 break;326 }327 328 // If parameterSetsTakingPipeInput is 0, then no parameter set from the _currentParameterSetFlag can take piped objects.329 // Then we just leave what it was, and the pipeline binding deal with the error later330 if (parameterSetsTakingPipeInput != 0)331 return _currentParameterSetFlag & parameterSetsTakingPipeInput;332 else333 return _currentParameterSetFlag;334 }335 336 /// <summary>337 /// Apply the binding for the default parameter defined by the user.338 /// </summary>339 /// <param name="bindingStage">340 /// Dictate which binding stage this default binding happens341 /// </param>342 /// <param name="isDynamic">343 /// Special operation needed if the default binding happens at the dynamic binding stage344 /// </param>345 /// <returns></returns>346 private void ApplyDefaultParameterBinding(string bindingStage, bool isDynamic)347 {348 if (!_useDefaultParameterBinding)349 {350 return;351 }352 353 if (isDynamic)354 {355 // Get user defined default parameter value pairs again, so that the356 // dynamic parameter value pairs could be involved.357 _allDefaultParameterValuePairs = GetDefaultParameterValuePairs(false);358 }359 360 Dictionary<MergedCompiledCommandParameter, object> qualifiedParameterValuePairs = GetQualifiedParameterValuePairs(_currentParameterSetFlag, _allDefaultParameterValuePairs);361 if (qualifiedParameterValuePairs != null)362 {363 bool isSuccess = false;364 using (ParameterBinderBase.bindingTracer.TraceScope(365 "BIND DEFAULT <parameter, value> pairs after [{0}] for [{1}]",366 bindingStage, _commandMetadata.Name))367 {368 isSuccess = BindDefaultParameters(_currentParameterSetFlag, qualifiedParameterValuePairs);369 if (isSuccess && !DefaultParameterBindingInUse)370 {371 DefaultParameterBindingInUse = true;372 }373 }374 375 s_tracer.WriteLine("BIND DEFAULT after [{0}] result [{1}]", bindingStage, isSuccess);376 }377 378 return;379 }380 381 /// <summary>382 /// Bind the default parameter value pairs.383 /// </summary>384 /// <param name="validParameterSetFlag">ValidParameterSetFlag.</param>385 /// <param name="defaultParameterValues">Default value pairs.</param>386 /// <returns>387 /// true if there is at least one default parameter bound successfully388 /// false if there is no default parameter bound successfully389 /// </returns>390 private bool BindDefaultParameters(uint validParameterSetFlag, Dictionary<MergedCompiledCommandParameter, object> defaultParameterValues)391 {392 bool ret = false;393 foreach (var pair in defaultParameterValues)394 {395 MergedCompiledCommandParameter parameter = pair.Key;396 object argumentValue = pair.Value;397 string parameterName = parameter.Parameter.Name;398 399 try400 {401 ScriptBlock scriptBlockArg = argumentValue as ScriptBlock;402 if (scriptBlockArg != null)403 {404 // Get the current binding state, and pass it to the ScriptBlock as the argument405 // The 'arg' includes HashSet properties 'BoundParameters', 'BoundPositionalParameters',406 // 'BoundDefaultParameters', and 'LastBindingStage'. So the user can set value407 // to a parameter depending on the current binding state.408 PSObject arg = WrapBindingState();409 Collection<PSObject> results = scriptBlockArg.Invoke(arg);410 if (results == null || results.Count == 0)411 {412 continue;413 }414 else if (results.Count == 1)415 {416 argumentValue = results[0];417 }418 else419 {420 argumentValue = results;421 }422 }423 424 CommandParameterInternal bindableArgument =425 CommandParameterInternal.CreateParameterWithArgument(426 /*parameterAst*/null, parameterName, "-" + parameterName + ":",427 /*argumentAst*/null, argumentValue, false);428 429 bool bindResult =430 BindParameter(431 validParameterSetFlag,432 bindableArgument,433 parameter,434 ParameterBindingFlags.ShouldCoerceType | ParameterBindingFlags.DelayBindScriptBlock);435 436 if (bindResult && !ret)437 {438 ret = true;439 }440 441 if (bindResult)442 {443 BoundDefaultParameters.Add(parameterName);444 }445 }446 catch (ParameterBindingException ex)447 {448 // We don't want the failures in default binding affect the command line binding,449 // so we write out a warning and ignore this binding failure450 if (!_warningSet.Contains(_commandMetadata.Name + Separator + parameterName))451 {452 string message = string.Format(CultureInfo.InvariantCulture,453 ParameterBinderStrings.FailToBindDefaultParameter,454 LanguagePrimitives.IsNull(argumentValue) ? "null" : argumentValue.ToString(),455 parameterName, ex.Message);456 _commandRuntime.WriteWarning(message);457 _warningSet.Add(_commandMetadata.Name + Separator + parameterName);458 }459 460 continue;461 }462 }463 464 return ret;465 }466 467 /// <summary>468 /// Wrap up current binding state to provide more information to the user.469 /// </summary>470 /// <returns></returns>471 private PSObject WrapBindingState()472 {473 HashSet<string> boundParameterNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);474 HashSet<string> boundPositionalParameterNames =475 this.DefaultParameterBinder.CommandLineParameters.CopyBoundPositionalParameters();476 HashSet<string> boundDefaultParameterNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);477 478 foreach (string paramName in BoundParameters.Keys)479 {480 boundParameterNames.Add(paramName);481 }482 483 foreach (string paramName in BoundDefaultParameters)484 {485 boundDefaultParameterNames.Add(paramName);486 }487 488 PSObject result = new PSObject();489 result.Properties.Add(new PSNoteProperty("BoundParameters", boundParameterNames));490 result.Properties.Add(new PSNoteProperty("BoundPositionalParameters", boundPositionalParameterNames));491 result.Properties.Add(new PSNoteProperty("BoundDefaultParameters", boundDefaultParameterNames));492 493 return result;494 }495 496 /// <summary>497 /// Get all qualified default parameter value pairs based on the498 /// given currentParameterSetFlag.499 /// </summary>500 /// <param name="currentParameterSetFlag"></param>501 /// <param name="availableParameterValuePairs"></param>502 /// <returns>Null if no qualified pair found.</returns>503 private Dictionary<MergedCompiledCommandParameter, object> GetQualifiedParameterValuePairs(504 uint currentParameterSetFlag,505 Dictionary<MergedCompiledCommandParameter, object> availableParameterValuePairs)506 {507 if (availableParameterValuePairs == null)508 {509 return null;510 }511 512 Dictionary<MergedCompiledCommandParameter, object> result = new Dictionary<MergedCompiledCommandParameter, object>();513 514 uint possibleParameterFlag = uint.MaxValue;515 foreach (var pair in availableParameterValuePairs)516 {517 MergedCompiledCommandParameter param = pair.Key;518 if ((param.Parameter.ParameterSetFlags & currentParameterSetFlag) == 0 && !param.Parameter.IsInAllSets)519 {520 continue;521 }522 523 if (BoundArguments.ContainsKey(param.Parameter.Name))524 {525 continue;526 }527 528 // check if this param's set conflicts with other possible params.529 if (param.Parameter.ParameterSetFlags != 0)530 {531 possibleParameterFlag &= param.Parameter.ParameterSetFlags;532 if (possibleParameterFlag == 0)533 {534 return null;535 }536 }537 538 result.Add(param, pair.Value);539 }540 541 if (result.Count > 0)542 {543 return result;544 }545 546 return null;547 }548 549 /// <summary>550 /// Get the aliases of the current cmdlet.551 /// </summary>552 /// <returns></returns>553 private List<string> GetAliasOfCurrentCmdlet()554 {555 var results = Context.SessionState.Internal.GetAliasesByCommandName(_commandMetadata.Name).ToList();556 557 return results.Count > 0 ? results : null;558 }559 560 /// <summary>561 /// Check if the passed-in aliasName matches an alias name in _aliasList.562 /// </summary>563 /// <param name="aliasName"></param>564 /// <returns></returns>565 private bool MatchAnyAlias(string aliasName)566 {567 if (_aliasList == null)568 {569 return false;570 }571 572 bool result = false;573 WildcardPattern aliasPattern = WildcardPattern.Get(aliasName, WildcardOptions.IgnoreCase);574 foreach (string alias in _aliasList)575 {576 if (aliasPattern.IsMatch(alias))577 {578 result = true;579 break;580 }581 }582 583 return result;584 }585 586 internal IDictionary DefaultParameterValues { get; set; }587 /// <summary>588 /// Get all available default parameter value pairs.589 /// </summary>590 /// <returns>Return the available parameter value pairs. Otherwise return null.</returns>591 private Dictionary<MergedCompiledCommandParameter, object> GetDefaultParameterValuePairs(bool needToGetAlias)592 {593 if (DefaultParameterValues == null)594 {595 _useDefaultParameterBinding = false;596 return null;597 }598 599 var availablePairs = new Dictionary<MergedCompiledCommandParameter, object>();600 601 if (needToGetAlias && DefaultParameterValues.Count > 0)602 {603 // Get all aliases of the current cmdlet604 _aliasList = GetAliasOfCurrentCmdlet();605 }606 607 // Set flag to true by default608 _useDefaultParameterBinding = true;609 610 string currentCmdletName = _commandMetadata.Name;611 612 IDictionary<string, MergedCompiledCommandParameter> bindableParameters = BindableParameters.BindableParameters;613 IDictionary<string, MergedCompiledCommandParameter> bindableAlias = BindableParameters.AliasedParameters;614 615 // Contains parameters that are set with different values by settings in $PSDefaultParameterValues.616 // We should ignore those settings and write out a warning617 var parametersToRemove = new HashSet<MergedCompiledCommandParameter>();618 var wildcardDefault = new Dictionary<string, object>();619 // Contains keys that are in bad format. For every bad format key, we should write out a warning message620 // the first time we encounter it, and remove it from the $PSDefaultParameterValues621 var keysToRemove = new List<object>();622 623 foreach (DictionaryEntry entry in DefaultParameterValues)624 {625 if (entry.Key is not string key)626 {627 continue;628 }629 630 key = key.Trim();631 string cmdletName = null;632 string parameterName = null;633 634 // The key is not in valid format635 if (!DefaultParameterDictionary.CheckKeyIsValid(key, ref cmdletName, ref parameterName))636 {637 if (key.Equals("Disabled", StringComparison.OrdinalIgnoreCase) &&638 LanguagePrimitives.IsTrue(entry.Value))639 {640 _useDefaultParameterBinding = false;641 return null;642 }643 // Write out a warning message if the key is not 'Disabled'644 if (!key.Equals("Disabled", StringComparison.OrdinalIgnoreCase))645 {646 keysToRemove.Add(entry.Key);647 }648 649 continue;650 }651 652 Diagnostics.Assert(cmdletName != null && parameterName != null, "The cmdletName and parameterName should be set in CheckKeyIsValid");653 654 if (WildcardPattern.ContainsWildcardCharacters(key))655 {656 wildcardDefault.Add(cmdletName + Separator + parameterName, entry.Value);657 continue;658 }659 660 // Continue to process this entry only if the specified cmdletName is the name661 // of the current cmdlet, or is an alias name of the current cmdlet.662 if (!cmdletName.Equals(currentCmdletName, StringComparison.OrdinalIgnoreCase) && !MatchAnyAlias(cmdletName))663 {664 continue;665 }666 667 GetDefaultParameterValuePairsHelper(668 cmdletName, parameterName, entry.Value,669 bindableParameters, bindableAlias,670 availablePairs, parametersToRemove);671 }672 673 foreach (KeyValuePair<string, object> wildcard in wildcardDefault)674 {675 string key = wildcard.Key;676 677 string cmdletName = key.Substring(0, key.IndexOf(Separator, StringComparison.OrdinalIgnoreCase));678 string parameterName = key.Substring(key.IndexOf(Separator, StringComparison.OrdinalIgnoreCase) + Separator.Length);679 680 WildcardPattern cmdletPattern = WildcardPattern.Get(cmdletName, WildcardOptions.IgnoreCase);681 // Continue to process this entry only if the cmdletName matches the name of the current682 // cmdlet, or matches an alias name of the current cmdlet683 if (!cmdletPattern.IsMatch(currentCmdletName) && !MatchAnyAlias(cmdletName))684 {685 continue;686 }687 688 if (!WildcardPattern.ContainsWildcardCharacters(parameterName))689 {690 GetDefaultParameterValuePairsHelper(691 cmdletName, parameterName, wildcard.Value,692 bindableParameters, bindableAlias,693 availablePairs, parametersToRemove);694 695 continue;696 }697 698 WildcardPattern parameterPattern = MemberMatch.GetNamePattern(parameterName);699 var matches = new List<MergedCompiledCommandParameter>();700 701 foreach (KeyValuePair<string, MergedCompiledCommandParameter> entry in bindableParameters)702 {703 if (parameterPattern.IsMatch(entry.Key))704 {705 matches.Add(entry.Value);706 }707 }708 709 foreach (KeyValuePair<string, MergedCompiledCommandParameter> entry in bindableAlias)710 {711 if (parameterPattern.IsMatch(entry.Key))712 {713 matches.Add(entry.Value);714 }715 }716 717 if (matches.Count > 1)718 {719 // The parameterPattern matches more than one parameters, so we write out a warning message and ignore this setting720 if (!_warningSet.Contains(cmdletName + Separator + parameterName))721 {722 _commandRuntime.WriteWarning(723 string.Format(CultureInfo.InvariantCulture, ParameterBinderStrings.MultipleParametersMatched, parameterName));724 _warningSet.Add(cmdletName + Separator + parameterName);725 }726 727 continue;728 }729 730 if (matches.Count == 1)731 {732 if (!availablePairs.ContainsKey(matches[0]))733 {734 availablePairs.Add(matches[0], wildcard.Value);735 continue;736 }737 738 if (!wildcard.Value.Equals(availablePairs[matches[0]]))739 {740 if (!_warningSet.Contains(cmdletName + Separator + parameterName))741 {742 _commandRuntime.WriteWarning(743 string.Format(CultureInfo.InvariantCulture, ParameterBinderStrings.DifferentValuesAssignedToSingleParameter, parameterName));744 _warningSet.Add(cmdletName + Separator + parameterName);745 }746 747 parametersToRemove.Add(matches[0]);748 }749 }750 }751 752 if (keysToRemove.Count > 0)753 {754 var keysInError = new StringBuilder();755 foreach (object badFormatKey in keysToRemove)756 {757 if (DefaultParameterValues.Contains(badFormatKey))758 DefaultParameterValues.Remove(badFormatKey);759 760 keysInError.Append(badFormatKey.ToString() + ", ");761 }762 763 keysInError.Remove(keysInError.Length - 2, 2);764 var multipleKeys = keysToRemove.Count > 1;765 string formatString = multipleKeys766 ? ParameterBinderStrings.MultipleKeysInBadFormat767 : ParameterBinderStrings.SingleKeyInBadFormat;768 _commandRuntime.WriteWarning(769 string.Format(CultureInfo.InvariantCulture, formatString, keysInError));770 }771 772 foreach (MergedCompiledCommandParameter param in parametersToRemove)773 {774 availablePairs.Remove(param);775 }776 777 if (availablePairs.Count > 0)778 {779 return availablePairs;780 }781 782 return null;783 }784 785 /// <summary>786 /// A helper method for GetDefaultParameterValuePairs.787 /// </summary>788 /// <param name="cmdletName"></param>789 /// <param name="paramName"></param>790 /// <param name="paramValue"></param>791 /// <param name="bindableParameters"></param>792 /// <param name="bindableAlias"></param>793 /// <param name="result"></param>794 /// <param name="parametersToRemove"></param>795 private void GetDefaultParameterValuePairsHelper(796 string cmdletName, string paramName, object paramValue,797 IDictionary<string, MergedCompiledCommandParameter> bindableParameters,798 IDictionary<string, MergedCompiledCommandParameter> bindableAlias,799 Dictionary<MergedCompiledCommandParameter, object> result,800 HashSet<MergedCompiledCommandParameter> parametersToRemove)801 {802 // No exception should be thrown if we cannot find a match for the 'paramName',803 // because the 'paramName' could be a dynamic parameter name, and this dynamic parameter804 // hasn't been introduced at the current stage.805 bool writeWarning = false;806 MergedCompiledCommandParameter matchParameter;807 object resultObject;808 if (bindableParameters.TryGetValue(paramName, out matchParameter))809 {810 if (!result.TryGetValue(matchParameter, out resultObject))811 {812 result.Add(matchParameter, paramValue);813 return;814 }815 816 if (!paramValue.Equals(resultObject))817 {818 writeWarning = true;819 parametersToRemove.Add(matchParameter);820 }821 }822 else823 {824 if (bindableAlias.TryGetValue(paramName, out matchParameter))825 {826 if (!result.TryGetValue(matchParameter, out resultObject))827 {828 result.Add(matchParameter, paramValue);829 return;830 }831 832 if (!paramValue.Equals(resultObject))833 {834 writeWarning = true;835 parametersToRemove.Add(matchParameter);836 }837 }838 }839 840 if (writeWarning && !_warningSet.Contains(cmdletName + Separator + paramName))841 {842 _commandRuntime.WriteWarning(843 string.Format(CultureInfo.InvariantCulture, ParameterBinderStrings.DifferentValuesAssignedToSingleParameter, paramName));844 _warningSet.Add(cmdletName + Separator + paramName);845 }846 }847 848 /// <summary>849 /// Verify if all arguments from the command line are bound.850 /// </summary>851 /// <param name="originalBindingException">852 /// Previous binding exceptions that possibly causes the failure853 /// </param>854 private void VerifyArgumentsProcessed(ParameterBindingException originalBindingException)855 {856 // Now verify that all the arguments that were passed in were processed.857 858 if (UnboundArguments.Count > 0)859 {860 ParameterBindingException bindingException;861 CommandParameterInternal parameter = UnboundArguments[0];862 863 // Get the argument type that was specified864 865 Type specifiedType = null;866 object argumentValue = parameter.ArgumentValue;867 if (argumentValue != null && argumentValue != UnboundParameter.Value)868 {869 specifiedType = argumentValue.GetType();870 }871 872 if (parameter.ParameterNameSpecified)873 {874 bindingException =875 new ParameterBindingException(876 ErrorCategory.InvalidArgument,877 this.Command.MyInvocation,878 GetParameterErrorExtent(parameter),879 parameter.ParameterName,880 null,881 specifiedType,882 ParameterBinderStrings.NamedParameterNotFound,883 "NamedParameterNotFound");884 }885 else886 {887 // If this was a positional parameter, and we have the original exception,888 // report on the original error889 if (originalBindingException != null)890 {891 bindingException = originalBindingException;892 }893 // Otherwise, give a generic error.894 else895 {896 string argument = StringLiterals.DollarNull;897 if (parameter.ArgumentValue != null)898 {899 try900 {901 argument = parameter.ArgumentValue.ToString();902 }903 catch (Exception e)904 {905 bindingException =906 new ParameterBindingArgumentTransformationException(907 e,908 ErrorCategory.InvalidData,909 this.InvocationInfo,910 null,911 null,912 null,913 parameter.ArgumentValue.GetType(),914 ParameterBinderStrings.ParameterArgumentTransformationErrorMessageOnly,915 "ParameterArgumentTransformationErrorMessageOnly",916 e.Message);917 918 if (!DefaultParameterBindingInUse)919 {920 throw bindingException;921 }922 else923 {924 ThrowElaboratedBindingException(bindingException);925 }926 }927 }928 929 bindingException =930 new ParameterBindingException(931 ErrorCategory.InvalidArgument,932 this.Command.MyInvocation,933 null,934 argument,935 null,936 specifiedType,937 ParameterBinderStrings.PositionalParameterNotFound,938 "PositionalParameterNotFound");939 }940 }941 942 if (!DefaultParameterBindingInUse)943 {944 throw bindingException;945 }946 else947 {948 ThrowElaboratedBindingException(bindingException);949 }950 }951 }952 953 /// <summary>954 /// Verifies that a single parameter set is selected and throws an exception if955 /// one of there are multiple and one of them is not the default parameter set.956 /// </summary>957 private void VerifyParameterSetSelected()958 {959 // Now verify that a parameter set has been selected if any parameter sets960 // were defined.961 962 if (this.BindableParameters.ParameterSetCount > 1)963 {964 if (_currentParameterSetFlag == uint.MaxValue)965 {966 if ((_currentParameterSetFlag &967 _commandMetadata.DefaultParameterSetFlag) != 0 &&968 _commandMetadata.DefaultParameterSetFlag != uint.MaxValue)969 {970 ParameterBinderBase.bindingTracer.WriteLine(971 "{0} valid parameter sets, using the DEFAULT PARAMETER SET: [{0}]",972 this.BindableParameters.ParameterSetCount.ToString(),973 _commandMetadata.DefaultParameterSetName);974 975 _currentParameterSetFlag =976 _commandMetadata.DefaultParameterSetFlag;977 }978 else979 {980 ParameterBinderBase.bindingTracer.TraceError(981 "ERROR: {0} valid parameter sets, but NOT DEFAULT PARAMETER SET.",982 this.BindableParameters.ParameterSetCount);983 984 // Throw an exception for ambiguous parameter set985 ThrowAmbiguousParameterSetException(_currentParameterSetFlag, BindableParameters);986 }987 }988 }989 }990 991 /// <summary>992 /// Restores the specified parameter to the original value.993 /// </summary>994 /// <param name="argumentToBind">995 /// The argument containing the value to restore.996 /// </param>997 /// <param name="parameter">998 /// The metadata for the parameter to restore.999 /// </param>1000 /// <returns>1001 /// True if the parameter was restored correctly, or false otherwise.1002 /// </returns>1003 private bool RestoreParameter(CommandParameterInternal argumentToBind, MergedCompiledCommandParameter parameter)1004 {1005 switch (parameter.BinderAssociation)1006 {1007 case ParameterBinderAssociation.DeclaredFormalParameters:1008 DefaultParameterBinder.BindParameter(argumentToBind.ParameterName, argumentToBind.ArgumentValue, parameter.Parameter);1009 break;1010 1011 case ParameterBinderAssociation.CommonParameters:1012 CommonParametersBinder.BindParameter(argumentToBind.ParameterName, argumentToBind.ArgumentValue, parameter.Parameter);1013 break;1014 1015 case ParameterBinderAssociation.ShouldProcessParameters:1016 Diagnostics.Assert(1017 _commandMetadata.SupportsShouldProcess,1018 "The metadata for the ShouldProcessParameters should only be available if the command supports ShouldProcess");1019 1020 ShouldProcessParametersBinder.BindParameter(argumentToBind.ParameterName, argumentToBind.ArgumentValue, parameter.Parameter);1021 break;1022 1023 case ParameterBinderAssociation.PagingParameters:1024 Diagnostics.Assert(1025 _commandMetadata.SupportsPaging,1026 "The metadata for the PagingParameters should only be available if the command supports paging");1027 1028 PagingParametersBinder.BindParameter(argumentToBind.ParameterName, argumentToBind.ArgumentValue, parameter.Parameter);1029 break;1030 1031 case ParameterBinderAssociation.TransactionParameters:1032 Diagnostics.Assert(1033 _commandMetadata.SupportsTransactions,1034 "The metadata for the TransactionParameters should only be available if the command supports Transactions");1035 1036 TransactionParametersBinder.BindParameter(argumentToBind.ParameterName, argumentToBind.ArgumentValue, parameter.Parameter);1037 break;1038 1039 case ParameterBinderAssociation.DynamicParameters:1040 Diagnostics.Assert(1041 _commandMetadata.ImplementsDynamicParameters,1042 "The metadata for the dynamic parameters should only be available if the command supports IDynamicParameters");1043 1044 _dynamicParameterBinder?.BindParameter(argumentToBind.ParameterName, argumentToBind.ArgumentValue, parameter.Parameter);1045 1046 break;1047 }1048 1049 return true;1050 }1051 1052 /// <summary>1053 /// Validate the given named parameter against the specified parameter set,1054 /// and then bind the argument to the parameter.1055 /// </summary>1056 protected override void BindNamedParameter(1057 uint parameterSets,1058 CommandParameterInternal argument,1059 MergedCompiledCommandParameter parameter)1060 {1061 if ((parameter.Parameter.ParameterSetFlags & parameterSets) == 0 &&1062 !parameter.Parameter.IsInAllSets)1063 {1064 string parameterSetName = BindableParameters.GetParameterSetName(parameterSets);1065 1066 ParameterBindingException bindingException =1067 new ParameterBindingException(1068 ErrorCategory.InvalidArgument,1069 this.Command.MyInvocation,1070 errorPosition: null,1071 argument.ParameterName,1072 parameterType: null,1073 typeSpecified: null,1074 ParameterBinderStrings.ParameterNotInParameterSet,1075 "ParameterNotInParameterSet",1076 parameterSetName);1077 1078 // Might be caused by default parameter binding1079 if (!DefaultParameterBindingInUse)1080 {1081 throw bindingException;1082 }1083 else1084 {1085 ThrowElaboratedBindingException(bindingException);1086 }1087 }1088 1089 try1090 {1091 BindParameter(parameterSets, argument, parameter,1092 ParameterBindingFlags.ShouldCoerceType | ParameterBindingFlags.DelayBindScriptBlock);1093 }1094 catch (ParameterBindingException pbex)1095 {1096 if (!DefaultParameterBindingInUse)1097 {1098 throw;1099 }1100 1101 ThrowElaboratedBindingException(pbex);1102 }1103 }1104 1105 /// <summary>1106 /// Determines if a ScriptBlock can be bound directly to the type of the specified parameter.1107 /// </summary>1108 /// <param name="parameter">1109 /// The metadata of the parameter to check the type of.1110 /// </param>1111 /// <returns>1112 /// true if the parameter type is Object, ScriptBlock, derived from ScriptBlock, a1113 /// collection of ScriptBlocks, a collection of Objects, or a collection of types derived from1114 /// ScriptBlock.1115 /// False otherwise.1116 /// </returns>1117 private static bool IsParameterScriptBlockBindable(MergedCompiledCommandParameter parameter)1118 {1119 bool result = false;1120 1121 Type parameterType = parameter.Parameter.Type;1122 1123 do // false loop1124 {1125 if (parameterType == typeof(object))1126 {1127 result = true;1128 break;1129 }1130 1131 if (parameterType == typeof(ScriptBlock))1132 {1133 result = true;1134 break;1135 }1136 1137 if (parameterType.IsSubclassOf(typeof(ScriptBlock)))1138 {1139 result = true;1140 break;1141 }1142 1143 ParameterCollectionTypeInformation parameterCollectionTypeInfo = parameter.Parameter.CollectionTypeInformation;1144 if (parameterCollectionTypeInfo.ParameterCollectionType != ParameterCollectionType.NotCollection)1145 {1146 if (parameterCollectionTypeInfo.ElementType == typeof(object))1147 {1148 result = true;1149 break;1150 }1151 1152 if (parameterCollectionTypeInfo.ElementType == typeof(ScriptBlock))1153 {1154 result = true;1155 break;1156 }1157 1158 if (parameterCollectionTypeInfo.ElementType.IsSubclassOf(typeof(ScriptBlock)))1159 {1160 result = true;1161 break;1162 }1163 }1164 } while (false);1165 1166 s_tracer.WriteLine("IsParameterScriptBlockBindable: result = {0}", result);1167 return result;1168 }1169 1170 /// <summary>1171 /// Binds the specified argument to the specified parameter using the appropriate1172 /// parameter binder. If the argument is of type ScriptBlock and the parameter takes1173 /// pipeline input, then the ScriptBlock is saved off in the delay-bind ScriptBlock1174 /// container for further processing of pipeline input and is not bound as the argument1175 /// to the parameter.1176 /// </summary>1177 /// <param name="parameterSets">1178 /// The parameter set used to bind the arguments.1179 /// </param>1180 /// <param name="argument">1181 /// The argument to be bound.1182 /// </param>1183 /// <param name="parameter">1184 /// The metadata for the parameter to bind the argument to.1185 /// </param>1186 /// <param name="flags">1187 /// Flags for type coercion, validation, and script block binding.1188 ///1189 /// ParameterBindingFlags.DelayBindScriptBlock:1190 /// If set, arguments that are of type ScriptBlock where the parameter is not of type ScriptBlock,1191 /// Object, or PSObject will be stored for execution during pipeline input and not bound as1192 /// an argument to the parameter.1193 /// </param>1194 /// <returns>1195 /// True if the parameter was successfully bound. False if <paramref name="flags"/>1196 /// has the flag <see cref="ParameterBindingFlags.ShouldCoerceType"/> set and the type does not match the parameter type.1197 /// </returns>1198 internal override bool BindParameter(1199 uint parameterSets,1200 CommandParameterInternal argument,