如何将参数列表中的 class 对象传递给另一台计算机并在其上调用函数?
How do I pass a class object in a argument list to a another computer and call a function on it?
我正在尝试创建一个 class 对象并使用 Invoke-Command
调用远程计算机上 class 上的函数。当我在没有计算机名称的情况下使用 Invoke-Command
时,这工作正常,但是当我尝试在远程计算机上执行此操作时,我收到一条错误消息,指出该类型不包含我的方法。这是我用来测试这个的脚本。
$ComputerName = "<computer name>"
[TestClass]$obj = [TestClass]::new("1", "2")
Get-Member -InputObject $obj
$credentials = Get-Credential
Invoke-Command -ComputerName $ComputerName -Credential $credentials -Authentication Credssp -ArgumentList ([TestClass]$obj) -ScriptBlock {
$obj = $args[0]
Get-Member -InputObject $obj
$obj.DoWork()
$obj.String3
}
class TestClass {
[string]$String1
[string]$String2
[string]$String3
[void]DoWork(){
$this.String3 = $this.String1 + $this.String2
}
TestClass([string]$string1, [string]$string2) {
$this.String1 = $string1
$this.String2 = $string2
}
}
这是我得到的输出。
PS > .\Test-Command.ps1
cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
User: <my user>
Password for user <my user>: *
TypeName: TestClass
Name MemberType Definition
---- ---------- ----------
DoWork Method void DoWork()
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
String1 Property string String1 {get;set;}
String2 Property string String2 {get;set;}
String3 Property string String3 {get;set;}
TypeName: Deserialized.TestClass
Name MemberType Definition
---- ---------- ----------
GetType Method type GetType()
ToString Method string ToString(), string ToString(string format, System.IFormatProvider formatProvider), string IFormattable.ToString(string format, System.IFormatProvider formatProvider)
String1 Property System.String {get;set;}
String2 Property System.String {get;set;}
String3 Property {get;set;}
Method invocation failed because [Deserialized.TestClass] does not contain a method named 'DoWork'.
+ CategoryInfo : InvalidOperation: (DoWork:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
+ PSComputerName : <computer name>
我可以看到类型从 TestClass
变为 Deserialized.TestClass
,我想知道是否有办法解决这个问题?我的目标是能够将我需要的功能发送到我所在的每台机器 运行 一个脚本,这样我就不必在 Invoke-Command
脚本块的上下文中重写这些功能.
简而言之:PowerShell 在远程处理 和后台作业 期间在幕后使用的基于 XML 的序列化/反序列化仅处理少数已知类型,具有类型保真度。
像您这样的自定义 class 实例是使用 无方法 模拟 "property bags" 以 [pscustomobject]
实例 的形式出现,这就是为什么 class 实例的模拟实例在远程机器上没有方法。
有关 PowerShell serialization/deserialization 的更详细概述,请参阅 的底部部分。
正如 Mike Twc 所建议的那样,您也可以通过将 class 定义 传递给您的远程命令来解决该问题,允许您在那里重新定义 class,然后在远程会话中重新创建自定义 class 的实例。
但是,由于您无法动态获取自定义 class' 定义,因此您必须复制 class 定义代码或将其定义为 string 首先,需要通过 Invoke-Expression
, which should generally be avoided.
进行评估
一个简化的示例,它使用 而不是参数(通过 -ArgumentList
)来包含来自调用方范围的值。
# Define your custom class as a *string* first.
$classDef = @'
class TestClass {
[string]$String1
[string]$String2
[string]$String3
[void]DoWork(){
$this.String3 = $this.String1 + $this.String2
}
TestClass([string]$string1, [string]$string2) {
$this.String1 = $string1
$this.String2 = $string2
}
# IMPORTANT:
# Also define a parameter-less constructor, for convenient
# construction by a hashtable of properties.
TestClass() {}
}
'@
# Define the class in the caller's session.
# CAVEAT: This particular use of Invoke-Expression is safe, but
# it should generally be avoided.
# See https://blogs.msdn.microsoft.com/powershell/2011/06/03/invoke-expression-considered-harmful/
Invoke-Expression $classDef
# Construct an instance
$obj = [TestClass]::new("1", "2")
# Invoke a command remotely, passing both the class definition and the input object.
Invoke-Command -ComputerName . -ScriptBlock {
# Define the class in the remote session too.
Invoke-Expression $using:classDef
# Now you can cast the emulated original object to the recreated class.
$recreatedObject = [TestClass] $using:obj
# Now you can call the method...
$recreatedObject.DoWork()
# ... and output the modified property
$recreatedObject.String3
}
我正在尝试创建一个 class 对象并使用 Invoke-Command
调用远程计算机上 class 上的函数。当我在没有计算机名称的情况下使用 Invoke-Command
时,这工作正常,但是当我尝试在远程计算机上执行此操作时,我收到一条错误消息,指出该类型不包含我的方法。这是我用来测试这个的脚本。
$ComputerName = "<computer name>"
[TestClass]$obj = [TestClass]::new("1", "2")
Get-Member -InputObject $obj
$credentials = Get-Credential
Invoke-Command -ComputerName $ComputerName -Credential $credentials -Authentication Credssp -ArgumentList ([TestClass]$obj) -ScriptBlock {
$obj = $args[0]
Get-Member -InputObject $obj
$obj.DoWork()
$obj.String3
}
class TestClass {
[string]$String1
[string]$String2
[string]$String3
[void]DoWork(){
$this.String3 = $this.String1 + $this.String2
}
TestClass([string]$string1, [string]$string2) {
$this.String1 = $string1
$this.String2 = $string2
}
}
这是我得到的输出。
PS > .\Test-Command.ps1
cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
User: <my user>
Password for user <my user>: *
TypeName: TestClass
Name MemberType Definition
---- ---------- ----------
DoWork Method void DoWork()
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
String1 Property string String1 {get;set;}
String2 Property string String2 {get;set;}
String3 Property string String3 {get;set;}
TypeName: Deserialized.TestClass
Name MemberType Definition
---- ---------- ----------
GetType Method type GetType()
ToString Method string ToString(), string ToString(string format, System.IFormatProvider formatProvider), string IFormattable.ToString(string format, System.IFormatProvider formatProvider)
String1 Property System.String {get;set;}
String2 Property System.String {get;set;}
String3 Property {get;set;}
Method invocation failed because [Deserialized.TestClass] does not contain a method named 'DoWork'.
+ CategoryInfo : InvalidOperation: (DoWork:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
+ PSComputerName : <computer name>
我可以看到类型从 TestClass
变为 Deserialized.TestClass
,我想知道是否有办法解决这个问题?我的目标是能够将我需要的功能发送到我所在的每台机器 运行 一个脚本,这样我就不必在 Invoke-Command
脚本块的上下文中重写这些功能.
简而言之:PowerShell 在远程处理 和后台作业 期间在幕后使用的基于 XML 的序列化/反序列化仅处理少数已知类型,具有类型保真度。
像您这样的自定义 class 实例是使用 无方法 模拟 "property bags" 以 [pscustomobject]
实例 的形式出现,这就是为什么 class 实例的模拟实例在远程机器上没有方法。
有关 PowerShell serialization/deserialization 的更详细概述,请参阅
正如 Mike Twc 所建议的那样,您也可以通过将 class 定义 传递给您的远程命令来解决该问题,允许您在那里重新定义 class,然后在远程会话中重新创建自定义 class 的实例。
但是,由于您无法动态获取自定义 class' 定义,因此您必须复制 class 定义代码或将其定义为 string 首先,需要通过 Invoke-Expression
, which should generally be avoided.
一个简化的示例,它使用 -ArgumentList
)来包含来自调用方范围的值。
# Define your custom class as a *string* first.
$classDef = @'
class TestClass {
[string]$String1
[string]$String2
[string]$String3
[void]DoWork(){
$this.String3 = $this.String1 + $this.String2
}
TestClass([string]$string1, [string]$string2) {
$this.String1 = $string1
$this.String2 = $string2
}
# IMPORTANT:
# Also define a parameter-less constructor, for convenient
# construction by a hashtable of properties.
TestClass() {}
}
'@
# Define the class in the caller's session.
# CAVEAT: This particular use of Invoke-Expression is safe, but
# it should generally be avoided.
# See https://blogs.msdn.microsoft.com/powershell/2011/06/03/invoke-expression-considered-harmful/
Invoke-Expression $classDef
# Construct an instance
$obj = [TestClass]::new("1", "2")
# Invoke a command remotely, passing both the class definition and the input object.
Invoke-Command -ComputerName . -ScriptBlock {
# Define the class in the remote session too.
Invoke-Expression $using:classDef
# Now you can cast the emulated original object to the recreated class.
$recreatedObject = [TestClass] $using:obj
# Now you can call the method...
$recreatedObject.DoWork()
# ... and output the modified property
$recreatedObject.String3
}