ParameterSetMetadata kann beim programmgesteuerten Erstellen eines Parameterblocks nicht generiert werden

StackOverflow https://stackoverflow.com/questions/2466210

Frage

Ich versuche, programmgesteuert einen Parameterblock für eine Funktion zu erstellen (nach dem Vorbild von dieser Blogbeitrag ).

Ich beginne mit einem CommandMetadata Objekt (aus einer vorhandenen Funktion).Ich kann das erstellen ParameterMetadaten Objekt und legen Sie Dinge wie den ParameterType, den Namen sowie einige Attribute fest.

Das Problem, auf das ich stoße, ist, dass, wenn ich das verwende GetParamBlock-Methode der ProxyCommand-Klasse, keines meiner Attribute, die ich in der Attributes-Auflistung der ParameterMetadata festgelegt habe, wird generiert.

Das dadurch verursachte Problem besteht darin, dass beim Aufruf von GetParamBlock der neue Parameter nicht mit dem entsprechenden Parameterattribut annotiert wird.

Beispiel:

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) 
War es hilfreich?

Lösung

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)

Andere Tipps

Der Grund dafür, dass es nicht angezeigt wird, liegt darin, dass Ihr NewParameter zu mindestens einem Parametersatz gehören muss.In diesem Fall sollte es Mitglied des speziellen Parametersatzes „__AllParameterSets“ sein.

Sie können dies überprüfen, indem Sie die ParameterSetMetadata-Instanz von InitialParameter kopieren.Leider kann ich nicht sofort erkennen, wie ich diese ParameterSetMetadata erhalten kann, wenn Sie keine Parameter haben, aus denen ich sie abrufen kann.Wenn man es aus dem anderen Parameter kopiert, erscheint es in der Ausgabe, aber es sind die Metadaten von InitialParameter. Das ist also nicht die Lösung, sondern nur der Grund, warum es (noch) nicht funktioniert. Ich werde diesen Beitrag aktualisieren, wenn ich es weiß aus.

-Oisin

schimpfen über:Ich war sogar sehr, sehr wütend darüber, dass wir einen Typ von System.Management.Automation.ParameterMetadata instanziieren, ihn aber nicht initialisieren können.Microsoft zerstört viel Freude an der Klassenbibliothek, indem es die Klassen durch die Verwendung privater, interner oder versiegelter Methoden einschränkt.Sie nutzen es zu oft und ohne ersichtlichen Grund.Das ist ein sehr verrücktes Bibliotheksdesign!schimpfen:

Für die Metaprogrammierung und die Erstellung von ProxyCommands (Proxyfunktionen) musste ich programmgesteuert einen Windows PowerShell-Parameter von Grund auf erstellen.Ich mag es nicht einmal, in Klassen einzubrechen und verbotene Dinge zu stehlen und zu benutzen, das kann sich ändern.Sogar der Serialisierungstrick ist der gleiche schmutzige Weg, dasselbe auf einer anderen Route zu tun.

Hier ist mein Prototyp einer Lösung.Ich erstelle eine Funktion mit einem Parameter als Text (Funktionsquellcode).Mein erster Versuch bestand darin, einen New-Item Function:\ -value {code} in das Funktionslaufwerk einzufügen und dann einen Get-Command für die neue Funktion auszuführen, um die Metadaten zu extrahieren.Aber das zeigt, dass es sich bei der Funktion um ein totes Quellcode-Pferd handelte.Es wurde nicht kompiliert.Also musste ich Invoke-Expression verwenden, um den Quellcode der Funktion zu „kompilieren“.

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

Mache ich es richtig, die Parametersätze zu konstruieren?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top