IResultObject Disposal

IResultObject Disposal

Overview

Objects based on the IResultObject interface hold unmanaged resources and must be disposed when no longer in use. This page shows the ways that IResultObject based objects are obtained using ExecuteQuery, GetInstance, GetArrayItems, and how to correctly dispose of them in each case. In almost all the sample code for SCCM SDK programming available online, the IResultObject based objects are usually not disposed correctly and rely on disposal will only occur during garbage collection.

This is the declaration of IResultObject:

namespace Microsoft.ConfigurationManagement.ManagementProvider
{
    public interface IResultObject : IComparable, IDisposable, ICloneable
}

As the interface implements IDisposable then the user of these objects is responsible for calling the Dipose method on them when they are no longer in use. Access to IResultObject is usually achieved from the return value from these methods:

  • CreateInstance / GetInstance
  • ExecuteQuery
  • GetArrayItems

CreateInstance and GetInstance.

MSDN description of CreateInstance and GetInstance.

CreateInstance and GetInstance return an IResultObject interface for a WqlResultObject object. This object implements IDisposable and must be disposed to release unmanaged resources.

ExecuteQuery

MSDN description of ExecuteQuery.

ExecuteQuery returns an IResultObject interface for a WqlQueryResultsObject object which acts as a collection of other IResultObject objects. This object implements IDisposable and must be disposed to release unmanaged resources. The fact that an IResultObject can describe both a single object and a object that references a collection of objects can be very confusing.

GetArrayItems

MSDN description of GetArrayItems.

GetArrayItems returns a List<IResultObject>. Even though List is a managed object the items contained in the list will not be automatically disposed until they are garbage collected so each item must be disposed manually when no longer required. This is done by iterating over each IResultObject in the list and calling Dispose. An easy way to achieve this is to create an extension method for IEnumerable (for handling more than just List) that provides a DisposeItems method that will iterate and dispose.

public static class Extensions
{
    /// <summary>
    ///     Runs Dispose() on all IResultObject items in an IEnumerable container such as a List. Use this when you have a list
    ///     of IResultObjects and you want to Dipose() each to release WMI resources.
    /// </summary>
    public static void DisposeItems(this IEnumerable<IResultObject> items)
    {
        foreach (IResultObject item in items)
        {
            item.Dispose();
        }
    }
}

Usage and disposal of GetArrayItems now becomes:

List<IResultObject> items = collection.GetArrayItems("CollectionRules");

// Do something with items

items.DisposeItems();

Examples and Downloads

The examples on this page can be downloaded from the GitHub repo:

IResultObject – ExecuteQuery Dispose with Using

This example demonstrates IResultObject disposal after performing a query to return a collection and enumerating that collection with foreach. The Using keyword is used to dispose of items as they are enumerated.

private void IResultObjectExecuteQueryDisposalV1(WqlConnectionManager wqlConnection)
{
    try
    {
        // As an example, get all SMS_Package objects.
        // ExecuteQuery returns IResultObject which is a WqlQueryResultsObject
        using (IResultObject queryResults = wqlConnection.QueryProcessor.ExecuteQuery("SELECT * FROM SMS_Package"))
        {
            // WqlQueryResultsObject.GetEnumerator() is implemented as:
            // yield return (object) new WqlResultObject(this.ConnectionManager, this.ConnectionManager.NamedValueDictionary, managementObject);
            foreach (IResultObject item in queryResults)
            {
                // Must call Dispose on each item enumerated
                using (item)
                {
                    string packageName = item["Name"].StringValue;
                    Console.WriteLine("Package: " + packageName);
                }
            }
        }
    }
    catch (SmsException)
    {
        // Error handling.
    }
}

IResultObject – ExecuteQuery Dispose Directly

This example demonstrates IResultObject disposal after performing a query to return a collection and enumerating that collection with foreach. The Dispose method is used to dispose of items.

private void IResultObjectExecuteQueryDisposalV2(WqlConnectionManager wqlConnection)
{
    try
    {
        // As an example, get all SMS_Package objects.
        // ExecuteQuery returns IResultObject which is a WqlQueryResultsObject
        using (IResultObject queryResults = wqlConnection.QueryProcessor.ExecuteQuery("SELECT * FROM SMS_Package"))
        {
            // WqlQueryResultsObject.GetEnumerator() is implemented as:
            // yield return (object) new WqlResultObject(this.ConnectionManager, this.ConnectionManager.NamedValueDictionary, managementObject);
            foreach (IResultObject item in queryResults)
            {
                string packageName = item["Name"].StringValue;
                Console.WriteLine("Package: " + packageName);

                // Must call Dispose on each item enumerated
                item.Dispose();
            }
        }
    }
    catch (SmsException)
    {
        // Error handling.
    }
}

IResultObject – Dispose Method Return

This example demonstrates how to return an IResultObject from a method and how the caller should dispose of it.

private void IResultObjectDisposalOfReturnValue(WqlConnectionManager wqlConnection)
{
    // Call method that returns an IResultObject - we are responsible for calling Dispose.
    using (IResultObject item = IResultObjectReturnSmsPackage(wqlConnection))
    {
        string packageName = item["Name"].StringValue;
        Console.WriteLine("Package: " + packageName);
    }
}

private IResultObject IResultObjectReturnSmsPackage(WqlConnectionManager wqlConnection)
{
    try
    {
        // As an example, get all SMS_Package objects.
        using (IResultObject queryResults = wqlConnection.QueryProcessor.ExecuteQuery("SELECT * FROM SMS_Package"))
        {
            // Just return the first item as an example
            foreach (IResultObject item in queryResults)
            {
                // Don't call Dispose on the item, the caller will now be responsible for it
                return item;
            }
        }
    }
    catch (SmsException)
    {
        // Error handling.
        return null;
    }

    // No items
    return null;
}