2010-03-17 47 views
2

我試圖以編程方式爲函數創建參數塊(沿着this blog post的行)。以編程方式創建參數塊時無法生成ParameterSetMetadata

我以一個CommandMetadata對象(來自現有函數)開始。我可以創建ParameterMetadata對象並設置參數類型,名稱以及一些屬性。

我遇到的問題是,當我使用GetParamBlock method of the ProxyCommand class時,我沒有任何屬性在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) 

回答

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:字符串)[],的RuntimeException + FullyQualifiedErrorId: NullArray 如果我嘗試使用我以前的參數,我會在屬性屬性和參數集屬性中獲得不同的信息。來自參數集屬性的信息是生成的腳本。 – 2010-03-21 05:01:47

+0

是的,你不能改變參數集,否則它改變你從它得到的地方。忘記了,試試這種新的反射方法。我不相信有沒有辦法創建這個班級。 – Jaykul 2010-03-21 06:45:58

+0

是的,我很驚訝沒有這樣的構造函數。感謝您的解決方法。 – 2010-03-21 15:25:50

1

它不露面的原因是因爲你的NewParameter必須屬於至少一個參數集。在這種情況下,它應該是特殊參數集「__AllParameterSets」的成員。

您可以通過從InitialParameter複製ParameterSetMetadata實例來驗證此情況。不幸的是,我不能立即看到如何獲得這個ParameterSetMetadata,如果你沒有任何參數來抓它。從另一個參數複製它會使它出現在輸出中,但它是來自InitialParameter的元數據,所以這不是解決方案,只是它不起作用的原因(還)。當我計算它時,我會更新這篇文章出。在

-Oisin

+0

感謝您的回答。我可以從現有參數中複製元數據,但這似乎是錯誤的,尤其是因爲您可能在屬性屬性和參數集屬性之間存在差異。 ;( – 2010-03-21 05:03:33

+0

是的,這根本不是正確的方法。對不起,如果沒有清楚地看到。 – x0n 2010-03-21 16:31:21

0

咆哮: 我甚至非常,非常生氣,我們可以實例System.Management.Automation.ParameterMetadata的類型,但是我們不能initialisize它。 微軟通過使用私人或內部或密封的方式來限制類,從而摧毀了類庫的許多樂趣......他們經常使用它,而且沒有任何可以想象的理由。 這是一個非常堅果的圖書館設計! rant off:

對於元編程和創建ProxyCommands(代理函數),我需要從頭開始以編程方式創建Windows PowerShell參數。 我甚至不喜歡闖入課堂,偷取和使用vorbidden的東西,這是可以改變的。即使是序列化技巧,也是在不同路線上做同樣的骯髒方法。

這裏是我的解決方案原型。 我正在創建一個帶有參數作爲文本的函數(函數源代碼)。 我的第一個嘗試是在函數驅動器中執行New-Item Function:\ -value {code},然後對新函數執行Get-Command以提取元數據。但是,這表明,該功能是一個死馬賽克只。它沒有被編譯。 所以我不得不使用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?