Exemple #1
0
HRESULT WpdObjectEnumerator::CreateEnumContext(
    __inout         ContextMap*         pContextMap,
    __in            LPCWSTR             pszParentID,
    __deref_out_opt LPWSTR*             ppszEnumContext)
{

    HRESULT         hr              = S_OK;
    GUID            guidContext     = GUID_NULL;
    CComBSTR        bstrContext;
    EnumContext*    pContext        = NULL;

    if((pContextMap     == NULL) ||
       (pszParentID     == NULL) ||
       (ppszEnumContext == NULL))
    {
        hr = E_POINTER;
        CHECK_HR(hr, "Cannot have NULL parameter");
        return hr;
    }

    *ppszEnumContext = NULL;

    hr = CoCreateGuid(&guidContext);
    if (hr == S_OK)
    {
        bstrContext = guidContext;
        if(bstrContext.Length() == 0)
        {
            hr = E_OUTOFMEMORY;
            CHECK_HR(hr, "Failed to create BSTR from GUID");
        }
    }

    if (hr == S_OK)
    {
        pContext = new EnumContext();
        if(pContext != NULL)
        {
            CAtlStringW strKey = bstrContext;
            pContext->ParentID = pszParentID;

            hr = pContextMap->Add(strKey, pContext);
            CHECK_HR(hr, "Failed to add enumeration context to client context map");

            pContext->Release();
        }
        else
        {
            hr = E_OUTOFMEMORY;
            CHECK_HR(hr, "Failed to allocate enumeration context");
        }
    }

    if (hr == S_OK)
    {
        *ppszEnumContext = AtlAllocTaskWideString(bstrContext);
    }

    return hr;
}
HRESULT WpdObjectPropertiesBulk::CreateBulkPropertiesContext(
    __in            ACCESS_SCOPE                          Scope,
    __inout         ContextMap*                           pContextMap,
    __in            IPortableDevicePropVariantCollection* pObjectIDs,
    __in            IPortableDeviceKeyCollection*         pProperties,
    __deref_out_opt LPWSTR*                               ppszBulkPropertiesContext)
{

    HRESULT                 hr       = S_OK;
    BulkPropertiesContext*  pContext = NULL;
    CAtlStringW             strKey;

    if((pContextMap               == NULL) ||
       (pObjectIDs                == NULL) ||
       (ppszBulkPropertiesContext == NULL))
    {
        hr = E_POINTER;
        CHECK_HR(hr, "Cannot have NULL parameter");
        return hr;
    }

    *ppszBulkPropertiesContext = NULL;

    if (hr == S_OK)
    {
        pContext = new BulkPropertiesContext();
        if(pContext == NULL)
        {
            hr = E_OUTOFMEMORY;
            CHECK_HR(hr, "Failed to allocate new bulk properties context");
        }
    }

    if (hr == S_OK)
    {
        pContext->ObjectIDs  = pObjectIDs;
        pContext->Properties = pProperties;
        pContext->Scope      = Scope;

        hr = pContextMap->Add(pContext, strKey);
        CHECK_HR(hr, "Failed to insert bulk property operation context into our context Map");
    }

    if (hr == S_OK)
    {
        *ppszBulkPropertiesContext = AtlAllocTaskWideString(strKey);
        if (*ppszBulkPropertiesContext == NULL)
        {
            hr = E_OUTOFMEMORY;
            CHECK_HR(hr, "Failed to allocate bulk properties context");
        }
    }

    SAFE_RELEASE(pContext);

    return hr;
}
HRESULT WpdObjectPropertiesBulk::CreateBulkPropertiesContext(
    _In_        ACCESS_SCOPE                     Scope,
    _In_        ContextMap*                      pContextMap,
    _In_        IPortableDeviceValuesCollection* pValuesCollection,
    _Outptr_result_nullonfailure_   LPWSTR*      ppszBulkPropertiesContext)
{
    HRESULT                 hr            = S_OK;
    BulkPropertiesContext*  pContext      = NULL;
    CAtlStringW             strKey;

    if((pContextMap               == NULL) ||
       (pValuesCollection         == NULL) ||
       (ppszBulkPropertiesContext == NULL))
    {
        hr = E_POINTER;
        CHECK_HR(hr, "Cannot have NULL parameter");
        return hr;
    }

    *ppszBulkPropertiesContext = NULL;

    pContext = new BulkPropertiesContext();
    if(pContext == NULL)
    {
        hr = E_OUTOFMEMORY;
        CHECK_HR(hr, "Failed to allocate new bulk properties context");
    }

    if (SUCCEEDED(hr))
    {
        pContext->ValuesCollection  = pValuesCollection;
        pContext->Scope             = Scope;

        hr = pContextMap->Add(pContext, strKey);
        CHECK_HR(hr, "Failed to insert bulk property operation context into our context Map");
    }

    if (SUCCEEDED(hr))
    {
        *ppszBulkPropertiesContext = AtlAllocTaskWideString(strKey);
        if (*ppszBulkPropertiesContext == NULL)
        {
            hr = E_OUTOFMEMORY;
            CHECK_HR(hr, "Failed to allocate bulk properties context");
        }
    }

    SAFE_RELEASE(pContext);

    return hr;
}
Exemple #4
0
HRESULT FakeDevice::CreatePropertiesOnlyObject(
                ACCESS_SCOPE           Scope,
    _In_        IPortableDeviceValues* pObjectProperties,
    _In_        IPortableDeviceValues* pEventParams,
    _Outptr_result_nullonfailure_      LPWSTR*  ppszNewObjectID)
{
    HRESULT      hr;
    LPWSTR       pszParentID = NULL;
    FakeContent* pParent     = NULL;
    FakeContent* pNewObject  = NULL;

    if ((pObjectProperties == NULL) ||
        (pEventParams      == NULL) ||
        (ppszNewObjectID   == NULL))
    {
        hr = E_POINTER;
        CHECK_HR(hr, "Cannot have NULL parameter");
        return hr;
    }

    *ppszNewObjectID = NULL;

    // Get WPD_OBJECT_PARENT_ID
    hr = pObjectProperties->GetStringValue(WPD_OBJECT_PARENT_ID, &pszParentID);
    CHECK_HR(hr, "Failed to get WPD_OBJECT_PARENT_ID");

    // Check if it is within our current access scope
    if (SUCCEEDED(hr))
    {
        hr = GetContent(Scope, pszParentID, &pParent);
        CHECK_HR(hr, "Failed to get content '%ws'", pszParentID);
    }

    if (SUCCEEDED(hr))
    {
        hr = pParent->CreatePropertiesOnlyObject(pObjectProperties, &m_dwLastObjectID, &pNewObject);
        CHECK_HR(hr, "Failed to create properties only object with parent '%ws'", pszParentID);
    }

    if (SUCCEEDED(hr))
    {
        *ppszNewObjectID = AtlAllocTaskWideString(pNewObject->ObjectID);
        if (*ppszNewObjectID == NULL)
        {
            hr = E_OUTOFMEMORY;
            CHECK_HR(hr, "Failed to allocate memory for created object ID");
        }

        HRESULT hrEvent = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_ADDED);
        CHECK_HR(hrEvent, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID");

        if (hrEvent == S_OK)
        {
            hrEvent = pEventParams->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, pNewObject->PersistentUniqueID);
            CHECK_HR(hrEvent, "Failed to add WPD_OBJECT_PERSISTENT_UNIQUE_ID");
        }

        if (hrEvent == S_OK)
        {
            hrEvent = pEventParams->SetStringValue(WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID, pNewObject->ParentPersistentUniqueID);
            CHECK_HR(hrEvent, "Failed to add WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID");
        }

        if (hrEvent == S_OK)
        {
            // Adding this event parameter will allow WPD to scope this event to the container functional object
            hrEvent = pEventParams->SetStringValue(WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, pNewObject->ContainerFunctionalObjectID);
            CHECK_HR(hrEvent, "Failed to add WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID");
        }
    }

    CoTaskMemFree(pszParentID);
    return hr;
}
HRESULT WpdObjectManagement::CreateObjectManagementContext(
    __inout         ContextMap*             pContextMap,
    __in            LPCWSTR                 pszObjectName,
    __in            IPortableDeviceValues*  pObjectProperties,
    __in            BOOL                    bUpdateRequest,
    __deref_out_opt LPWSTR*                 ppszObjectManagementContext)
{

    HRESULT                     hr              = S_OK;
    GUID                        guidContext     = GUID_NULL;
    ObjectManagementContext*    pContext        = NULL;
    CComBSTR    bstrContext;

    if((pContextMap                 == NULL) ||
       (pObjectProperties           == NULL) ||
       (pszObjectName               == NULL) ||
       (ppszObjectManagementContext == NULL))
    {
        hr = E_POINTER;
        CHECK_HR(hr, "Cannot have NULL parameter");
        return hr;
    }

    *ppszObjectManagementContext = NULL;

    hr = CoCreateGuid(&guidContext);
    if (hr == S_OK)
    {
        bstrContext = guidContext;
        if(bstrContext.Length() == 0)
        {
            hr = E_OUTOFMEMORY;
            CHECK_HR(hr, "Failed to create BSTR from GUID");
        }
    }

    if (hr == S_OK)
    {
        pContext = new ObjectManagementContext();
        if(pContext != NULL)
        {
            CAtlStringW strKey = bstrContext;
            pContext->Name              = pszObjectName;
            pContext->ObjectProperties  = pObjectProperties;
            pContext->UpdateRequest     = bUpdateRequest;

            hr = pContextMap->Add(strKey, pContext);
            CHECK_HR(hr, "Failed to add object creation context to client context map");

            pContext->Release();
        }
        else
        {
            hr = E_OUTOFMEMORY;
            CHECK_HR(hr, "Failed to allocate object creation context");
        }
    }

    if (hr == S_OK)
    {
        *ppszObjectManagementContext = AtlAllocTaskWideString(bstrContext);
    }

    return hr;
}
/**
 *  This method is called when we receive a WPD_COMMAND_COMMON_GET_OBJECT_IDS_FROM_PERSISTENT_UNIQUE_IDS
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_COMMON_PERSISTENT_UNIQUE_IDS: Contains an IPortableDevicePropVariantCollection of VT_LPWSTR,
 *    indicating the PersistentUniqueIDs.
 *
 *  The driver should:
 *  - Iterate through the PersistentUniqueIDs, and convert to a currently valid object id.
 *    This object ID list should be returned as an IPortableDevicePropVariantCollection of VT_LPWSTR
 *    in WPD_PROPERTY_COMMON_OBJECT_IDS.
 *    Order is implicit, i.e. the first element in the Persistent Unique ID list corresponds to the
 *    to the first element of the ObjectID list and so on.
 *
 *    For those elements where an existing ObjectID could not be found (e.g. the
 *    object is no longer present on the device), the element will contain the
 *    empty string (L"").
 */
