2010-03-17 7 views
2

Je tente de créer par programme un bloc de paramètres pour une fonction (le long des lignes this blog post).Impossible de générer ParameterSetMetadata lors de la création d'un bloc de paramètres par programme

Je commence par un objet CommandMetadata (à partir d'une fonction existante). Je peux créer l'objet ParameterMetadata et définir des choses comme le ParameterType, le nom, ainsi que certains attributs.

Le problème que je rencontre est que lorsque j'utilise le GetParamBlock method of the ProxyCommand class, aucun de mes attributs que je définis dans la collection d'attributs de ParameterMetadata n'est généré.

Le problème que cela provoque est que lorsque le GetParamBlock est appelé, le nouveau paramètre n'est pas annoté avec l'attribut Parameter approprié.

Exemple:

function test 
{ 
    [CmdletBinding()] 
    param (
    [Parameter()] 
    $InitialParameter) 

    Write-Host "I don't matter." 
} 

$MetaData = New-Object System.Management.Automation.CommandMetaData (get-command test) 

$NewParameter = New-Object System.Management.Automation.ParameterMetadata 'NewParameter' 

$NewParameter.ParameterType = [string[]] 

$Attribute = New-Object System.Management.Automation.ParameterAttribute 
$Attribute.Position = 1 
$Attribute.Mandatory = $true 
$Attribute.ValueFromPipeline = $true 

$NewParameter.Attributes.Add($Attribute) 
$MetaData.Parameters.Add('NewParameter', $NewParameter) 


[System.Management.Automation.ProxyCommand]::GetParamBlock($MetaData) 

Répondre

5
function test 
{ 
    [CmdletBinding()] 
    param (
    [Parameter()] 
    $InitialParameter) 

    Write-Host "I don't matter." 
} 

$MetaData = New-Object System.Management.Automation.CommandMetaData (get-command test) 

$NewParameter = New-Object System.Management.Automation.ParameterMetadata 'NewParameter' 

$NewParameter.ParameterType = [string[]] 

$Attribute = New-Object System.Management.Automation.ParameterAttribute 
$Attribute.Position = 1 
$Attribute.Mandatory = $true 
$Attribute.ValueFromPipeline = $true 

$NewParameter.Attributes.Add($Attribute) 
$MetaData.Parameters.Add('NewParameter', $NewParameter) 

$ParameterSetMetadata = "System.Management.Automation.ParameterSetMetadata" 
$ParameterSetInfo = new-object psobject -Property @{ 
    Position=[Int]::MinValue 
    Flags=3 
    HelpMessage="Please Enter a Value" 
} | ForEach { 
    $_.PSTypeNames.Add("Deserialized.$ParameterSetMetadata") 
    write-Output $_ 
} 

$converter = new-object Microsoft.PowerShell.DeserializingTypeConverter 
$ConvertedSet = $converter.ConvertFrom($ParameterSetInfo,$ParameterSetMetadata, $null, $true) 

$NewParameter.ParameterSets.Add('__AllParameterSets', $ConvertedSet) 

[System.Management.Automation.ProxyCommand]::GetParamBlock($MetaData) 
+0

Tout d'abord, merci pour la réponse, mais en utilisant 'OutVariable' pour copier les informations du jeu de paramètres à partir d'une erreur - Impossible d'indexer dans un tableau nul. À la ligne: 25 ombles: 55. + $ MetaData.Parameters [ 'OutVariable'] [ParameterSets <<<< '__AllParameterSets']) + CategoryInfo: InvalidOperation: (__AllParameterSets: String) [], RuntimeException + FullyQualifiedErrorId: NullArray Si j'essaie d'utiliser mon paramètre précédent, j'obtiens des informations différentes dans la propriété d'attribut et la propriété de jeu de paramètres. Les informations de la propriété de jeu de paramètres sont les scripts générés. –

+0

Oui, vous ne pouvez pas modifier le jeu de paramètres, ou bien cela change l'endroit d'où vous l'avez obtenu. Oubliez ça, essayez cette nouvelle méthode de réflexion. Je ne peux pas croire qu'il n'y a aucun moyen de créer cette classe. – Jaykul

+0

Oui, j'étais surpris qu'il n'y avait pas de constructeur pour ça. Merci pour la solution de contournement. –

1

La raison pour laquelle il ne montre pas est parce que votre NewParameter doit appartenir à au moins un jeu de paramètres. Dans ce cas, il doit être membre du jeu de paramètres spécial "__AllParameterSets".

Vous pouvez vérifier cela en copiant l'instance ParameterSetMetadata de InitialParameter. Malheureusement, je ne peux pas voir immédiatement comment obtenir cette ParameterSetMetadata si vous n'avez aucun paramètre pour le récupérer. Le copier depuis l'autre paramètre le fait apparaître dans la sortie, mais c'est les métadonnées de InitialParameter, donc ce n'est pas la solution, seulement la raison pour laquelle ça ne marche pas (encore.) Je mettrai à jour ce post quand je le comprendrai en dehors.

-Oisin

+0

Merci pour la réponse. Je peux copier les métadonnées à partir d'un paramètre existant, mais cela semble incorrect, d'autant plus que vous pouvez alors avoir un écart entre la propriété attributes et la propriété sets de paramètres. ( –

+0

ouais, ce n'est pas la bonne façon du tout.désolé si cela ne s'est pas manifesté clairement. – x0n

0

diatribe sur: j'étais même très, très en colère que l'on peut instancier un type de System.Management.Automation.ParameterMetadata mais nous ne pouvons pas initialisize il. Microsoft détruit beaucoup de joie de la bibliothèque de classe en contrant les classes par l'utilisation de privé ou interne ou scellé ...... ils l'utilisent souvent, et sans aucune raison pensable. C'est un design de bibliothèque très fou! rant off:

Pour la métaprogrammation et la création de ProxyCommands (fonctions proxy), j'ai eu besoin de créer un paramètre Windows PowerShell à partir de rien. Je n'aime même pas entrer dans les classes et voler et utiliser des trucs vorbidden, qui est sujet à changement. Même l'astuce de sérialisation est la même façon de faire la même chose sur un itinéraire différent.

Voici mon prototype de solution. Je crée une fonction avec un paramètre comme texte (fonction sourcecode). mon premier atempt était de faire une fonction New-Item: \ -value {code} dans le lecteur de fonction, puis faire un Get-Command à la nouvelle fonction pour extraire les métadonnées. Mais cela apparaît, que la fonction était un cheval de sorcecode mort seul. Ce n'était pas compilé. J'ai donc dû utiliser Invoke-Expression pour 'compiler' le code source de la fonction.

Function New-Parameter { 

    [CmdletBinding()] 
    param(
     [Switch]$Mandatory, 
     [UInt32]$Position, 
     [Switch]$ValueFromPipeline, 
     [Switch]$ValueFromPipelineByPropertyName, 
     [Switch]$ValueFromRemainingArguments, 
     [String]$HelpMessage, 

     [Type]$Type=[Type]'System.Management.Automation.SwitchParameter', 
     [Parameter(Mandatory=$True)] 
     [String]$Name, 
     [String]$DefaultValue, 

     [Switch]$DontShow, 

     [String[]]$ParameterSetName, 
     [String[]]$Aliases, 
     # if Metadata is present the result is an System.Management.Automation.ParameterMetadata object 
     # If Metadata is absent the sourcecode for the Parameter is returned 
     [Switch]$Metadata 
    ) 

    $ParameterAttrib = [System.Collections.ArrayList]@() 

    # using GUID to create an unique function Name 
    $Guid = ([Guid]::NewGuid()).ToString() 

    # using a StringBuilder to glue the sourcecode 
    $stringBuilder = New-Object System.Text.StringBuilder 

     If($Metadata.IsPresent) { 

     # Open the Function{} block 
     [Void]$stringBuilder.AppendLine("Function $Guid {") 

     # add the [CmdletBinding()] attribute 
     [Void]$stringBuilder.AppendLine("[CmdletBinding()]") 

     # Open the Param() block 
     [Void]$stringBuilder.AppendLine("param(") 
    } 

    # query if we have one or more ParameterSetName 
    $ParmameterSetNameCount = 0 
    If(-not [String]::IsNullOrEmpty($ParameterSetName)) { 
     $ParmameterSetNameCount = @($ParameterSetName).Count 
    } 

    # Open the [Parameter()] attribut 
    [Void]$stringBuilder.Append('[Parameter(') 

    If($Mandatory.IsPresent) { 
     [Void]$ParameterAttrib.Add('Mandatory=$True') 
    } 
    If($Position) { 
     [Void]$ParameterAttrib.Add("Position=$Position") 
    } 
    If($ParmameterSetNameCount -gt 0){ 
      # in the first full blown [Parameter()] attribut allways insert the first ParametersetName 
      [Void]$ParameterAttrib.Add("ParameterSetName='$($ParameterSetName[0])'") 
    } 


    If($ValueFromPipeline.IsPresent) { 
     [Void]$ParameterAttrib.Add('ValueFromPipeline=$True') 
    } 
    If($ValueFromPipelineByPropertyName.IsPresent) { 
     [Void]$ParameterAttrib.Add('ValueFromPipelineByPropertyName=$True') 
    } 
    If($ValueFromRemainingArguments.IsPresent) { 
     [Void]$ParameterAttrib.Add('ValueFromRemainingArguments=$True') 
    } 
    If($DontShow.IsPresent) { 
     If($PSVersionTable.PSVersion.Major -lt 4) { 
      Write-Warning "The 'DontShow' attribute requires PowerShell 4.0 or above! `n Supressing the 'DontShow' attribute!" 
     } Else { 
      [Void]$ParameterAttrib.Add('DontShow') 
     } 

    } 
    If(-not [String]::IsNullOrEmpty($HelpMessage)) { 
     [Void]$ParameterAttrib.Add("HelpMessage='$HelpMessage'") 
    } 

    # generate comma separated list from array 
    [Void]$stringBuilder.Append("$($ParameterAttrib -Join ',')") 

    $ParameterAttrib.Clear() 

    # close the [Parameter()] attribut 
    [Void]$stringBuilder.AppendLine(")]") 
    $ParmameterSetLoopCounter++ 

    # If we have more then one ParametersetName 
    IF($ParmameterSetNameCount -gt 1) { 
     # add remaining parameterset names the parameter belongs to 
     for ($i = 1; $i -lt $ParmameterSetNameCount; $i++) { 
      [Void]$stringBuilder.AppendLine("[Parameter(ParameterSetName='$($ParameterSetName[$i])')]") 
     } 
    } 

    # Create Alias Attribute from Aliases 
    If(-not [String]::IsNullOrEmpty($Aliases)) { 
     [Void]$stringBuilder.AppendLine("[Alias('$($Aliases -join "','")')]") 
    } 

    # add Parameter Type 
    [Void]$stringBuilder.Append("[$($Type.Fullname)]") 

    # add the Parameter Name 
    [Void]$stringBuilder.Append("`$$Name") 

     If(-not [String]::IsNullOrEmpty($ParameterSetName)) { 
     [Void]$stringBuilder.Append("=$DefaultValue") 
     } 

    If($Metadata.IsPresent) { 
     # close the Param() block 
     [Void]$stringBuilder.AppendLine() 
     [Void]$stringBuilder.AppendLine(')') 

     # close the Function block 
     [Void]$stringBuilder.AppendLine('}') 
    } 

    # return the result 
    If($Metadata.IsPresent) { 
     # if we have to return a ParameterMetadata Object we create a temporary function 
     # because you can instatiate a ParameterMetadata Object but most of the Properties are constrained to get only and not to set! 

     # Create and 'compile' the function into the function: drive 
     Invoke-Expression ($stringBuilder.ToString()) 

     # from the temporary function we query the the ParameterMetadata and 
     # return theParameterMetadata Object 
     (Get-Command -Name $Guid -CommandType Function).Parameters.$Name 

     # remove the Function from Function: drive 
     $Null = Remove-Item Function:\$Guid -Force 

    } Else { 
     # return the sourcecode of the Parameter 
     Write-Output $stringBuilder.ToString() 
    } 

} 

#Example calls: 

# without Parametersets 
New-Parameter -Name 'Param1' -Mandatory -Position 3 -ValueFromPipeline -ValueFromPipelineByPropertyName -ValueFromRemainingArguments -HelpMessage "Give me hope Joana!" -Type 'System.String' -Aliases 'Ali1','Ali2','Ali3' -DontShow -DefaultValue 34 
New-Parameter -Name 'Param1' -Mandatory -Position 3 -ValueFromPipeline -ValueFromPipelineByPropertyName -ValueFromRemainingArguments -HelpMessage "Give me hope Joana!" -Type 'System.String' -Aliases 'Ali1','Ali2','Ali3' -DontShow -DefaultValue 34 -Metadata 

# with Parametersets 
New-Parameter -Name 'Param1' -Mandatory -Position 3 -ValueFromPipeline -ValueFromPipelineByPropertyName -ValueFromRemainingArguments -HelpMessage "Give me hope Joana!" -Type 'System.String' -ParameterSetName 'Snover','Payette' -Aliases 'Ali1','Ali2','Ali3' -DontShow -DefaultValue 34 
New-Parameter -Name 'Param1' -Mandatory -Position 3 -ValueFromPipeline -ValueFromPipelineByPropertyName -ValueFromRemainingArguments -HelpMessage "Give me hope Joana!" -Type 'System.String' -ParameterSetName 'Snover','Payette' -Aliases 'Ali1','Ali2','Ali3' -DontShow -DefaultValue 34 -Metadata 

que je fais DROIT DE CONSTRUIRE LES PARAMETERSETS?