Setting WPD properties in C#

Since setting a WPD property requires manipulating a PROPVARIANT structure through interop, we must make use of the marshalling rules we set in our last post.

We'll take a look at a function that allows string properties to be set.

 static void SetStringValue(
    PortableDeviceApiLib.PortableDeviceClass pPortableDevice,
    string objectID,
    ref PortableDeviceApiLib._tagpropertykey propKey,
    string newValue)
{
    //
    // Retrieve IPortableDeviceContent interface required to
    // obtain the IPortableDeviceProperties interface
    //
    PortableDeviceApiLib.IPortableDeviceContent pContent;
    pPortableDevice.Content(out pContent);

    //
    // Retrieve IPortableDeviceProperties interface required
    // to get all the properties
    //
    PortableDeviceApiLib.IPortableDeviceProperties pProperties;
    pContent.Properties(out pProperties);

    //
    // Create the Values collection to hold the value to be set
    //
    PortableDeviceApiLib.IPortableDeviceValues pSetValues = 
        (PortableDeviceApiLib.IPortableDeviceValues)
            new PortableDeviceTypesLib.PortableDeviceValuesClass();

    //
    // Use the C# PropVariant definition to set the string value
    //
    PropVariant pvSet = new PropVariant();
    pvSet.variantType = 31; // VT_LPWSTR
    pvSet.pointerValue = Marshal.StringToCoTaskMemUni(newValue);

    //
    // Marshal our definition into a pointer
    //
    IntPtr ptrValue = Marshal.AllocHGlobal(Marshal.SizeOf(pvSet));
    Marshal.StructureToPtr(pvSet, ptrValue, false);

    //
    // Marshal pointer into the interop PROPVARIANT 
    //
    PortableDeviceApiLib.tag_inner_PROPVARIANT ipSet =
      (PortableDeviceApiLib.tag_inner_PROPVARIANT)
        Marshal.PtrToStructure(ptrValue, typeof(PortableDeviceApiLib.tag_inner_PROPVARIANT));

    //
    // Call the SetValues API to set the specified property
    //
    pSetValues.SetValue(ref propKey, ref ipSet);

    PortableDeviceApiLib.IPortableDeviceValues pResults;
    pProperties.SetValues(objectID, pSetValues, out pResults);
}

The example assumes that a device connection has already been opened. For this function to build, the PropVariant definition needs to be present and PortableDeviceConstants.cs needs to be part of the project.

We first acquire the IPortableDeviceProperties interface since that exposes the SetValues API method. Next we prepare the PROPVARIANT of vartype VT_LPWSTR that needs to be sent in. We first create an instance of the C# definition and set the pointer value to a marshalled instance of the value to be set using Marshal.StringToCoTaskMemUni. We next marshal our definition into a pointer and then re-marshal the pointer into the tag_inner_PROPVARIANT type expected by the API. This final value is then added to the values collection and this collection is sent down to the driver via the SetValues API.

This style of preparing the PROPVARIANT is common across different APIs. e.g. The Delete API expects a PropVariantCollection. All that needs to be done is prepare the PROPVARIANT using the style above and then add the prepared PROPVARIANT into a PropVariantCollection. This collection can then be sent down with the Delete API.