示例#1
0
/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_CREATE_OBJECT_WITH_PROPERTIES_ONLY
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES: Contains an IPortableDeviceValues, describing
 *      properties of the new object.  At the very least, it will contain:
 *      -  WPD_OBJECT_NAME: The object name.
 *      -  WPD_PARENT_ID: Identifies the parent object.  The object should be inserted as a child of
 *         this parent (e.g. this would be the target directory in a file system based device).
 *
 *  The driver should:
 *  - Create the object, and return its ID in WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID.
 */
HRESULT WpdObjectManagement::OnCreateObjectWithPropertiesOnly(
    IPortableDeviceValues*  pParams,
    IPortableDeviceValues*  pResults)
{

    HRESULT                         hr              = S_OK;
    LPWSTR                          pszContext      = NULL;
    CComPtr<IPortableDeviceValues>  pValues;
    CComPtr<IPortableDeviceValues>  pEventParams;

    // Get the Object Properties
    if (hr == S_OK)
    {
        hr = pParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES, &pValues);
        CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES");
    }

    // Save the object to the device here.
    if (hr == S_OK)
    {
        hr = m_pFakeDevice->SaveNewObject(pValues, &pszContext);
        CHECK_HR(hr, "Failed to save new (properties only) object to device");
    }

    if (hr == S_OK)
    {
        hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID, pszContext);
        CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID");
    }

    // Send the event
    HRESULT hrTemp = S_OK;
    // Get the object properties used for sending the event
    hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pszContext, &pEventParams);
    CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pszContext);
    if (hrTemp == S_OK)
    {
        // Set the event-specific parameters
        hrTemp = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_ADDED);
        CHECK_HR(hrTemp, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID");

        // Send the Event
        if (hrTemp == S_OK)
        {
            PostWpdEvent(pParams, pEventParams);
        }
    }

    // Free the memory.  CoTaskMemFree ignores NULLs so no need to check.
    CoTaskMemFree(pszContext);

    return hr;
}
/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_CREATE_OBJECT_WITH_PROPERTIES_ONLY
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES: Contains an IPortableDeviceValues, describing
 *      properties of the new object.  At the very least, it will contain:
 *      -  WPD_OBJECT_NAME: The object name.
 *      -  WPD_PARENT_ID: Identifies the parent object.  The object should be inserted as a child of
 *         this parent (e.g. this would be the target directory in a file system based device).
 *
 *  The driver should:
 *  - Create the object, and return its ID in WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID.
 */
