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 = 
            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 =
        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.