We'll create a little helper function that we can use to figure out if a WPD object property can be modified.

All WPD object properties have attributes on them. The MSDN documentation provides a complete list of attributes here. We'll take a look at the WPD_PROPERTY_ATTRIBUTE_CAN_WRITE attribute since the value of that attribute decides if a property can be modifed.

The IPortableDeviceProperties::GetPropertyAttributes API can be used to retrieve the attributes for a given property. We'll make our helper function flexible enough so that it can decide if a property is modifiable for any object.

// This function needs the object ID and property to check for
// If S_OK is returned, the supplied by-ref BOOL bWritable 
// parameter should be checked to determine if the property can
// be modified
HRESULT IsPropertyWritable(IPortableDevice* pDevice, 
                           LPCWSTR pwszObjectID,
                           REFPROPERTYKEY pkeyProperty,
                           BOOL& bWritable)
{
    HRESULT hr = S_OK;

    // Get the Properties interface through the Content interface 
    CComPtr<IPortableDeviceContent> spContent;
    if (hr == S_OK)
    {
        hr = pDevice->Content(&spContent);
    }

    CComPtr<IPortableDeviceProperties> spProperties;
    if (hr == S_OK)
    {
        hr = spContent->Properties(&spProperties);
    }

    // Execute the GetPropertyAttributes for the supplied parameters
    CComPtr<IPortableDeviceValues> spAttributes;
    if (hr == S_OK)
    {        
        hr = spProperties->GetPropertyAttributes(pwszObjectID, pkeyProperty, &spAttributes);
    }

    // Check for WPD_PROPERTY_ATTRIBUTE_CAN_WRITE in the returned collection
    if (hr == S_OK)
    {
        hr = spAttributes->GetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_WRITE, &bWritable);
    }

    return hr;
}

Checking attributes turns out to be pretty easy. The GetPropertyAttributes API returns us an IPortableDeviceValues collection. We check if the WPD_PROPERTY_ATTRIBUTE_CAN_WRITE attribute is present in this collection and retrieve its BOOL value. This function can also be optimized by referencing a global IPortableDeviceProperties interface instead of obtaining it each time the function is called.

This is just an example of how GetPropertyAttributes can be used. Another interesting and practical way of using it is to check what range of values a property can accept. For that you retrieve the WPD_PROPERTY_ATTRIBUTE_FORM and then if it is WPD_PROPERTY_ATTRIBUTE_FORM_ENUMERATION or WPD_PROPERTY_ATTRIBUTE_FORM_RANGE, either check WPD_PROPERTY_ATTRIBUTE_ENUMERATION_ELEMENTS or WPD_PROPERTY_ATTRIBUTE_RANGE_STEP / WPD_PROPERTY_ATTRIBUTE_RANGE_MIN / WPD_PROPERTY_ATTRIBUTE_RANGE_MAX.