HRESULT WpdObjectManagement::OnCreateObjectWithPropertiesOnly(
    _In_    IPortableDeviceValues*  pParams,
    _In_    IPortableDeviceValues*  pResults)
{
    HRESULT hr          = S_OK;
    LPWSTR  pszObjectID = NULL;

    CComPtr<IPortableDeviceValues>  pObjectProperties;
    CComPtr<IPortableDeviceValues>  pEventParams;

    hr = CoCreateInstance(CLSID_PortableDeviceValues,
                          NULL,
                          CLSCTX_INPROC_SERVER,
                          IID_IPortableDeviceValues,
                          (VOID**) &pEventParams);
    CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues");

    // Get the Object Properties
    if (SUCCEEDED(hr))
    {
        hr = pParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES, &pObjectProperties);
        CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES");
    }

    // Save the object to the device here.
    if (SUCCEEDED(hr))
    {
        ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams);
        hr = m_pDevice->CreatePropertiesOnlyObject(Scope, pObjectProperties, pEventParams, &pszObjectID);
        CHECK_HR(hr, "Failed to save new (properties only) object to device");
    }

    if (SUCCEEDED(hr))
    {
        // Create is successful, so we post an event.  
        // This is best effort, so errors are ignored
        HRESULT hrEvent = PostWpdEvent(pParams, pEventParams);  
        CHECK_HR(hrEvent, "Failed post event for new object [%ws] (errors ignored)", pszObjectID);
    }

    if (SUCCEEDED(hr))
    {
        hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID, pszObjectID);
        CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID");
    }

    // Free the memory.  CoTaskMemFree ignores NULLs so no need to check.
    CoTaskMemFree(pszObjectID);

    return hr;
}
示例#3
0
HRESULT WpdObjectManagement::CommitNewObject(
    __in    ObjectManagementContext*    pContext,
    __in    LPCWSTR                     pszContext,
    __in    IPortableDeviceValues*      pParams,
    __out   IPortableDeviceValues*      pResults)
{

    LPWSTR pszObjectID = NULL;

    // Save the new object
    HRESULT hr = m_pFakeDevice->SaveNewObject(pContext->ObjectProperties, &pszObjectID);
    CHECK_HR(hr, "Failed to save new object [%ws] to device", pContext->Name);

    // Let the caller know the new object's ID.
    if (hr == S_OK)
    {
        hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID, pszObjectID);
        CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID");
    }

    if (hr == S_OK)
    {
        // Send the event
        HRESULT hrTemp = S_OK;
        CComPtr<IPortableDeviceValues> pEventParams;

        // Get the object properties used for sending the event
        hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pszObjectID, &pEventParams);
        CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pszObjectID);

        if (hrTemp == S_OK)
        {
            // Set the event-specific parameters
            hrTemp = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_ADDED);
            CHECK_HR(hrTemp, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID");

            hrTemp = pEventParams->SetStringValue(WPD_EVENT_PARAMETER_OBJECT_CREATION_COOKIE, pszContext);
            CHECK_HR(hrTemp, "Failed to add WPD_EVENT_PARAMETER_OBJECT_CREATION_COOKIE");
            
            // Send the Event
            if (hrTemp == S_OK)
            {
                PostWpdEvent(pParams, pEventParams);
            }
        }
    }
    
    CoTaskMemFree(pszObjectID);

    return hr;
}
示例#4
0
HRESULT WpdObjectManagement::UpdateObject(
    __in    ObjectManagementContext*    pContext,
    __in    IPortableDeviceValues*      pParams,
    __out   IPortableDeviceValues*      pResults)
{
    // For convenience, the ObjectID was stored in Name for object updates
    LPCWSTR pszObjectId = pContext->Name;
    
    // Update the object
    HRESULT hr = m_pFakeDevice->UpdateContentObject(pszObjectId, pContext->ObjectProperties);
    CHECK_HR(hr, "Failed to update object [%ws] to device", pszObjectId);

    // Set the object ID.
    if (hr == S_OK)
    {
        hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID, pszObjectId);
        CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID");
    }

    if (hr == S_OK)
    {
        // Send the event
        HRESULT hrTemp = S_OK;
        CComPtr<IPortableDeviceValues> pEventParams;

        // Get the object properties used for sending the event
        hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pContext->Name, &pEventParams);
        CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pszObjectId);

        if (hrTemp == S_OK)
        {
            // Set the event-specific parameters
            hrTemp = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_UPDATED);
            CHECK_HR(hrTemp, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID");

            // Send the Event
            if (hrTemp == S_OK)
            {
                PostWpdEvent(pParams, pEventParams);
            }
        }
    }
    
    return hr;
}
示例#5
0
/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_COPY_OBJECTS
 *  command.
 *
 * This command will copy the specified objects to the destination folder.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS : the ObjectIDs, indicating which objects to copy.  These may
 *      be folder objects.
 *  - WPD_PROPERTY_OBJECT_MANAGEMENT_DESTINATION_FOLDER_OBJECT_ID: Indicates the destination folder for the copy operation.
 *
 *  The driver should:
 *  - Attempt to copy the object specified in WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS to the folder specified in
 *    WPD_PROPERTY_OBJECT_MANAGEMENT_DESTINATION_FOLDER_OBJECT_ID.
 * - Fill out the operation results in WPD_PROPERTY_OBJECT_MANAGEMENT_COPY_RESULTS.  It contains an IPortableDevicePropVariantCollection of
 *   VT_ERROR values indicating the success or failure of the operation for that element.
 *   Order is implicit, i.e. the first element of WPD_PROPERTY_OBJECT_MANAGEMENT_COPY_RESULTS corresponds to the first element of WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS and so on.
 *  - The driver should return:
 *      - S_OK if all objects were copied successfully.
 *      - S_FALSE if any object copy failed.
 *      - An error return indicates that the driver did not copy any objects, and
 *        WPD_PROPERTY_OBJECT_MANAGEMENT_COPY_RESULTS is ignored.
 */
