Property passed to Invoke-Command changes type from IDictionary to HashTable

安稳与你 提交于 2019-12-05 04:03:24

问题


I've been getting an error running Invoke-Command where the script block takes a parameter of type dictionary:

Cannot process argument transformation on parameter 'dictionary'. Cannot convert the "System.Collections.Hashtable" value of type "System.Collections.Hashtable" to type "System.Collections.Generic.IDictionary`2[System.String,System.String]". At line:7 char:1 + Invoke-Command -ComputerName . -ArgumentList $dictionary -ScriptBlock ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidData: (:) [], ParameterBindin...mationException + FullyQualifiedErrorId : ParameterArgumentTransformationError + PSComputerName : localhost

After a lot of digging I was able to reduce the script to the the MVP below to show the root of this issue:

[System.Collections.Generic.IDictionary[string, string]]$dictionary = New-Object -TypeName 'System.Collections.Generic.Dictionary[string, string]' 
$dictionary.Add('one', 'hello')
$dictionary.Add('two', 'world')
Write-Verbose "Main Script $($dictionary.GetType().FullName)" -Verbose #outputs: VERBOSE: Before System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
Invoke-Command -ComputerName . -ArgumentList $dictionary -ScriptBlock {
    Param (
        #[System.Collections.Generic.IDictionary[string, string]] #if I leave this in I get a conversion error
        $dictionary
    )
    Write-Verbose "Function before $($dictionary.GetType().FullName)" -Verbose #outputs: VERBOSE: After System.Collections.Hashtable
    function Poc {} #this line seems to cause the `$dictionary` to become a HashTable
    Write-Verbose "Function after $($dictionary.GetType().FullName)" -Verbose #outputs: VERBOSE: After System.Collections.Hashtable

}

It seems that if the script block for Invoke-Command includes any inline functions then the parameter is automatically converted to a HashTable; whilst if the script block doesn't contain any nested function definitions the parameter is left as a System.Collections.Generic.IDictionary[string, string].

Am I misusing this feature / is there a common workaround? Or is this a just a bug in PowerShell?


回答1:


This is a known problem with the deserialization code that is involved in PowerShell remoting (which is what Invoke-Command -ComputerName is based on), unfortunately, as of PowerShell Core 7.0.0-preview.2:

On deserialization, any object that implements the IDictionary interface is incorrectly assumed to always be a regular, non-generic [hashtable][1] and deserialized as such.

This is especially problematic when the original object was an ordered dictionary[2], because the ordering of the keys is lost, as is the ability to index into them.

See this GitHub issue, which is tagged as "up for grabs", meaning that the community is free to offer a PR that fixes the problem.


[1] System.Collections.Hashtable, creatable in PowerShell with @{ ... } literals, where both the keys and values are [object]-typed.

[2] E.g., System.Collections.Specialized.OrderedDictionary, creatable in PowerShell with [ordered] @{ ... } literals, also with [object]-typed keys and values (non-generic); suprisingly, as of this writing, there is no generic ordered dictionary type - see this question.



来源:https://stackoverflow.com/questions/57572937/property-passed-to-invoke-command-changes-type-from-idictionary-to-hashtable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!