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?
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 GitHub issue #2861, 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With