HRESULT WpdObjectManagement::OnCopy(
    IPortableDeviceValues*  pParams,
    IPortableDeviceValues*  pResults)
{

    HRESULT hr                    = S_OK;
    LPWSTR  pszDestFolderObjectID = NULL;
    CComPtr<IPortableDevicePropVariantCollection> pObjectIDs;
    CComPtr<IPortableDevicePropVariantCollection> pCopyResults;
    BOOL    bCopyFailed           = FALSE;
    VARTYPE vt                    = VT_EMPTY;

    if (hr == S_OK)
    {
        hr = pParams->GetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS, &pObjectIDs);
        CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS");
    }

    // Ensure that this is a collection of VT_LPWSTR
    if (hr == S_OK)
    {
        hr = pObjectIDs->GetType(&vt);
        CHECK_HR(hr, "Failed to get the VARTYP of WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS");
        if (hr == S_OK)
        {
            if (vt != VT_LPWSTR)
            {
                hr = E_INVALIDARG;
                CHECK_HR(hr, "WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS is not a collection of VT_LPWSTR");
            }
        }
    }

    if (hr == S_OK)
    {
        hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_DESTINATION_FOLDER_OBJECT_ID, &pszDestFolderObjectID);
        CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_DESTINATION_FOLDER_OBJECT_ID");
    }

    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDevicePropVariantCollection,
                              (VOID**) &pCopyResults);
        CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDevicePropVariantCollection");
    }


    if (hr == S_OK)
    {
        DWORD   cObjects    = 0;
        // Loop through the object list and attempt to copy
        hr = pObjectIDs->GetCount(&cObjects);
        CHECK_HR(hr, "Failed to get number of objects to copy");

        if (hr == S_OK)
        {
            for(DWORD dwIndex = 0; dwIndex < cObjects; dwIndex++)
            {
                CComPtr<IPortableDeviceValues> pEventParams;
                LPWSTR  pszNewObjectID = NULL;
                HRESULT hrTemp = S_OK;
                PROPVARIANT pv = {0};

                PropVariantInit(&pv);
                // Get the next Object to copy
                hrTemp = pObjectIDs->GetAt(dwIndex, &pv);
                CHECK_HR(hr, "Failed to get next object id at index %d", dwIndex);
                if (hrTemp == S_OK)
                {
                    // Copy this object
                    hrTemp = m_pFakeDevice->CopyObject(pv.pwszVal, pszDestFolderObjectID, &pszNewObjectID);
                    CHECK_HR(hrTemp, "Failed to copy object [%ws] to folder [%ws]", pv.pwszVal, pszDestFolderObjectID);

                    if(FAILED(hrTemp))
                    {
                        bCopyFailed = TRUE;
                    }

                    PROPVARIANT pvResult = {0};

                    PropVariantInit(&pvResult);

                    // Save this result
                    pvResult.vt       = VT_ERROR;
                    pvResult.scode    = hrTemp;
                    hrTemp = pCopyResults->Add(&pvResult);
                    CHECK_HR(hrTemp, "Failed to add result for [%ws] to list of results", pv.pwszVal);

                    PropVariantClear(&pvResult);

                    if (hrTemp == S_OK)
                    {
                        // Send the event
                        // Get the object properties used for sending the event
                        hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pszNewObjectID, &pEventParams);
                        CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pszNewObjectID);
                        if (hrTemp == S_OK)
                        {
                            // Set the event-specific parameters
                            hrTemp = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_ADDED);
                            CHECK_HR(hrTemp, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID");

                            // Send the Event
                            if (hrTemp == S_OK)
                            {
                                PostWpdEvent(pParams, pEventParams);
                            }
                        }
                    }

                }

                if (pszNewObjectID != NULL)
                {
                    CoTaskMemFree(pszNewObjectID);
                    pszNewObjectID = NULL;
                }

                PropVariantClear(&pv);
            }
        }
    }

    // Set the results
    if (hr == S_OK)
    {
        hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_MANAGEMENT_COPY_RESULTS, pCopyResults);
        CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_COPY_RESULTS");
    }

    CoTaskMemFree(pszDestFolderObjectID);

    // If an object failed to copy, make sure we return S_FALSE
    if ((hr == S_OK) && (bCopyFailed))
    {
        hr = S_FALSE;
    }

    return hr;
}
示例#6
0
/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_DELETE_OBJECTS
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS: the ObjectIDs, indicating which objects to delete.  These may
 *      contain children.
 *  - WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_OPTIONS: Flag parameter indicating delete options. Must be one
 *                  of the following:
 *                 - PORTABLE_DEVICE_DELETE_NO_RECURSION - Deletes the
 *                          object only.  This should fail if children exist.
 *                 - PORTABLE_DEVICE_DELETE_WITH_RECURSION - Deletes this
 *                          object and all children.
 *
 *  The driver should:
 *  - If the flag is PORTABLE_DEVICE_DELETE_NO_RECURSION the driver should delete the
 *    specified object only.  If the object still has children the driver should not delete
 *    the object and instead return HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION).
 *  - If the flag is PORTABLE_DEVICE_DELETE_WITH_RECURSION the driver should delete the
 *    specified object and all of its children.
 *  - Fill out the operation results in WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS.  It contains an IPortableDevicePropVariantCollection of
 *    VT_ERROR values indicating the success or failure of the operation for that element.
 *    Order is implicit, i.e. the first element of WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS corresponds to the first element of WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS and so on.
 *  - The driver should return:
 *      - S_OK if all objects were deleted successfully.
 *      - S_FALSE if any object delete failed.
 *      - An error return indicates that the driver did not delete any objects, and
 *        WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS is ignored.
 */
