Here is a script in which I am trying to pass a variable of type System.Data.DataTable via invoke-command to the remote host:
function Test1 {
param (
[System.Data.DataTable]
$Table
)
process {
[PSCustomObject]$OptionsInvokeCommand = @{
ComputerName = '192.168.0.5'
Authentication = 'Negotiate'
ArgumentList = [System.Data.DataTable]$Table
}
write-host $Table.GetType()
write-host $OptionsInvokeCommand.ArgumentList[0].GetType()
[System.Management.Automation.ScriptBlock]$ScriptBlock = {
param (
[System.Data.DataTable]$Table
)
process {
write-host $Table.GetType()
}
}
Invoke-Command @OptionsInvokeCommand -ScriptBlock $ScriptBlock
}
}
$Table = New-Object System.Data.DataTable
Test1 -Table $Table
It says:
Cannot process argument transformation on parameter 'Table'. Cannot convert the "System.Collections.ArrayList" value of type "System.Collections.ArrayList" to type "System.Data.DataTable".
+ CategoryInfo : InvalidData: (:) [], ParameterBindin...mationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError
+ PSComputerName : 192.168.0.5
I also tried passing a variable without an ArgumentList:
function Test2 {
param (
[System.Data.DataTable]
$Table
)
process {
[PSCustomObject]$OptionsInvokeCommand = @{
ComputerName = '192.168.0.5'
Authentication = 'Negotiate'
}
write-host $Table.GetType()
[System.Management.Automation.ScriptBlock]$ScriptBlock = {
process {
[System.Data.DataTable]$Table = $using:Table
write-host $Table.GetType()
}
}
Invoke-Command @OptionsInvokeCommand -ScriptBlock $ScriptBlock
}
}
$Table = New-Object System.Data.DataTable
Test2 -Table $Table
But It also says:
Cannot process argument transformation on parameter 'Table'. Cannot convert the "System.Collections.ArrayList" value of type "System.Collections.ArrayList" to type "System.Data.DataTable".
+ CategoryInfo : InvalidData: (:) [], ParameterBindin...mationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError
+ PSComputerName : 192.168.0.5
I tried Powershell versions 5.1 and 7.2.
How do I pass a variable System.Data.DataTable to remote host without converting it to another type?
Addition (2025.05.15):
I've read the information here (thanks @SantiagoSquarzon). PowerShell cannot transfer System.Data.DataTable to the remote server. And that's why PowerShell, hidden from the user, converts System.Data.DataTable to Xml (serialization) on the local host. And then PowerShell does the reverse conversion of Xml to System.Data.DataTable (deserialization) on the remote host. When PowerShell does this, it corrupts the data and the reverse transformation becomes impossible.
This is a PowerShell bug! Don't say it's not true. You're just used to it, but it's a bug!
Since PowerShell cannot correctly perform this operation on its own, I tried to do it for it. And I did it.:
function Test1 {
param (
[System.Data.DataTable]
$Table
)
process {
[string]$TmpPath = New-TemporaryFile
$Table.WriteXml($TmpPath, [Data.XmlWriteMode]::WriteSchema)
[string[]]$XmlString = Get-content $TmpPath
Remove-Item -Force $TmpPath
[PSCustomObject]$OptionsInvokeCommand = @{
ComputerName = '192.168.0.5'
Authentication = 'Negotiate'
ArgumentList = ,[string[]]$XmlString
}
write-host $Table.GetType()
[System.Management.Automation.ScriptBlock]$ScriptBlock = {
param (
[string[]]$XmlString
)
process {
[string]$TmpPath = New-TemporaryFile
Out-File -InputObject $XmlString -FilePath $TmpPath -Encoding utf8
$Ds = New-Object Data.DataSet
$Ds.ReadXml($TmpPath, [Data.XmlReadMode]::ReadSchema)
$Table = $Ds.Tables[0]
Remove-Item -Force $TmpPath
write-host $Table.GetType()
}
}
[void](Invoke-Command @OptionsInvokeCommand -ScriptBlock $ScriptBlock)
}
}
$Table = New-Object System.Data.DataTable NameOfTable
Test1 -Table $Table
function Test2 {
param (
[System.Data.DataTable]
$Table
)
process {
[string]$TmpPath = New-TemporaryFile
$Table.WriteXml($TmpPath, [Data.XmlWriteMode]::WriteSchema)
[string[]]$XmlString = Get-content $TmpPath
Remove-Item -Force $TmpPath
[PSCustomObject]$OptionsInvokeCommand = @{
ComputerName = '192.168.0.5'
Authentication = 'Negotiate'
}
write-host $Table.GetType()
[System.Management.Automation.ScriptBlock]$ScriptBlock = {
process {
[string[]]$XmlString = $using:XmlString
[string]$TmpPath = New-TemporaryFile
Out-File -InputObject $XmlString -FilePath $TmpPath -Encoding utf8
$Ds = New-Object Data.DataSet
$Ds.ReadXml($TmpPath, [Data.XmlReadMode]::ReadSchema)
$Table = $Ds.Tables[0]
Remove-Item -Force $TmpPath
write-host $Table.GetType()
}
}
[void](Invoke-Command @OptionsInvokeCommand -ScriptBlock $ScriptBlock)
}
}
$Table = New-Object System.Data.DataTable NameOfTable
Test2 -Table $Table
But this greatly complicates the code. Can someone simplify this?
And again, maybe it's possible to do without converting variables?