Oh, PowerShell. Why do you do this to me? I run a query from your command line and see a bounty of properties, But, alas, when run from code, the properties that I expect are missing! Where could they be? ExtensionData? Ugh.
Now, it really seems like getting property values out of ExtensionData shouldn’t be so hard. It was difficult and annoying enough to me that I feel like I must be doing something wrong. If you know of a better way, please–PLEASE–let me know. Until then, I present to you a method for accessing the properties using reflection.
In this example, I’m essentially trying to access the following path of a PSObject:
psObject.ExtensionData.Value.Members
.FirstOrDefault(x => x.Name == "propertyName").Value.Value
Things start off pretty straightforward. We want to get the ExtensionData property, which is included in the PSObject‘s Members collection.
var extensionData = psObject.Members["ExtensionData"].Value;
extensionData has its own Members collection, so we get that next. It’s not a public property, though, so we have to dig it out using reflection. Also note that we cast the object to an IEnumerable<object>
var members = extensionData.GetType()
.GetProperty("Members", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(extensionData) as IEnumerable<object>;
Things are starting to get a little trickier. We need to reflect on the members to find the property name that we’re looking for.
var memberType = members.First().GetType();
var nameProperty = memberType.Getproperty("Name", BindingFlags.Public | BindingFlags.Instance);
var member = members
.Where(x => string.equals(propertyName, nameProperty.GetValue(x) as string,
StringComparison.InvariantCultureIgnoreCase))
.FirstOrDefault();
Now we’re in the home stretch, and we just need to get the property value. One caveat, though: the property is a data node, so you actually need to get its value. That’s right, we need Value.Value.
var valueProperty = memberType.GetProperty("Value", BindingFlags.Public | BindingFlags.Instance);
var value = valueProperty.GetValue(member);
valueProperty = value.GetType().GetProperty("Value", BindingFlags.Public | BindingFlags.Instance);
return valueProperty.GetValue(value) as string;
It got kinda gross in the end there, but mission accomplished. I found that the data types weren’t preserved in the extension data, so I had to return values as strings and cast to the appropriate data type (e.g., bool) outside my function.
Here’s the complete solution. (Error/null-checks omitted for brevity.)
string GetPropertyFromExtensionData(PSObject psObject, string propertyName)
{
var extensionData = psObject.Members["ExtensionData"].Value;
// members = extensionData.Members as IEnumerable<object>
var members = extensionData.GetType()
.GetProperty("Members", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(extensionData) as IEnumerable<object>;
// member = members.Where(x => x.Name == propertyName)
var memberType = members.First().GetType();
var nameProperty = memberType.Getproperty("Name", BindingFlags.Public | BindingFlags.Instance);
var member = members
.Where(x => string.equals(propertyName, nameProperty.GetValue(x) as string,
StringComparison.InvariantCultureIgnoreCase))
.FirstOrDefault();
// return member.Value.Value as string
var valueProperty = memberType.GetProperty("Value", BindingFlags.Public | BindingFlags.Instance);
var value = valueProperty.GetValue(member);
valueProperty = value.GetType().GetProperty("Value", BindingFlags.Public | BindingFlags.Instance);
return valueProperty.GetValue(value) as string;
}
Like this:
Like Loading...