HRESULT WpdObjectManagement::OnDelete(
    IPortableDeviceValues*  pParams,
    IPortableDeviceValues*  pResults)
{

    HRESULT hr              = S_OK;
    DWORD   dwOptions       = PORTABLE_DEVICE_DELETE_NO_RECURSION;
    BOOL    bDeleteFailed   = FALSE;
    CComPtr<IPortableDevicePropVariantCollection> pObjectIDs;
    CComPtr<IPortableDevicePropVariantCollection> pDeleteResults;
    CComPtr<IPortableDeviceValues> pEventParams;
    VARTYPE vt              = VT_EMPTY;

    if (hr == S_OK)
    {
        hr = pParams->GetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS, &pObjectIDs);
        CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS");
    }

    // Ensure that this is a collection of VT_LPWSTR
    if (hr == S_OK)
    {
        hr = pObjectIDs->GetType(&vt);
        CHECK_HR(hr, "Failed to get the VARTYP of WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS");
        if (hr == S_OK)
        {
            if (vt != VT_LPWSTR)
            {
                hr = E_INVALIDARG;
                CHECK_HR(hr, "WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS is not a collection of VT_LPWSTR");
            }
        }
    }

    if (hr == S_OK)
    {
        hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_OPTIONS, &dwOptions);
        CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_OPTIONS");
    }

    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDevicePropVariantCollection,
                              (VOID**) &pDeleteResults);
        CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDevicePropVariantCollection");
    }


    if (hr == S_OK)
    {
        DWORD   cObjects    = 0;
        // Loop through the object list and attempt to delete
        hr = pObjectIDs->GetCount(&cObjects);
        CHECK_HR(hr, "Failed to get number of objects to delete");

        if (hr == S_OK)
        {
            for(DWORD dwIndex = 0; dwIndex < cObjects; dwIndex++)
            {
                HRESULT hrTemp = S_OK;
                PROPVARIANT pv = {0};

                PropVariantInit(&pv);
                // Get the next Object to delete
                hr = pObjectIDs->GetAt(dwIndex, &pv);
                CHECK_HR(hr, "Failed to get next object id at index %d", dwIndex);
                if (hr == S_OK)
                {
                    pEventParams = NULL;
                    // Get the object properties used for sending the event.  Ignore errors (these are expected in some cases
                    // e.g. app is sending object ID that doesn't exist), since this relates to the event, not the delete operation,
                    // and we return results for the delete (an error posting the event is non-fatal).
                    hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pv.pwszVal, &pEventParams);
                    CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pv.pwszVal);

                    HRESULT     hrDelete = S_OK;
                    PROPVARIANT pvResult = {0};

                    PropVariantInit(&pvResult);

                    hrDelete = m_pFakeDevice->DeleteObject(dwOptions, pv.pwszVal);
                    CHECK_HR(hrDelete, "Failed to delete object [%ws]", pv.pwszVal);

                    if(FAILED(hrDelete))
                    {
                        bDeleteFailed = TRUE;
                    }

                    // Save this result
                    pvResult.vt       = VT_ERROR;
                    pvResult.scode    = hrDelete;
                    hrTemp = pDeleteResults->Add(&pvResult);
                    PropVariantClear(&pvResult);
                    CHECK_HR(hrTemp, "Failed to add result for [%ws] to list of results", pv.pwszVal);

                    if ((hrDelete == S_OK) && (hrTemp == S_OK))
                    {
                        HRESULT hrEvent = S_OK;
                        // Set the event-specific parameters
                        hrEvent = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_REMOVED);
                        CHECK_HR(hrEvent, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID");

                        // Send the Event
                        if (hrEvent == S_OK)
                        {
                            PostWpdEvent(pParams, pEventParams);
                        }
                    }
                    PropVariantClear(&pv);
                }
                else
                {
                    break;
                }
            }
        }
    }

    // Set the results
    if (hr == S_OK)
    {
        hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS, pDeleteResults);
        CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS");
    }

    // If an object failed to delete, make sure we return S_FALSE
    if ((hr == S_OK) && (bDeleteFailed))
    {
        hr = S_FALSE;
    }

    return hr;
}
/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_NEXT
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT: the context the driver returned to
 *      the client in OnGetValuesByObjectListStart.
 *
 *  The driver should:
 *  - Write the next set of property values, and return the write results in WPD_PROPERTY_OBJECT_PROPERTIES_BULK_WRITE_RESULTS.
 *    If there are no more properties to be written, an empty collection should be returned.
 *  - It is up to the driver to write as many object property values as it wants.  If zero write results are returned
 *    it is assumed the bulk operation is complete and the WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_END
 *    will be called next.
 *
 *  - S_OK should be returned if the collection can be returned successfully.
 *  - Any error return indicates that the driver did not fill in any results, and the caller will
 *      not attempt to unpack any property values.
 */
