Share via


Determining Package Status

The software distribution process can take from several minutes to several hours depending on the site settings, network topography, whether the package includes source files, and the number of distribution points that have been specified for the package. Creating the package, distribution points, programs, and advertisement instances initiates the software distribution process that is managed by the SMS Distribution Manager.

The Distribution Manager must first distribute the package's source files, which is the time-consuming aspect of the software distribution process. Only after the source files are distributed can the advertisements be offered on a site. You can use the package summarizer classes to determine if a package has been distributed and is ready to be advertised.

The level of status detail you want determines which of the following package summarizer classes to use:

The SMS_PackageStatusDetailSummarizer class gives you package status at the site level and the SMS_PackageStatusRootSummarizer class gives you package status for all sites. Note that you can only use the SMS_PackageStatusDistPointsSummarizer class if your package contains source files.

For packages that do not contain source files, an instance in the root or detail class signifies that the distribution portion of the process is complete (the value for Targeted is 0). For packages that do contain source files, when the value in the Installed property equals the value in the Targeted property, the source files have been successfully distributed.

To determine the status of a package you can either create your own polling mechanism using a timer (you can use your own timer mechanism or create a WMI timer event) that queries the summarizer for a specific package or you can register for a WMI temporary intrinsic event that polls for create instance and modify instance events on the summarizer class as the following example shows. Note that using WMI to poll for events is expensive and should be used with consideration.

  [C/C++]
//This example does not handle errors and assumes the IWbemServices object exists.
//Remember to set the authentication level to NONE in CoInitializeSecurity().

//Create the sink object that receives the package status events
class CStatusSink : public IWbemObjectSink
{
    LONG m_lRefCount;               // Reference count

public:

    CStatusSink() { m_lRefCount = 1; }
    ~CStatusSink() { }

    // IUnknown
    HRESULT __stdcall QueryInterface(REFIID riid, LPVOID *ppvObj);
    ULONG __stdcall AddRef();
    ULONG __stdcall Release();

    // IWbemObjectSink
    HRESULT __stdcall Indicate(LONG lObjectCount, IWbemClassObject **ppObjArray);
    HRESULT __stdcall SetStatus(LONG lFlags, HRESULT hResult, BSTR strParam, IWbemClassObject *pObjParam);

};


//Implement the object sink methods.
HRESULT CStatusSink::QueryInterface(REFIID riid, LPVOID *ppvObj)
{
    if( riid == IID_IUnknown || riid == IID_IWbemObjectSink )
    {
        *ppvObj = this;
        AddRef();
        return NOERROR;
    }
    else
    {
        *ppvObj = NULL;
        return E_NOINTERFACE;
    }
}


ULONG CStatusSink::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}


ULONG CStatusSink::Release()
{
    if( InterlockedDecrement(&m_lRefCount) == 0 )
    {
        delete this;
    }

    return (ULONG) m_lRefCount;
}


//The event notification results are sent to this method.
HRESULT CStatusSink::Indicate(LONG lObjectCount, IWbemClassObject **ppObjArray)
{
    HRESULT            hr;
    _variant_t         vPackageID, vTargeted, vInstalled;   //pkg status properties
    _variant_t         vTarget;                             //pkg status object
    IUnknown          *pIUnknown;
    IWbemClassObject  *pinstPkgStatus = NULL;

    for( int i = 0; i < lObjectCount; i++ )
    {
        hr = (ppObjArray[i])->Get(L"TargetInstance", 0, &vTarget, NULL, NULL);
        pIUnknown = (IUnknown *)vTarget;
        hr = pIUnknown->QueryInterface(IID_IWbemClassObject, (void **)&pinstPkgStatus);
        pIUnknown->Release();

        hr = pinstPkgStatus->Get(L"PackageID", 0, &vPackageID, NULL, NULL);
        hr = pinstPkgStatus->Get(L"Targeted", 0, &vTargeted, NULL, NULL);
        hr = pinstPkgStatus->Get(L"Installed", 0, &vInstalled, NULL, NULL);
        pinstPkgStatus->Release();
        pinstPkgStatus = NULL;

        //Do something with the information
    }

    return WBEM_NO_ERROR;
}

//SetStatus is called when you cancel the event notification query or to indicate
//an error condition.
HRESULT CStatusSink::SetStatus(LONG lFlags, HRESULT hResult, BSTR strParam, IWbemClassObject *pinstExtStatus)
{
    If (hResult != WBEM_E_CALL_CANCELLED)
    {
        //Process the error
    }

    return WBEM_NO_ERROR;
}


    //Set up the event notification query
    CStatusSink *pStatusSink = new CStatusSink();
    _bstr_t   bstrCreateQuery = L"SELECT * FROM __InstanceCreationEvent WITHIN 120 "
           L"WHERE TargetInstance.__CLASS = \"SMS_PackageStatusRootSummarizer\" ";
    _bstr_t   bstrModifyQuery = L"SELECT * FROM __InstanceModificationEvent WITHIN 120 "
           L"WHERE TargetInstance.__CLASS = \"SMS_PackageStatusRootSummarizer\" ";

    hr = pServices->ExecNotificationQueryAsync(_bstr_t(L"WQL"), bstrCreateQuery,
                                               0, NULL, pStatusSink);
    hr = pServices->ExecNotificationQueryAsync(_bstr_t(L"WQL"), bstrModifyQuery,
                                               0, NULL, pStatusSink);

    //Do something

    //When you are done, cancel the event sink.
    hr = pServices->CancelAsyncCall(pStatusSink);
    pStatusSink->Release();

[Visual Basic]
'This example does not handle errors and assumes that the SWbemServices object exists.

Dim WithEvents StatusSink As SWbemSink


'Somewhere you need to call this subroutine to kill the events.
Private Sub KillEvents()
    StatusSink.Cancel
    Set StatusSink = Nothing
End Sub


Private Sub Form_Load()
    Dim Query As String

    Set StatusSink = New SWbemSink

    'You have to specify a polling interval (Within 45) because SMS
    'does not provide an intrinsic event provider for these classes.
    Query = "SELECT * FROM __InstanceCreationEvent Within 120 " & _
            "WHERE TargetInstance.__Class = 'SMS_PackageStatusRootSummarizer' "
    Services.ExecNotificationQueryAsync StatusSink, Query

    Query = "SELECT * FROM __InstanceModificationEvent Within 120 " & _
            "WHERE TargetInstance.__Class = 'SMS_PackageStatusRootSummarizer' "
    Services.ExecNotificationQueryAsync StatusSink, Query
End Sub


'OnObjectReady processes the events. Scripting users must prefix the declared
'objects with WbemScripting, such as WbemScripting.SWbemObject.
Private Sub StatusSink_OnObjectReady(ByVal StatusEvent As SWbemObject, _
                                     ByVal EventContext As SWbemNamedValueSet)

    'do something with the status object
    'txtTargeted = StatusEvent.TargetInstance.Targeted
    'txtInstalled = StatusEvent.TargetInstance.Installed
End Sub


'OnCompleted is called when an error occurs or you cancelled the event sink.
'When you call the StatusSink.Cancel method, HResult equals &H80041032.
Private Sub StatusSink_OnCompleted(ByVal HResult As WbemErrorEnum, _
                                   ByVal ErrorObject As SWbemObject, _
                                   ByVal EventContext As SWbemNamedValueSet)

    If HResult <> wbemErrCallCancelled Then
        'Process the error.
    End If
End Sub

If you want more visibility of the distribution process, you can read the status messages for the SMS_Distribution_Manager component. The status messages trace the distribution activity and you can also use them to determine when errors occur in the distribution process.