Custom PowerShell Host and Converting PSObject back to base type

时光怂恿深爱的人放手 提交于 2019-11-30 09:11:20

问题


When hosting the PowerShell runtime is it possible to convert a PSObject back into its original type some how?

For example:

I have a cmdlet that calls WriteObject and pushes a collection of ClassXzy in the pipeline. When I call PowerShell.Invoke from the host end of things I retrieve a collection of PSObjects with a BaseObject property. Casting BaseObject to ClassXyz fails.

Is there any way around mapping each property value to its corresponding original object?
I'm assuming PowerShell does this somehow as you can pass PSObjects to cmdlets and they get translated to the Parameter types. But how?

I spent along time tearing into the PS assemblies with Reflector but haven't really nailed down how this magic happens.

Any ideas?

EDIT: I forgot one very important detail. The PSObject that I'm testing against is a remote object thus the BaseObject type is named Deserialized.ClassXyz. This is why I'm seeing such strange behavior.


回答1:


Keith had answered your question before you mentioned the deserialization process.

As for serialization/deserialization

I doubt it is possible to get original object. I don't know what type of serialization PowerShell uses, but if you consider simple Xml serialization, then you can realize that you can serialize only properties and nothing else.
You can't serialize bodys of its methods.
You can't serialize all it's event's subscribers (or maybe in some cases it would be possible but I'm not such a .NET expert).
And because the type (as in my example) may not be available (e.g. the assembly is present only on the remote computer), all the type information would need to be transmitted.

It is not only about the type, but about all the inheritance hierarchy and interfaces the object implements. They would be serialized somehow as well.

Just try this example:

$deserialized = Start-Job {
    Add-Type -TypeDefinition @"
    public class Parent {
        public override string ToString() { return "overriden parent"; }
        public int IntParent { get { return 1; } }
    }
    public class TestClass : Parent
    {
        public string GString() { return "this is a test string"; }
        public override string ToString() { return "overriden tostring" + System.DateTime.Now.ToString(); }
        public int IntProp { get { return 3451; } }
    }
"@
    New-Object TestClass
} | Wait-Job | Receive-Job
$deserialized.ToString()
$deserialized | gm -for

You will see, that PowerShell

  • flattens the inheritance hierarchy.
  • 'implements' only the properties
  • and because it knows the value of ToString(), it can add the result of the method as well. But as you can see the information returned from ToString() doesn't reflect the date change any more - it is frozen value.

I don't see any difference between serialization for remoting, serialization to clixml (via Export-CliXml) or when Receive-Job when considering what I've written above, so I think in both cases it is impossible.




回答2:


You can access either the BaseObject property on PSObject (which walks each PSObject until it hits the actual base object) or ImmediateBaseObject which just grabs the next object in the chain.




回答3:


You can do what is describe above, however these methods will all breakdown once PSRemoting comes into play, since you will be be accessing a Proxy Object. It's best practice to use the PSMembers & PSProperties



来源:https://stackoverflow.com/questions/2330659/custom-powershell-host-and-converting-psobject-back-to-base-type

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