HRESULT WpdObjectPropertiesBulk::OnSetValuesByObjectListNext(
    _In_    IPortableDeviceValues*  pParams,
    _In_    IPortableDeviceValues*  pResults)
{
    HRESULT                                  hr          = S_OK;
    LPWSTR                                   pwszContext = NULL;
    BulkPropertiesContext*                   pContext    = NULL;
    DWORD                                    cObjects    = 0;
    CComPtr<IPortableDeviceValues>           pEventParams;
    CComPtr<IPortableDeviceValuesCollection> pWriteResults;
    CComPtr<IPortableDeviceValuesCollection> pValuesCollection;

    hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT, &pwszContext);
    CHECK_HR(hr, "Failed to get WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT from IPortableDeviceValues");

    // Get the bulk property operation context
    if (SUCCEEDED(hr))
    {
        hr = GetClientContext(pParams, pwszContext, (IUnknown**) &pContext);
        CHECK_HR(hr, "Failed to get bulk property context");
    }

    // Make sure the the collection holds a ValuesCollection, then get the number of elements.
    if (SUCCEEDED(hr))
    {
        if(pContext->ValuesCollection != NULL)
        {
            hr = pContext->ValuesCollection->GetCount(&cObjects);
            CHECK_HR(hr, "Failed to get number of objectIDs from bulk properties context");
        }
        else
        {
            hr = E_INVALIDARG;
            CHECK_HR(hr, "Incorrect context specified - this context does not contain a values collection");
        }
    }

    // Create the collection to hold the write results
    if (SUCCEEDED(hr))
    {
        hr = CoCreateInstance(CLSID_PortableDeviceValuesCollection,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValuesCollection,
                              (VOID**) &pWriteResults);
        CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValuesCollection");
    }

    // Create the collection to hold the event parameters
    if (SUCCEEDED(hr))
    {
        hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**) &pEventParams);
        CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
    }

    if (SUCCEEDED(hr))
    {
        for (DWORD dwIndex = pContext->NextObject; dwIndex < cObjects; dwIndex++)
        {
            CComPtr<IPortableDeviceValues> pValues;
            CComPtr<IPortableDeviceValues> pSetResults;

            bool bObjectChanged = false;

            hr = pContext->ValuesCollection->GetAt(dwIndex, &pValues);
            CHECK_HR(hr, "Failed to get next values from bulk properties context");

            // CoCreate a collection to store the per object results.
            if (SUCCEEDED(hr))
            {
                hr = CoCreateInstance(CLSID_PortableDeviceValues,
                                      NULL,
                                      CLSCTX_INPROC_SERVER,
                                      IID_IPortableDeviceValues,
                                      (VOID**) &pSetResults);
                CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
            }

            if (SUCCEEDED(hr))
            {
                LPWSTR pszObjectID = NULL;

                // Get which object this is for
                hr =  pValues->GetStringValue(WPD_OBJECT_ID, &pszObjectID);
                if (SUCCEEDED(hr))
                {
                    hr = m_pDevice->SetPropertyValues(pContext->Scope, pszObjectID, pValues, pSetResults, pEventParams, &bObjectChanged);
                    CHECK_HR(hr, "Failed to get count of values");
                }

                if (SUCCEEDED(hr))
                {
                    // Ensure the write results contain which ObjectID this was for
                    hr = pSetResults->SetStringValue(WPD_OBJECT_ID, pszObjectID);
                    CHECK_HR(hr, "Failed to set WPD_OBJECT_ID in write results");

                    if (SUCCEEDED(hr) && bObjectChanged)
                    {
                        // set property values is successful and object has changed, so we post an event.  
                        // This is best effort, so errors are ignored
                        HRESULT hrEvent = PostWpdEvent(pParams, pEventParams);  
                        CHECK_HR(hrEvent, "Failed post event for updated object [%ws] (errors ignored)", pszObjectID);
                    }
                    pEventParams->Clear();
                }

                CoTaskMemFree(pszObjectID);
            }

            if (SUCCEEDED(hr))
            {
                hr = pWriteResults->Add(pSetResults);
                CHECK_HR(hr, "Failed to add IPortableDeviceValues to IPortableDeviceValuesCollection");
            }

            pContext->NextObject++;
        }
    }

    if (SUCCEEDED(hr))
    {
        hr = pResults->SetIUnknownValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_WRITE_RESULTS, pWriteResults);
        CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_BULK_WRITE_RESULTS");
    }

    // Free the memory.  CoTaskMemFree ignores NULLs so no need to check.
    CoTaskMemFree(pwszContext);

    SAFE_RELEASE(pContext);

    return hr;
}
/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_DELETE_OBJECTS
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS: the ObjectIDs, indicating which objects to delete.  These may
 *      contain children.
 *  - WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_OPTIONS: Flag parameter indicating delete options. Must be one
 *                  of the following:
 *                 - PORTABLE_DEVICE_DELETE_NO_RECURSION - Deletes the
 *                          object only.  This should fail if children exist.
 *                 - PORTABLE_DEVICE_DELETE_WITH_RECURSION - Deletes this
 *                          object and all children.
 *
 *  The driver should:
 *  - If the flag is PORTABLE_DEVICE_DELETE_NO_RECURSION the driver should delete the
 *    specified object only.  If the object still has children the driver should not delete
 *    the object and instead return HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION).
 *  - If the flag is PORTABLE_DEVICE_DELETE_WITH_RECURSION the driver should delete the
 *    specified object and all of its children.
 *  - Fill out the operation results in WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS.  It contains an IPortableDevicePropVariantCollection of
 *    VT_ERROR values indicating the success or failure of the operation for that element.
 *    Order is implicit, i.e. the first element of WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS corresponds to the first element of WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS and so on.
 *  - The driver should return:
 *      - S_OK if all objects were deleted successfully.
 *      - S_FALSE if any object delete failed.
 *      - An error return indicates that the driver did not delete any objects, and
 *        WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS is ignored.
 */
