// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
#include "stdafx.h"
// This number controls how many object identifiers are requested during each call
// to IEnumPortableDeviceObjectIDs::Next()
//
#define NUM_OBJECTS_TO_REQUEST 10
// Recursively called function which enumerates using the specified
// object identifier as the parent.
void RecursiveEnumerate(
_In_ PCWSTR objectID,
_In_ IPortableDeviceContent* content)
{
ComPtr enumObjectIDs;
// Print the object identifier being used as the parent during enumeration.
wprintf(L"%ws\n", objectID);
// Get an IEnumPortableDeviceObjectIDs interface by calling EnumObjects with the
// specified parent object identifier.
HRESULT hr = content->EnumObjects(0, // Flags are unused
objectID, // Starting from the passed in object
nullptr, // Filter is unused
&enumObjectIDs);
if (FAILED(hr))
{
wprintf(L"! 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 numFetched = 0;
PWSTR objectIDArray[NUM_OBJECTS_TO_REQUEST] = {0};
hr = enumObjectIDs->Next(NUM_OBJECTS_TO_REQUEST, // Number of objects to request on each NEXT call
objectIDArray, // Array of PWSTR array which will be populated on each NEXT call
&numFetched); // 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 index = 0; (index < numFetched) && (objectIDArray[index] != nullptr); index++)
{
RecursiveEnumerate(objectIDArray[index], content);
// Free allocated PWSTRs after the recursive enumeration call has completed.
CoTaskMemFree(objectIDArray[index]);
objectIDArray[index] = nullptr;
}
}
}
}
//
// Enumerate all content on the device starting with the
// "DEVICE" object
//
void EnumerateAllContent(
_In_ IPortableDevice* device)
{
HRESULT hr = S_OK;
ComPtr content;
// Get an IPortableDeviceContent interface from the IPortableDevice interface to
// access the content-specific methods.
hr = device->Content(&content);
if (FAILED(hr))
{
wprintf(L"! Failed to get IPortableDeviceContent from IPortableDevice, hr = 0x%lx\n", hr);
}
// Enumerate content starting from the "DEVICE" object.
if (SUCCEEDED(hr))
{
wprintf(L"\n");
RecursiveEnumerate(WPD_DEVICE_OBJECT_ID, content.Get());
}
}
//
// 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(
_In_ PCWSTR objectID,
_In_ IPortableDeviceContent* content,
_In_ IPortableDevicePropVariantCollection* objectIDs)
{
HRESULT hr = S_OK;
ComPtr enumObjectIDs;
// Add the object identifier being used as the parent during enumeration
// to the collection.
PROPVARIANT pv;
// Allocated a new string in a PROPVARIANT so we can add it to our
// collection object.
hr = InitPropVariantFromString(objectID, &pv);
if (FAILED(hr))
{
wprintf(L"! Failed to copy object identifier '%ws', hr = 0x%lx\n", objectID, hr);
return;
}
// Add the object identifier...
hr = objectIDs->Add(&pv);
// Free the allocated string in the PROPVARIANT
PropVariantClear(&pv);
// If we failed to add the object identifier, return immediately.
if (FAILED(hr))
{
wprintf(L"! Failed to add object identifier '%ws' to the IPortableDevicePropVariantCollection, hr = 0x%lx\n", objectID, hr);
return;
}
// Get an IEnumPortableDeviceObjectIDs interface by calling EnumObjects with the
// specified parent object identifier.
hr = content->EnumObjects(0, // Flags are unused
objectID, // Starting from the passed in object
nullptr, // Filter is unused
&enumObjectIDs);
if (FAILED(hr))
{
wprintf(L"! 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 numFetched = 0;
PWSTR objectIDArray[NUM_OBJECTS_TO_REQUEST] = {0};
hr = enumObjectIDs->Next(NUM_OBJECTS_TO_REQUEST, // Number of objects to request on each Next() call
objectIDArray, // PWSTR array which will be populated on each Next() call
&numFetched); // 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 index = 0; (index < numFetched) && (objectIDArray[index] != nullptr); index++)
{
RecursiveEnumerateAndCopyToCollection(objectIDArray[index], content, objectIDs);
// Free allocated PWSTRs after the recursive enumeration call has completed.
CoTaskMemFree(objectIDArray[index]);
objectIDArray[index] = nullptr;
}
}
}
}
// Enumerate all content on the device starting with the
// "DEVICE" object and populates the returned object identifiers
// into an IPortableDevicePropVariantCollection
HRESULT CreateIPortableDevicePropVariantCollectionWithAllObjectIDs(
_In_ IPortableDeviceContent* content,
_COM_Outptr_ IPortableDevicePropVariantCollection** objectIDs)
{
*objectIDs = nullptr;
ComPtr objectIDsTemp;
// CoCreate an IPortableDevicePropVariantCollection interface to hold the the object identifiers
HRESULT hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&objectIDsTemp));
if (SUCCEEDED(hr))
{
RecursiveEnumerateAndCopyToCollection(WPD_DEVICE_OBJECT_ID, content, objectIDsTemp.Get());
*objectIDs = objectIDsTemp.Detach();
}
return hr;
}
HRESULT SendHintsCommand(
_In_ IPortableDevice* device,
_In_ REFGUID contentType,
_COM_Outptr_ IPortableDeviceValues** results)
{
*results = nullptr;
// Create and initialize the command parameters
ComPtr params;
ComPtr resultsTemp;
HRESULT hr = CoCreateInstance(CLSID_PortableDeviceValues,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(¶ms));
if (SUCCEEDED(hr))
{
hr = params->SetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY,
WPD_COMMAND_DEVICE_HINTS_GET_CONTENT_LOCATION.fmtid);
if (SUCCEEDED(hr))
{
hr = params->SetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID,
WPD_COMMAND_DEVICE_HINTS_GET_CONTENT_LOCATION.pid);
if (SUCCEEDED(hr))
{
hr = params->SetGuidValue(WPD_PROPERTY_DEVICE_HINTS_CONTENT_TYPE,
contentType);
if (SUCCEEDED(hr))
{
// Send the command
hr = device->SendCommand(0, params.Get(), &resultsTemp);
if (SUCCEEDED(hr))
{
*results = resultsTemp.Detach();
}
}
}
}
}
return hr;
}
void ReadHintsResults(
_In_ IPortableDeviceProperties* properties,
_In_ IPortableDeviceValues* results)
{
ComPtr folderIDs;
// Get the collection
if (S_OK == results->GetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_DEVICE_HINTS_CONTENT_LOCATIONS,
&folderIDs))
{
// Get the count of folders
DWORD numFolderIDs = 0;
if (SUCCEEDED(folderIDs->GetCount(&numFolderIDs)))
{
// Loop through each of the folders
for (DWORD index = 0; index < numFolderIDs; ++index)
{
// Get the folder id
PROPVARIANT folderID = {0};
if (SUCCEEDED(folderIDs->GetAt(index, &folderID)))
{
if (folderID.vt == VT_LPWSTR)
{
// Get the properties for this item
ComPtr folderProperties;
if (SUCCEEDED(properties->GetValues(folderID.pwszVal, nullptr, &folderProperties)))
{
// Get the persistent unique object id
PWSTR folderPersistentUniqueID = nullptr;
if (SUCCEEDED(folderProperties->GetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID,
&folderPersistentUniqueID)))
{
wprintf(L" '%ws' (%ws)\n", folderID.pwszVal, folderPersistentUniqueID);
CoTaskMemFree(folderPersistentUniqueID);
}
}
}
else
{
wprintf(L"Driver returned unexpected PROVARIANT Type: %u\n", folderID.vt);
}
}
PropVariantClear(&folderID);
}
}
}
}
void ReadHintLocations(
_In_ IPortableDevice* device)
{
// Get the device content
ComPtr content;
HRESULT hr = device->Content(&content);
if (SUCCEEDED(hr))
{
// Get the device properties
ComPtr properties;
hr = content->Properties(&properties);
if (SUCCEEDED(hr))
{
// Loop through some typical content types supported by Portable Devices
const GUID* formatTypes[] = {&WPD_CONTENT_TYPE_IMAGE, &WPD_CONTENT_TYPE_VIDEO};
PCWSTR formatTypeStrings[] = {L"WPD_CONTENT_TYPE_IMAGE", L"WPD_CONTENT_TYPE_VIDEO"};
for (DWORD index = 0; index < ARRAYSIZE(formatTypes); ++index)
{
ComPtr results;
wprintf(L"Folders for content type '%ws':\n", formatTypeStrings[index]);
if (SUCCEEDED(SendHintsCommand(device, *formatTypes[index], &results)))
{
ReadHintsResults(properties.Get(), results.Get());
}
}
}
}
}