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

https://blogs.msdn.com/webdav_101/archive/2008/09/26/links-on-common-powershell-automation-questions.aspx

Please also read the following:

Don't redistribute product DLLs unless you know its safe and legal to do so.

https://blogs.msdn.com/webdav_101/archive/2009/04/02/don-t-redistribute-product-dlls-unless-you-know-its-safe-and-legal-to-do-so.aspx