HRESULT WpdObjectManagement::OnDelete(
    _In_    IPortableDeviceValues*  pParams,
    _In_    IPortableDeviceValues*  pResults)
{

    HRESULT hr              = S_OK;
    DWORD   dwOptions       = PORTABLE_DEVICE_DELETE_NO_RECURSION;
    BOOL    bDeleteFailed   = FALSE;
    VARTYPE vt              = VT_EMPTY;

    CComPtr<IPortableDevicePropVariantCollection> pObjectIDs;
    CComPtr<IPortableDevicePropVariantCollection> pDeleteResults;
    CComPtr<IPortableDeviceValues>                pEventParams;                

    if (hr == S_OK)
    {
        hr = pParams->GetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS, &pObjectIDs);
        CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS");
    }

    // Ensure that this is a collection of VT_LPWSTR
    if (hr == S_OK)
    {
        hr = pObjectIDs->GetType(&vt);
        CHECK_HR(hr, "Failed to get the VARTYP of WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS");
        if (hr == S_OK)
        {
            if (vt != VT_LPWSTR)
            {
                hr = E_INVALIDARG;
                CHECK_HR(hr, "WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS is not a collection of VT_LPWSTR");
            }
        }
    }

    if (hr == S_OK)
    {
        hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_OPTIONS, &dwOptions);
        CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_OPTIONS");
    }

    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDevicePropVariantCollection,
                              (VOID**) &pDeleteResults);
        CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDevicePropVariantCollection");
    }

    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**) &pEventParams);
        CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues");
    }

    if (hr == S_OK)
    {
        DWORD   cObjects    = 0;
        // Loop through the object list and attempt to delete
        hr = pObjectIDs->GetCount(&cObjects);
        CHECK_HR(hr, "Failed to get number of objects to delete");

        if (hr == S_OK)
        {
            ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams);

            for(DWORD dwIndex = 0; dwIndex < cObjects; dwIndex++)
            {
                HRESULT hrTemp = S_OK;
                PROPVARIANT pv = {0};

                PropVariantInit(&pv);
                // Get the next Object to delete
                hr = pObjectIDs->GetAt(dwIndex, &pv);
                CHECK_HR(hr, "Failed to get next object id at index %d", dwIndex);
                if (hr == S_OK)
                {
                    HRESULT     hrDelete = S_OK;
                    PROPVARIANT pvResult = {0};

                    PropVariantInit(&pvResult);

                    hrDelete = m_pDevice->DeleteObject(Scope, dwOptions, pv.pwszVal, pEventParams);
                    CHECK_HR(hrDelete, "Failed to delete object [%ws]", pv.pwszVal);

                    if(FAILED(hrDelete))
                    {
                        bDeleteFailed = TRUE;
                    }
                    else
                    {
                        // Delete is successful, so we post an event.  
                        // This is best effort, so errors are ignored
                        HRESULT hrEvent = PostWpdEvent(pParams, pEventParams);  
                        CHECK_HR(hrEvent, "Failed post event for deleted object [%ws] (errors ignored)", pv.pwszVal);
                    }

                    // Clear event parameters for reuse
                    pEventParams->Clear();

                    // Save this result
                    pvResult.vt       = VT_ERROR;
                    pvResult.scode    = hrDelete;
                    hrTemp = pDeleteResults->Add(&pvResult);
                    PropVariantClear(&pvResult);
                    CHECK_HR(hrTemp, "Failed to add result for [%ws] to list of results", pv.pwszVal);

                    PropVariantClear(&pv);
                }
                else
                {
                    break;
                }
            }
        }
    }

    // Set the results
    if (hr == S_OK)
    {
        hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS, pDeleteResults);
        CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS");
    }

    // If an object failed to delete, make sure we return S_FALSE
    if ((hr == S_OK) && (bDeleteFailed))
    {
        hr = S_FALSE;
    }

    return hr;
}
HRESULT WpdServiceMethods::DispatchMethod(
    _In_         LPCWSTR                 pwszContext,
    _In_         IPortableDeviceValues*  pStartParams,
    _COM_Outptr_ IPortableDeviceValues** ppResults)
{
    HRESULT hr       = S_OK;
    HRESULT hrStatus = S_OK;
    GUID    Method   = GUID_NULL;
    CComPtr<IPortableDeviceValues> pMethodParams;
    CComPtr<IPortableDeviceValues> pMethodResults;

    *ppResults = NULL;

    // Get the method GUID
    hr = pStartParams->GetGuidValue(WPD_PROPERTY_SERVICE_METHOD, &Method);
    CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_METHOD");

    // Get the method parameters.  These can be optional if the methods don't require parameters
    if (SUCCEEDED(hr))
    {
        HRESULT hrTemp = pStartParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_SERVICE_METHOD_PARAMETER_VALUES, &pMethodParams);
        CHECK_HR(hrTemp, "Failed to get WPD_PROPERTY_SERVICE_METHOD_PARAMETER_VALUES (ok if method does not require parameters)");
    }

    // Prepare the results collection
    if (SUCCEEDED(hr))
    {
        hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**)&pMethodResults);
        CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
    }

    if (SUCCEEDED(hr))
    {
        // Invoke the method
        if (IsEqualGUID(METHOD_FullEnumSyncSvc_BeginSync, Method))
        {
            hrStatus = m_pContactsService->OnBeginSync(pMethodParams, *ppResults);
            CHECK_HR(hrStatus, "BeginSync method failed");
        }
        else if (IsEqualGUID(METHOD_FullEnumSyncSvc_EndSync, Method))
        {
            hrStatus = m_pContactsService->OnEndSync(pMethodParams, *ppResults);
            CHECK_HR(hrStatus, "EndSync method failed");
        }
        else if (IsEqualGUID(MyCustomMethod, Method))
        {
            CComPtr<IPortableDeviceValues> pCustomEventParams;

            hr = CoCreateInstance(CLSID_PortableDeviceValues,
                                  NULL,
                                  CLSCTX_INPROC_SERVER,
                                  IID_IPortableDeviceValues,
                                  (VOID**)&pCustomEventParams);
            CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");

            if (SUCCEEDED(hr))
            {
                hrStatus = m_pContactsService->OnMyCustomMethod(pMethodParams, pMethodResults, pCustomEventParams);
                CHECK_HR(hrStatus, "MyCustomMethod method failed");
            }

            if (SUCCEEDED(hr))
            {
                // In addition to a method complete event, we can also send a custom event,
                // for example, to indicate progress of the method
                hr = PostWpdEvent(pStartParams, pCustomEventParams);
                CHECK_HR(hr, "Failed to post custom event");
            }
        }
        else
        {
            hrStatus = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
            CHECK_HR(hr, "Unknown method %ws received",CComBSTR(Method));
        }
    }

    // We always want to post a method completion event
    // Even if the method has failed
    {
        CComPtr<IPortableDeviceValues> pEventParams;
        hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**)&pEventParams);
        CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");

        if (SUCCEEDED(hr))
        {
            hr = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_SERVICE_METHOD_COMPLETE);
            CHECK_HR(hr, "Failed to set the event id to WPD_EVENT_SERVICE_METHOD_COMPLETE");
        }

        if (SUCCEEDED(hr))
        {
            hr = pEventParams->SetStringValue(WPD_EVENT_PARAMETER_SERVICE_METHOD_CONTEXT, pwszContext);
            CHECK_HR(hr, "Failed to set the method context for WPD_EVENT_SERVICE_METHOD_COMPLETE");
        }

        if (SUCCEEDED(hr))
        {
            hr = PostWpdEvent(pStartParams, pEventParams);
            CHECK_HR(hr, "Failed to post WPD_EVENT_SERVICE_METHOD_COMPLETE");
        }
    }

    if (SUCCEEDED(hr))
    {
        hr = hrStatus;
    }

    if (SUCCEEDED(hr))
    {
        *ppResults = pMethodResults.Detach();
    }

    return hr;
}
示例#10
0
HRESULT WpdServiceMethods::DispatchMethod(
    _In_        LPCWSTR                 pwszContext,
    _In_        IPortableDeviceValues*  pStartParams,
    _Outptr_    IPortableDeviceValues** ppResults)
{
    HRESULT hr       = S_OK;
    HRESULT hrStatus = S_OK;
    GUID    Method   = GUID_NULL;
    CComPtr<IPortableDeviceValues> pMethodParams;
    CComPtr<IPortableDeviceValues> pMethodResults;

    *ppResults = NULL;

    // Get the method GUID
    hr = pStartParams->GetGuidValue(WPD_PROPERTY_SERVICE_METHOD, &Method);
    CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_METHOD");

    // Get the method parameters.  These can be optional if the methods don't require parameters
    if (hr == S_OK)
    {
        HRESULT hrTemp = pStartParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_SERVICE_METHOD_PARAMETER_VALUES, &pMethodParams);
        CHECK_HR(hrTemp, "Failed to get WPD_PROPERTY_SERVICE_METHOD_PARAMETER_VALUES (ok if method does not require parameters)");
    }

    // Prepare the results collection
    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**)ppResults);
        CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
    }

    if (hr == S_OK)
    {
        // Invoke the method to the service
        m_pGattService->OnMethodInvoke(Method, pMethodParams, *ppResults);
    }

    // Post the completion event if the method has failed.
    {

        if (hr == S_OK)
        {
            hr = (*ppResults)->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_SERVICE_METHOD_COMPLETE);
            CHECK_HR(hr, "Failed to set the event id to WPD_EVENT_SERVICE_METHOD_COMPLETE");
        }

        if (hr == S_OK)
        {
            hr = (*ppResults)->SetStringValue(WPD_EVENT_PARAMETER_SERVICE_METHOD_CONTEXT, pwszContext);
            CHECK_HR(hr, "Failed to set the method context for WPD_EVENT_SERVICE_METHOD_COMPLETE");
        }

        if (hr == S_OK)
        {
            hr = PostWpdEvent(pStartParams, *ppResults);
            CHECK_HR(hr, "Failed to post WPD_EVENT_SERVICE_METHOD_COMPLETE");
        }
    }

    if (hr == S_OK)
    {
        hr = hrStatus;
    }

    return hr;
}
示例#11
0
/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_SET
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values we want to return.
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES: an IPortableDeviceValues of values, identifying which
 *      specific property values we are requested to write.
 *
 *  The driver should:
 *  - Write all requested property values.  For each property, a write result should be returned in the
 *      write result property store.
 *  - If any property write failed, the corresponding write result value should be
 *      set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure.
 *  - S_OK should be returned if all properties were written successfully.
 *  - S_FALSE should be returned if any property write failed.
 *  - Any error return indicates that the driver did not write any results, and the caller will
 *      not attempt to unpack any property write results.
 */
