如何将参数列表中的类对象传递给另一台计算机并在其上调用函数? [英] How do I pass a class object in a argument list to a another computer and call a function on it?

查看:39
本文介绍了如何将参数列表中的类对象传递给另一台计算机并在其上调用函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个类对象并使用 Invoke-Command 在远程机器上的类上调用函数.当我在没有计算机名称的情况下使用 Invoke-Command 时,这可以正常工作,但是当我尝试在远程计算机上执行此操作时,我收到一条错误消息,指出该类型不包含我的方法.这是我用来测试这个的脚本.

I am attempting to create a class object and use Invoke-Command to call a function on the class on a remote machine. When I use Invoke-Command with no computer name this works fine but when I attempt to do this on a remote computer I get an error saying the that the type does not contain my method. Here is the script I am using for testing this.

$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 脚本的上下文中重写函数阻止.

I can see that the type changes from TestClass to Deserialized.TestClass and I am wondering if there is a way around this? My goal is to be able to ship the functions I need to each of the machines I am running a script on so that I don't have to rewrite the functions in the context of the Invoke-Command script block.

推荐答案

简而言之:PowerShell 在远程处理期间在幕后使用的基于 XML 的序列化/反序列化和仅在后台作业中类型保真度处理少量已知类型.

In short: The XML-based serialization / deserialization that PowerShell employs behind the scenes during remoting and in background jobs only handles a handful of known types with type fidelity.

像您这样的自定义类的实例模拟无方法[pscustomobject] 实例,这就是为什么你的类实例的模拟实例在远程机器上没有方法.

Instances of custom classes such as yours are emulated with method-less "property bags" in the form of [pscustomobject] instances, which is why the emulated instances of your class instances have no methods on the remote machine.

有关 PowerShell 序列化/反序列化的更详细概述,请参阅此答案的底部部分.

For a more detailed overview of PowerShell's serialization/deserialization, see the bottom section of this answer.

正如Mike Twc 所建议的那样,您可以通过传递您的类来解决该问题定义以及您的远程命令,允许您在那里重新定义类,然后在远程会话中重新创建自定义类的实例.

As Mike Twc suggests, you can work around the problem by passing your class definition along to your remote command as well, allowing you to redefine the class there and then recreate instances of your custom class in the remote session.

但是,由于您无法动态获取自定义类的定义,因此您必须复制类定义代码或将其定义为 string 开始,需要通过 Invoke-Expression,其中一般应该避免.

However, since you cannot dynamically obtain a custom class' definition, you'd have to either duplicate the class definition code or define it as a string to begin with, requiring evaluation via Invoke-Expression, which should generally be avoided.

一个简化的例子,它使用 $using: 范围 而不是参数(通过-ArgumentList) 以包含来自调用方范围的值.

A simplified example, which uses the $using: scope rather than parameters (via -ArgumentList) to include values from the caller's scope.

# 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
}

这篇关于如何将参数列表中的类对象传递给另一台计算机并在其上调用函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