2

我正在尝试以编程方式为函数创建一个参数块(按照此博客文章的内容)。

我从一个CommandMetadata对象(来自现有函数)开始。我可以创建ParameterMetadata对象并设置诸如 ParameterType、名称以及一些属性之类的内容。

我遇到的问题是,当我使用ProxyCommand 类的 GetParamBlock 方法时,我在 ParameterMetadata 的 Attributes 集合中设置的属性都不会生成。

这导致的问题是,当调用 GetParamBlock 时,新参数未使用适当的 Parameter 属性进行注释。

例子:

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) 
4

3 回答 3

4
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)
于 2010-03-20T20:27:49.683 回答
1

它不显示的原因是因为您的 NewParameter 需要属于至少一个参数集。在这种情况下,它应该是特殊参数集“__AllParameterSets”的成员。

您可以通过从 InitialParameter 复制 ParameterSetMetadata 实例来验证这一点。不幸的是,如果您没有任何参数可以从中获取,我无法立即看到如何获取此 ParameterSetMetadata。从另一个参数复制它使其出现在输出中,但它是来自 InitialParameter 的元数据,所以这不是解决方案,只是它不起作用的原因(还)。我会在我想出来的时候更新这篇文章出去。

-Oisin

于 2010-03-20T19:41:21.483 回答
0

咆哮:我什至非常非常生气,我们可以实例化 System.Management.Automation.ParameterMetadata 类型,但我们无法初始化它。微软通过使用私有或内部或密封的类来限制类,破坏了类库的许多乐趣......他们经常使用它,而且没有任何可以想到的理由。这是一个非常疯狂的图书馆设计!吐槽:

对于元编程和创建 ProxyCommands(代理函数),我需要从头开始以编程方式创建 Windows PowerShell 参数。我什至不喜欢闯入课堂偷窃和使用 vorbidden 的东西,这可能会发生变化。即使是序列化技巧也是在不同的路线上做同样的肮脏方式。

这是我的解决方案原型。我正在创建一个带有参数作为文本的函数(函数源代码)。我的第一次尝试是在函数驱动器中执行一个 New-Item Function:\ -value {code},然后对新函数执行一个 Get-Command 以提取元数据。但这表明,该函数是一匹死去的 sorcecode 只有马。它没有被编译。所以我不得不使用 Invoke-Expression 来“编译”函数的源代码。

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

我是否正确构建参数集?

于 2014-06-20T06:57:08.683 回答