Get Hyper-V guest data without XML parsing
I recently needed to query the Hyper-V KVP Exchange data for a guest VM to find the currently configured IPv4 address of the VM’s network adapter. A quick search of the Internet reveals that the Msvm_KvpExchangeComponent WMI class is the source of this information and there are at least two blog posts that cover it well:
- How to get the IP address of a virtual machine from Hyper-V
- Customizing The Key Value Pair (KVP) Integration Component
However, in both of these blogs, the actual data comes back as XML which is then parsed using XPath. The original XML looks something like this:
<INSTANCE CLASSNAME="Msvm_KvpExchangeDataItem"> <PROPERTY NAME="Data" TYPE="string"> <VALUE>169.254.103.5</VALUE> </PROPERTY> <PROPERTY NAME="Name" TYPE="string"> <VALUE>RDPAddressIPv4</VALUE> </PROPERTY> <PROPERTY NAME="Source" TYPE="uint16"> <VALUE>2</VALUE> </PROPERTY> </INSTANCE>
As soon as I saw this XML I recognised it as the DMTF CIM XML format – the same format that the new PowerShell v3 CIM Cmdlets use to transport CIM instances over HTTP (I believe). If this is the format used by PowerShell, it seemed a reasonable assumption that PowerShell or the .NET Framework must already have an implementation for deserializing this XML properly so I don’t have to code it myself.
With Reflector in hand, I started my investigation at ManagementBaseObject.GetText which converts to XML but I couldn’t find any complementary methods to go the other direction. I then proceeded to look at ManageBaseObject’s implementation of the ISerializable interface and corresponding constructor but that appears to use binary serialization of COM types.
Finally, I turned to the CimInstance class and its implementation of ISerializable and discovered the CimDeserializer. Unfortunately the CimDeserializer methods take a byte array as input and I had strings. So assuming round-tripping should work, I looked to the CimSerializer and tried passing it a CimInstance and inspected the byte array that was returned – every second byte is zero, and the rest fit within 7-bits… smells like Unicode.
Taking a small gamble I took the strings from the Msvm_KvpExchangeComponent instance, used System.Text.Encoding.Unicode to convert them to byte arrays and passed them to CimDeserializer.DeserializeInstance. Huzzah! Properly deserialized Msvm_KvpExchangeDataItem instances.
And here is the final PowerShell script to return the items for a given VM name: https://gist.github.com/jstangroome/6068782