2010-03-17 2 views
2

Я пытаюсь программно создать блок параметров для функции (по линиям this blog post).Не удается создать ParameterSetMetadata при программном создании блока параметров

Я начинаю с объекта CommandMetadata (из существующей функции). Я могу создать объект ParameterMetadata и задать такие вещи, как ParameterType, имя, а также некоторые атрибуты.

Проблема, с которой я столкнулся, заключается в том, что когда я использую GetParamBlock method of the ProxyCommand class, ни один из моих атрибутов, которые я установил в коллекции атрибутов ParameterMetadata, не генерируется.

Проблема заключается в том, что при вызове 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) 

ответ

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

Во-первых, спасибо за ответ, однако используя «OutVariable», чтобы скопировать информацию о параметрах из числа ошибок - Невозможно индексировать нулевой массив. В строке: 25 символ: 55. + $ MetaData.Parameters [ 'OutVariable'] ParameterSets [<<<< '__AllParameterSets']) + CategoryInfo: InvalidOperation: (__AllParameterSets: String) [], RuntimeException + FullyQualifiedErrorId: NullArray Если я попытаюсь использовать мой предыдущий параметр, я получаю различную информацию в свойстве атрибута и свойстве набора параметров. Информация из свойства набора параметров - это сценарий. –

+0

Да, вы не можете изменить набор параметров, иначе он изменит место, из которого вы его получили. Забудьте об этом, попробуйте этот новый метод отражения. Не могу поверить, что создать этот класс невозможно. – Jaykul

+0

Да, я был удивлен, что для этого не было конструктора. Спасибо за обходной путь. –

1

Причина, по которой не проявляется в том, что ваш NewParameter должен принадлежать по крайней мере, одного набора параметров. В этом случае он должен быть членом специального набора параметров «__AllParameterSets».

Это можно проверить, скопировав экземпляр ParameterSetMetadata из InitialParameter. К сожалению, я не могу сразу увидеть, как получить этот ParameterSetMetadata, если у вас нет каких-либо параметров для его захвата. Копирование его из другого параметра заставляет его отображаться на выходе, но это метаданные из InitialParameter, так что это не решение, только причина, по которой он не работает (пока). Я обновлю этот пост, когда буду это понимать вне.

-Oisin

+0

Спасибо за ответ. Я могу скопировать метаданные из существующего параметра, но это кажется неправильным, особенно потому, что вы можете иметь расхождение между свойством атрибутов и свойством свойств параметров. ; ( –

+0

да, это совсем не так.извините, если это не получилось. – x0n

0

разглагольствовать на: Я даже очень, очень зол, что мы можем создать экземпляр Тип System.Management.Automation.ParameterMetadata, но мы не можем initialisize его. Microsoft разрушает много радости в библиотеке классов, преследуя классы с помощью частных или внутренних или запечатанных ... они используют его часто и без какой-либо мыслимой причины. Это очень дизайн библиотеки орехов! rant off:

Для метапрограммирования и создания ProxyCommands (прокси-функций) мне потребовалось создать параметр PowerShell Windows программно с нуля. Я даже не люблю врываться в классы и воровать и использовать vorbidden вещи, которые могут быть изменены. Даже трюк сериализации - это тот же грязный способ сделать то же самое на другом маршруте.

вот мой прототип решения. Я создаю функцию с параметром как текст (function sourcecode). Моей первой задачей было сделать функцию нового элемента: \ -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 

я делаю ПРАВО Постройте PARAMETERSETS?

Смежные вопросы