HRESULT WpdObjectProperties::OnWriteProperties(
    IPortableDeviceValues*  pParams,
    IPortableDeviceValues*  pResults)
{

    HRESULT hr          = S_OK;
    LPWSTR  pszObjectID = NULL;
    CComPtr<IPortableDeviceValues>  pValues;
    CComPtr<IPortableDeviceValues>  pWriteResults;
    CComPtr<IPortableDeviceValues>  pEventParams;

    if (hr == S_OK)
    {
        hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &pszObjectID);
        CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID");
    }

    if (hr == S_OK)
    {
        hr = pParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, &pValues);
        CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES");
    }

    if (hr == S_OK)
    {
        hr = m_pFakeDevice->WritePropertiesOnObject(pszObjectID, pValues, &pWriteResults);
        if(FAILED(hr))
        {
            CHECK_HR(hr, "Failed to write properties on [%ws]", pszObjectID);
        }
    }

    // Be sure to preserve possible S_FALSE returns
    if (SUCCEEDED(hr))
    {
        HRESULT hrTemp = S_OK;
        hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_WRITE_RESULTS, pWriteResults);
        CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_WRITE_RESULTS"));

        if(FAILED(hrTemp))
        {
            hr = hrTemp;
        }

        pEventParams = NULL;
        hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pszObjectID, &pEventParams);
        CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pszObjectID);

        if (hrTemp == S_OK)
        {
            HRESULT hrEvent = S_OK;
            // Set the event-specific parameters
            hrEvent = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_UPDATED);
            CHECK_HR(hrEvent, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID");

            // Send the Event
            if (hrEvent == S_OK)
            {
                PostWpdEvent(pParams, pEventParams);
            }
        }
    }

    // Free the memory.  CoTaskMemFree ignores NULLs so no need to check.
    CoTaskMemFree(pszObjectID);

    return hr;
}