HRESULT WpdBaseDriver::OnGetObjectIDsFromPersistentUniqueIDs(
    _In_ IPortableDeviceValues* pParams,
    _In_ IPortableDeviceValues* pResults)
{
    HRESULT hr      = S_OK;
    DWORD   dwCount = 0;
    CComPtr<IPortableDevicePropVariantCollection> pPersistentIDs;
    CComPtr<IPortableDevicePropVariantCollection> pObjectIDs;

    if((pParams    == NULL) ||
       (pResults   == NULL))
    {
        hr = E_POINTER;
        CHECK_HR(hr, "Cannot have NULL parameter");
        return hr;
    }

    // Get the list of Persistent IDs
    if (hr == S_OK)
    {
        hr = pParams->GetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_COMMON_PERSISTENT_UNIQUE_IDS, &pPersistentIDs);
        CHECK_HR(hr, "Failed to get WPD_PROPERTY_COMMON_PERSISTENT_UNIQUE_IDS");
    }

    // Create the collection to hold the ObjectIDs
    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDevicePropVariantCollection,
                              (VOID**) &pObjectIDs);
        CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection");
    }

    // Iterate through the persistent ID list and add the equivalent object ID for each element.
    if (hr == S_OK)
    {
        hr = pPersistentIDs->GetCount(&dwCount);
        CHECK_HR(hr, "Failed to get count from persistent ID collection");

        if (hr == S_OK)
        {
            DWORD       dwIndex        = 0;
            PROPVARIANT pvPersistentID = {0};
            PROPVARIANT pvObjectID     = {0};

            PropVariantInit(&pvPersistentID);
            PropVariantInit(&pvObjectID);

            for(dwIndex = 0; dwIndex < dwCount; dwIndex++)
            {
                pvObjectID.vt = VT_LPWSTR;
                hr = pPersistentIDs->GetAt(dwIndex, &pvPersistentID);
                CHECK_HR(hr, "Failed to get persistent ID at index %d", dwIndex);

                // Since our persistent unique identifier are identical to our object
                // identifiers, we just return it back to the caller.
                if (hr == S_OK)
                {
                    pvObjectID.pwszVal = AtlAllocTaskWideString(pvPersistentID.pwszVal);
                }

                if (hr == S_OK)
                {
                    hr = pObjectIDs->Add(&pvObjectID);
                    CHECK_HR(hr, "Failed to add next Object ID");
                }

                PropVariantClear(&pvPersistentID);
                PropVariantClear(&pvObjectID);

                if(FAILED(hr))
                {
                    break;
                }
            }
        }
    }

    if (hr == S_OK)
    {
        hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_COMMON_OBJECT_IDS, pObjectIDs);
        CHECK_HR(hr, "Failed to set WPD_PROPERTY_COMMON_OBJECT_IDS");
    }

    return hr;
}
HRESULT WpdServiceMethods::StartMethod(
    _In_                           IPortableDeviceValues*  pParams,
    _Outptr_result_nullonfailure_  LPWSTR*                 ppwszMethodContext)
{
    HRESULT                 hr          = S_OK;
    ContextMap*             pContextMap = NULL;
    ServiceMethodContext*   pContext    = NULL;
    GUID                    Method      = GUID_NULL;

    CAtlStringW  strKey;
    CComPtr<IPortableDeviceValues> pMethodParams;

    if((pParams         == NULL)  ||
       (ppwszMethodContext    == NULL))
    {
        hr = E_POINTER;
        CHECK_HR(hr, "Cannot have NULL parameter");
        return hr;
    }

    *ppwszMethodContext = NULL;

    // Check if the method is supported
    hr = pParams->GetGuidValue(WPD_PROPERTY_SERVICE_METHOD, &Method);
    CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_METHOD");

    if (SUCCEEDED(hr) && !m_pContactsService->IsMethodSupported(Method))
    {
        hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
        CHECK_HR(hr, "Unknown method %ws received",CComBSTR(Method));
    }

    if (SUCCEEDED(hr))
    {
        // Get the context map which the driver stored in pParams for convenience
        hr = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, (IUnknown**)&pContextMap);
        CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP");
    }

    if (SUCCEEDED(hr))
    {
        pContext = new ServiceMethodContext();
        if(pContext == NULL)
        {
            hr = E_OUTOFMEMORY;
            CHECK_HR(hr, "Failed to allocate new method context");
        }
    }

    if (SUCCEEDED(hr))
    {
        hr = pContextMap->Add(pContext, strKey);
        CHECK_HR(hr, "Failed to insert method context into our context Map");
    }

    if (SUCCEEDED(hr))
    {
        hr = pContext->Initialize(this, pParams, strKey);
        CHECK_HR(hr, "Failed to initialize the method context");
    }

    if (SUCCEEDED(hr))
    {
        *ppwszMethodContext = AtlAllocTaskWideString(strKey);
        if (*ppwszMethodContext == NULL)
        {
            hr = E_OUTOFMEMORY;
            CHECK_HR(hr, "Failed to allocate method context string");
        }
    }

    SAFE_RELEASE(pContextMap);
    SAFE_RELEASE(pContext);

    return hr;
}
HRESULT WpdObjectPropertiesBulk::CreateBulkPropertiesContext(
    __in            ACCESS_SCOPE                   Scope,
    __inout         ContextMap*                    pContextMap,
    __in            REFGUID                        guidObjectFormat,
    __in            LPCWSTR                        pszParentObjectID,
    __in            DWORD                          dwDepth,
    __in            IPortableDeviceKeyCollection*  pProperties,
    __deref_out_opt LPWSTR*                        ppszBulkPropertiesContext)
{

    HRESULT                 hr            = S_OK;
    BulkPropertiesContext*  pContext      = NULL;
    CAtlStringW             strKey;
    CComPtr<IPortableDevicePropVariantCollection> pObjectIDs;

    if((pContextMap               == NULL) ||
       (pszParentObjectID         == NULL) ||
       (ppszBulkPropertiesContext == NULL))
    {
        hr = E_POINTER;
        CHECK_HR(hr, "Cannot have NULL parameter");
        return hr;
    }

    *ppszBulkPropertiesContext = NULL;

    hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
                          NULL,
                          CLSCTX_INPROC_SERVER,
                          IID_IPortableDevicePropVariantCollection,
                          (VOID**) &pObjectIDs);
    CHECK_HR(hr, "Failed to CoCreate CLSID_IPortableDevicePropVariantCollection");

    if (hr == S_OK)
    {
        hr = m_pDevice->GetObjectIDsByFormat(Scope, guidObjectFormat, pszParentObjectID, dwDepth, pObjectIDs);
        CHECK_HR(hr, "Faield to get list of object ids by format");
    }

    if (hr == S_OK)
    {
        pContext = new BulkPropertiesContext();
        if(pContext == NULL)
        {
            hr = E_OUTOFMEMORY;
            CHECK_HR(hr, "Failed to allocate new bulk properties context");
        }
    }

    if (hr == S_OK)
    {
        pContext->Properties = pProperties;
        pContext->ObjectIDs  = pObjectIDs;
        pContext->Scope      = Scope;

        hr = pContextMap->Add(pContext, strKey);
        CHECK_HR(hr, "Failed to insert bulk property operation context into our context Map");
    }

    if (hr == S_OK)
    {
        *ppszBulkPropertiesContext = AtlAllocTaskWideString(strKey);
        if (*ppszBulkPropertiesContext == NULL)
        {
            hr = E_OUTOFMEMORY;
            CHECK_HR(hr, "Failed to allocate bulk properties context");
        }
    }

    SAFE_RELEASE(pContext);

    return hr;
}
// Retreives the object identifier for the persistent unique identifer
void GetObjectIdentifierFromPersistentUniqueIdentifier(
    IPortableDevice* pDevice)
{
    if (pDevice == NULL)
    {
        printf("! A NULL IPortableDevice interface pointer was received\n");
        return;
    }

    HRESULT                                         hr                  = S_OK;
    WCHAR                                           szSelection[81]     = {0};
    CComPtr<IPortableDeviceContent>                 pContent;
    CComPtr<IPortableDevicePropVariantCollection>   pPersistentUniqueIDs;
    CComPtr<IPortableDevicePropVariantCollection>   pObjectIDs;
	//<SnippetContentProp7>
    // Prompt user to enter an unique identifier to convert to an object idenifier.
    printf("Enter the Persistant Unique Identifier of the object you wish to convert into an object identifier.\n>");
    hr = StringCbGetsW(szSelection,sizeof(szSelection));
    if (FAILED(hr))
    {
        printf("An invalid persistent object identifier was specified, aborting the query operation\n");
    }
	//</SnippetContentProp7>
    // 1) Get an IPortableDeviceContent interface from the IPortableDevice interface to
    // access the content-specific methods.
	//<SnippetContentProp8>
    if (SUCCEEDED(hr))
    {
        hr = pDevice->Content(&pContent);
        if (FAILED(hr))
        {
            printf("! Failed to get IPortableDeviceContent from IPortableDevice, hr = 0x%lx\n",hr);
        }
    }
	//</SnippetContentProp8>

    // 2) CoCreate an IPortableDevicePropVariantCollection interface to hold the the Unique Identifiers
    // to query for Object Identifiers.
    //
    // NOTE: This is a collection interface so more than 1 identifier can be requested at a time.
    //       This sample only requests a single unique identifier.
	//<SnippetContentProp9>
    hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
                          NULL,
                          CLSCTX_INPROC_SERVER,
                          IID_PPV_ARGS(&pPersistentUniqueIDs));
	//</SnippetContentProp9>
	//<SnippetContentProp10>
    if (SUCCEEDED(hr))
    {
        if (pPersistentUniqueIDs != NULL)
        {
            PROPVARIANT pv = {0};
            PropVariantInit(&pv);

            // Initialize a PROPVARIANT structure with the object identifier string
            // that the user selected above. Notice we are allocating memory for the
            // PWSTR value.  This memory will be freed when PropVariantClear() is
            // called below.
            pv.vt      = VT_LPWSTR;
            pv.pwszVal = AtlAllocTaskWideString(szSelection);
            if (pv.pwszVal != NULL)
            {
                // Add the object identifier to the objects-to-delete list
                // (We are only deleting 1 in this example)
                hr = pPersistentUniqueIDs->Add(&pv);
                if (SUCCEEDED(hr))
                {
                    // 3) Attempt to get the unique idenifier for the object from the device
                    hr = pContent->GetObjectIDsFromPersistentUniqueIDs(pPersistentUniqueIDs,
                                                                       &pObjectIDs);
                    if (SUCCEEDED(hr))
                    {
                        PROPVARIANT pvId = {0};
                        hr = pObjectIDs->GetAt(0, &pvId);
                        if (SUCCEEDED(hr))
                        {
                            printf("The persistent unique identifier '%ws' relates to object identifier '%ws' on the device.\n", szSelection, pvId.pwszVal);
                        }
                        else
                        {
                            printf("! Failed to get the object identifier for '%ws' from the IPortableDevicePropVariantCollection, hr = 0x%lx\n",szSelection, hr);
                        }

                        // Free the returned allocated string from the GetAt() call
                        PropVariantClear(&pvId);
                    }
                    else
                    {
                        printf("! Failed to get the object identifier from persistent object idenifier '%ws', hr = 0x%lx\n",szSelection, hr);
                    }
                }
                else
                {
                    printf("! Failed to get the object identifier from persistent object idenifier because we could no add the persistent object identifier string to the IPortableDevicePropVariantCollection, hr = 0x%lx\n",hr);
                }
            }
            else
            {
                hr = E_OUTOFMEMORY;
                printf("! Failed to get the object identifier because we could no allocate memory for the persistent object identifier string, hr = 0x%lx\n",hr);
            }

            // Free any allocated values in the PROPVARIANT before exiting
            PropVariantClear(&pv);
        }
    }
	//</SnippetContentProp10>
}
Exemple #10
0
LPWSTR AFXAPI AfxTaskStringA2W(LPCSTR lpa)
{
	LPWSTR lpw = AtlAllocTaskWideString(lpa);
	CoTaskMemFree((void*)lpa);
	return lpw;
}
//</SnippetContentEnum1>
// Recursively called function which enumerates using the specified
// object identifier as the parent and populates the returned object
// identifiers into an IPortableDevicePropVariantCollection object.
void RecursiveEnumerateAndCopyToCollection(
    PCWSTR                                pszObjectID,
    IPortableDeviceContent*               pContent,
    IPortableDevicePropVariantCollection* pObjectIDs)
{
    HRESULT                               hr = S_OK;
    CComPtr<IEnumPortableDeviceObjectIDs> pEnumObjectIDs;

    // Add the object identifier being used as the parent during enumeration
    // to the collection.
    PROPVARIANT pv = {0};
    PropVariantInit(&pv);

    // Allocated a new string in a PROPVARIANT so we can add it to our
    // collection object.
    pv.vt      = VT_LPWSTR;
    pv.pwszVal = AtlAllocTaskWideString(pszObjectID);

    // Add the object identifer...
    hr = pObjectIDs->Add(&pv);

    // Free the allocated string in the PROPVARIANT
    PropVariantClear(&pv);

    // If we failed to add the object identifier, return immediately.
    if (FAILED(hr))
    {
        printf("! Failed to add object identifier '%ws' to the IPortableDevicePropVariantCollection, hr = 0x%lx\n",pszObjectID, hr);
        return;
    }

    // Get an IEnumPortableDeviceObjectIDs interface by calling EnumObjects with the
    // specified parent object identifier.
    hr = pContent->EnumObjects(0,               // Flags are unused
                               pszObjectID,     // Starting from the passed in object
                               NULL,            // Filter is unused
                               &pEnumObjectIDs);
    if (FAILED(hr))
    {
        printf("! Failed to get IEnumPortableDeviceObjectIDs from IPortableDeviceContent, hr = 0x%lx\n",hr);
    }

    // Loop calling Next() while S_OK is being returned.
    while(hr == S_OK)
    {
        DWORD  cFetched = 0;
        PWSTR  szObjectIDArray[NUM_OBJECTS_TO_REQUEST] = {0};
        hr = pEnumObjectIDs->Next(NUM_OBJECTS_TO_REQUEST,   // Number of objects to request on each NEXT call
                                  szObjectIDArray,          // Array of PWSTR array which will be populated on each NEXT call
                                  &cFetched);               // Number of objects written to the PWSTR array
        if (SUCCEEDED(hr))
        {
            // Traverse the results of the Next() operation and recursively enumerate
            // Remember to free all returned object identifiers using CoTaskMemFree()
            for (DWORD dwIndex = 0; dwIndex < cFetched; dwIndex++)
            {
                RecursiveEnumerateAndCopyToCollection(szObjectIDArray[dwIndex], pContent, pObjectIDs);

                // Free allocated PWSTRs after the recursive enumeration call has completed.
                CoTaskMemFree(szObjectIDArray[dwIndex]);
                szObjectIDArray[dwIndex] = NULL;
            }
        }
    }
}