Handling results of calling Powershell - Multivalued and string arrays.
When calling PowerShell from managed code, you need to be aware of what is being returned and to not process properties returned always as strings. When you get information you were not expecting or no info returned from the call, you should look at TypeNameOfValue and see what is returned - this often gives a clue as to the problem.
The type of data is returned from the call is not Exchange specific - its returned as things like strings, ICollection, string arrays, etc. There is generic ICollection support on MVP, this is the supported and recommended way to get to this type of data. There are no plans to remove this in the future - it would be a breaking change and would be significant.
String Arrays:
Here is an issue where calling "get-clusteredmailboxserverstatus" returns a string array. Below is how the value was handled.
if (ps.Members["OperationalMachines"].TypeNameOfValue == "System.String[]")
{
aString = (string[])ps.Members["OperationalMachines"].Value;
for (iCount = 0; aString.Length != iCount; iCount++)
{
Trace.WriteLine("(" + iCount.ToString() + ") " + aString[iCount]);
}
}
else
{
Trace.WriteLine("OperationalMachines: " + ps.Members["OperationalMachines"].Value + "\n");
}
Multivalued properties:
With multi-valued properties, you only have a few options in order to break them down to be usable. There are three options which I know of.
1. Reference some files in the “Exchange Server\Bin\”folder. This is a non-supported thing. Those DLLs may change in the future and any usage of them is not documented/known.
2. You could use reflection. This does not sound too bad until you start to use the code heavily. What you will find is that the code is pretty slow.
3. Make it an ICollection. Here you handle the property value as ICollection.
So, I'm going to cover way #3.
Sometimes multivalued properties are returned as a collection, and sometimes if there is 1 item it’s not. So, the best thing to do is to see if the property is collection and if so process it as such.
Calling:
// Start Listening
Stream strAppTraceFile = File.Create("c:\\LogResults.txt");
TextWriterTraceListener AppTextListener = new TextWriterTraceListener(strAppTraceFile);
Trace.Listeners.Add(AppTextListener);
...
// Print out the results of the pipeline execution
if (results != null && results.Count > 0) // NOTE: Be sure there are results!!!
{
DumpAllProperties(ref results);
}
My little dump routine...
//==============================================================================================
// DumpAllProperties
//
//==============================================================================================
static void DumpAllProperties(ref ICollection<PSObject> results)
{
// === All properties =================================================================
// Use this foreach for getting all properties:
Trace.WriteLine("\n");
Trace.WriteLine("------=======***************************=======------");
Trace.WriteLine("------=======***** ALL PROPERTIES ****=======------");
Trace.WriteLine("------=======***************************=======------");
Trace.WriteLine("\n");
foreach (PSObject result in results) // process result set.
{
foreach (PSProperty prop in result.Properties)
{
String propName = prop.Name;
Object propValue = prop.Value;
//string strMsg;
if (propValue != null)
{
Trace.WriteLine("----[Begin: " + prop.Name + "]---------------------------------\n");
Trace.WriteLine(" TypeNameOfValue: " + prop.TypeNameOfValue + "");
Trace.WriteLine(" MemberType: " + prop.MemberType.ToString() + "");
Trace.WriteLine(" ");
if (propValue is ICollection)
{
ICollection collection = (ICollection)propValue;
Trace.WriteLine(" Multi-valued Property:");
foreach (object value in collection)
{
Trace.WriteLine(" Value: " + value.ToString() + "");
}
Trace.WriteLine("");
}
else
{
Trace.WriteLine(" Value: " + propValue.ToString() + "");
Trace.WriteLine("");
}
}
}
}
}
Sample from the log:
----[Begin: AddressListMembership]----------------------------------------------------------
TypeNameOfValue: Microsoft.Exchange.Data.MultiValuedProperty`1[[Microsoft.Exchange.Data.Directory.ADObjectId,
Microsoft.Exchange.Data.Directory, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]
MemberType: Property
Multi-valued Property:
Value: \Default Global Address List
Value: \All Users
For further information on PowerShell, please visit the link below:
Links on Common PowerShell Automation Questions
Please also read the following:
Don't redistribute product DLLs unless you know its safe and legal to do so.
Comments
Anonymous
September 26, 2008
I've put together a list of articles which cover common questions on PowerShell automation. These linksAnonymous
April 02, 2009
Redistribution of files in the “ C:Program FilesMicrosoftExchange Server ” folder and sub-foldersAnonymous
February 29, 2012
Hello, I attempted this method when working with Exchagne 2010 remote powershell. When a multivalue attribute comes back it is of type PSObject. Getting the individual values from a PSObject seems quite opaque. In particular I am returning the EmailAddresses Property. I would like get the individaul email addresses back in a string array. Any suggestions on this? Thanks