<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/atom.xsl" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><title type="html">dimeby8</title><subtitle type="html" /><id>http://blogs.msdn.com/b/dimeby8/atom.aspx</id><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/" /><link rel="self" type="application/atom+xml" href="http://blogs.msdn.com/b/dimeby8/atom.aspx" /><generator uri="http://telligent.com" version="5.6.583.19199">Telligent Community 5.6.583.19199 (Build: 5.6.583.19199)</generator><updated>2006-10-12T15:37:01Z</updated><entry><title>Sample XML document for ProvisioningAgent.provisionFromXmlDocumentAsync()</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2011/11/01/sample-xml-document-for-provisioningagent-provisionfromxmldocumentasync.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2011/11/01/sample-xml-document-for-provisioningagent-provisionfromxmldocumentasync.aspx</id><published>2011-11-01T16:50:00Z</published><updated>2011-11-01T16:50:00Z</updated><content type="html">&lt;p&gt;Below is an example of a provisioning XML document that may be passed to the &lt;a href="http://msdn.microsoft.com/en-us/library/windows/apps/windows.networking.networkoperators.provisioningagent.provisionfromxmldocumentasync(v=VS.85).aspx" target="_blank"&gt;ProvisioningAgent.provisionFromXmlDocumentAsync&lt;/a&gt; method. The following fields need to be customized at the least - Issuer and Subscriber. The value for the Subscriber node must be set to the subscriber ID for a mobile-broadband interface (can be retrieved by running 'netsh mbn show ready interface=*' at a command prompt).&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre lang="xml"&gt;&amp;lt;?xml version="1.0"?&amp;gt;
&amp;lt;CarrierProvisioning xmlns="http://www.microsoft.com/networking/CarrierControl/v1"&amp;gt;
    &amp;lt;Issuer&amp;gt;{11111111-1111-1111-1111-111111111111}&amp;lt;/Issuer&amp;gt;
    &amp;lt;Subscriber&amp;gt;1234567890&amp;lt;/Subscriber&amp;gt;
    &amp;lt;Provisioning&amp;gt;
        &amp;lt;RefreshParameters&amp;gt;
            &amp;lt;DelayInDays&amp;gt;30&amp;lt;/DelayInDays&amp;gt;
            &amp;lt;RefreshURL&amp;gt;https://www.contoso.com/provisionme.aspx?subscriber=1234567890&amp;lt;/RefreshURL&amp;gt;
            &amp;lt;Username&amp;gt;012345678901234&amp;lt;/Username&amp;gt;
            &amp;lt;Password&amp;gt;SomePassword&amp;lt;/Password&amp;gt;
        &amp;lt;/RefreshParameters&amp;gt;
    &amp;lt;/Provisioning&amp;gt; 
    &amp;lt;MBNProfiles&amp;gt;
        &amp;lt;DefaultProfile xmlns="http://www.microsoft.com/networking/CarrierControl/WWAN/v1"&amp;gt;
            &amp;lt;Name&amp;gt;Contoso&amp;lt;/Name&amp;gt;
            &amp;lt;Description&amp;gt;Contoso&amp;lt;/Description&amp;gt;
            &amp;lt;Cost xmlns="http://www.microsoft.com/networking/CarrierControl/DUSM/v1" PlanType="Fixed" OverDataLimit="true" Congested="true"&amp;gt;
                &amp;lt;UsageInMegabytes Timestamp="2002-05-30T09:30:10+06:00"&amp;gt;25&amp;lt;/UsageInMegabytes&amp;gt;
                &amp;lt;DataLimitInMegabytes&amp;gt;10&amp;lt;/DataLimitInMegabytes&amp;gt;
                &amp;lt;BillingCycle StartDate="2011-09-30T09:30:10+06:00" Duration="P1Y4M20DT2H34M78S" Resets="false"&amp;gt;&amp;lt;/BillingCycle&amp;gt;
                &amp;lt;BandwidthInKbps&amp;gt;5&amp;lt;/BandwidthInKbps&amp;gt;
                &amp;lt;MaxDownloadFileSizeInMegabytes&amp;gt;25&amp;lt;/MaxDownloadFileSizeInMegabytes&amp;gt;
                &amp;lt;CarrierPolicy&amp;gt;
                    &amp;lt;SecurityUpdatesExempt&amp;gt;true&amp;lt;/SecurityUpdatesExempt&amp;gt;
                &amp;lt;/CarrierPolicy&amp;gt;
            &amp;lt;/Cost&amp;gt;
            &amp;lt;HomeProviderName&amp;gt;Contoso&amp;lt;/HomeProviderName&amp;gt;
            &amp;lt;AutoConnectOnInternet&amp;gt;true&amp;lt;/AutoConnectOnInternet&amp;gt;
            &amp;lt;Context&amp;gt;
                &amp;lt;AccessString&amp;gt;Contoso.Contoso&amp;lt;/AccessString&amp;gt;
                &amp;lt;UserLogonCred&amp;gt;
                    &amp;lt;UserName&amp;gt;user&amp;lt;/UserName&amp;gt;
                    &amp;lt;Password&amp;gt;password&amp;lt;/Password&amp;gt;
                &amp;lt;/UserLogonCred&amp;gt;
            &amp;lt;/Context&amp;gt;
        &amp;lt;/DefaultProfile&amp;gt;
    &amp;lt;/MBNProfiles&amp;gt;
    &amp;lt;WLANProfiles&amp;gt;
        &amp;lt;WLANProfile xmlns="http://www.microsoft.com/networking/CarrierControl/WLAN/v1"&amp;gt;
            &amp;lt;name&amp;gt;Contoso Wi-Fi&amp;lt;/name&amp;gt;
            &amp;lt;SSIDConfig&amp;gt;
                &amp;lt;SSID&amp;gt;
                    &amp;lt;name&amp;gt;contosowifi&amp;lt;/name&amp;gt;
                &amp;lt;/SSID&amp;gt;
            &amp;lt;/SSIDConfig&amp;gt;
            &amp;lt;MSM&amp;gt;
                &amp;lt;security&amp;gt;
                    &amp;lt;authEncryption&amp;gt;
                        &amp;lt;authentication&amp;gt;open&amp;lt;/authentication&amp;gt;
                        &amp;lt;encryption&amp;gt;none&amp;lt;/encryption&amp;gt;
                        &amp;lt;useOneX&amp;gt;false&amp;lt;/useOneX&amp;gt;
                    &amp;lt;/authEncryption&amp;gt;
                    &amp;lt;HotspotProfile xmlns="http://www.microsoft.com/networking/WLAN/HotspotProfile/v1"&amp;gt;
                        &amp;lt;TrustedDomains&amp;gt;
                            &amp;lt;TrustedDomain&amp;gt;.contoso.net&amp;lt;/TrustedDomain&amp;gt;
                        &amp;lt;/TrustedDomains&amp;gt;
                        &amp;lt;UserName&amp;gt;user@contoso.net&amp;lt;/UserName&amp;gt;
                        &amp;lt;Password&amp;gt;somepassword&amp;lt;/Password&amp;gt;
                    &amp;lt;/HotspotProfile&amp;gt;
                &amp;lt;/security&amp;gt;
            &amp;lt;/MSM&amp;gt;
        &amp;lt;/WLANProfile&amp;gt;
    &amp;lt;/WLANProfiles&amp;gt;
&amp;lt;/CarrierProvisioning&amp;gt;
&lt;/pre&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10232127" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Change ‘Unidentified network’ from Public to Work in Windows 7</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2009/06/10/change-unidentified-network-from-public-to-work-in-windows-7.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2009/06/10/change-unidentified-network-from-public-to-work-in-windows-7.aspx</id><published>2009-06-11T01:13:00Z</published><updated>2009-06-11T01:13:00Z</updated><content type="html">&lt;p&gt;There isn&amp;rsquo;t an easy way to set the category of an Unidentified network in Windows 7 RC+ builds. By default, an Unidentified network will be set to Public for security. Often, the Unidentified network is setup intentionally (e.g. two machines connected via a hub; a network TV tuner, etc.). In these cases, Home/Work is a better category to allow common network tasks to succeed.&lt;/p&gt;
&lt;p&gt;Below is a Powershell script to change the category from Public to Work (identical to Home, except Homegroup won&amp;rsquo;t be started up). You need to be already connected to the &amp;lsquo;Unidentified network&amp;rsquo; prior to running this script. &lt;/p&gt;
&lt;p&gt;&lt;textarea style="font-size: 8pt; height: 329px; width: 100%;"&gt;# 
# Name: ChangeCategory.ps1 
# Copyright: Microsoft 2009 
# Revision: 1.0 
# 
# This script can be used to change the network category of 
# an 'Unidentified' network to Private to allow common network 
# activity. This script should only be run when connected to 
# a network that is trusted since it will also affect the 
# firewall profile used. 
# This script is provided as-is and Microsoft does not assume any 
# liability. This script may be redistributed as long as the file 
# contains these terms of use unmodified. 
# 
# Usage: 
# Start an elevated Powershell command window and execute 
# ChangeCategory.ps1 
#  
$NLMType = [Type]::GetTypeFromCLSID('DCB00C01-570F-4A9B-8D69-199FDBA5723B')
$INetworkListManager = [Activator]::CreateInstance($NLMType)

$NLM_ENUM_NETWORK_CONNECTED  = 1
$NLM_NETWORK_CATEGORY_PUBLIC = 0x00
$NLM_NETWORK_CATEGORY_PRIVATE = 0x01
$UNIDENTIFIED = "Unidentified network"

$INetworks = $INetworkListManager.GetNetworks($NLM_ENUM_NETWORK_CONNECTED)

foreach ($INetwork in $INetworks)
{
    $Name = $INetwork.GetName()
    $Category = $INetwork.GetCategory()

    if ($INetwork.IsConnected -and ($Category -eq $NLM_NETWORK_CATEGORY_PUBLIC) -and ($Name -eq $UNIDENTIFIED))
    {
        $INetwork.SetCategory($NLM_NETWORK_CATEGORY_PRIVATE)
    }
}
&lt;/textarea&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Save the script locally as ChangeCategory.ps1 (say under c:\) &lt;/li&gt;
&lt;li&gt;Launch an elevated Powershell command window &lt;/li&gt;
&lt;li&gt;Change your execution policy to allow the script to run (if not already changed) &lt;br /&gt;&lt;b&gt;PS&amp;gt; set-executionpolicy remotesigned&lt;/b&gt; &lt;/li&gt;
&lt;li&gt;Run the script &lt;br /&gt;&lt;b&gt;PS&amp;gt; c:\ChangeCategory.ps1&lt;/b&gt; &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At the end of this, the category for the Unidentified network will show as Work in Network and Sharing Center. This setting is not persisted across re-connects and you will require to run the script again to change the category. Be sure to run the script only for networks that you trust since this affects the firewall profile that will be used.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9725147" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Where is wpdmtpextensions.h?</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2007/05/15/where-is-wpdmtpextensions-h.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2007/05/15/where-is-wpdmtpextensions-h.aspx</id><published>2007-05-16T02:29:09Z</published><updated>2007-05-16T02:29:09Z</updated><content type="html">&lt;p&gt;The header file - Wpdmtpextensions.h - has been referenced in several of my posts and is needed to talk MTP to the device through WPD. The header file is not available in the Vista SDK/WDK or the WMP 11 SDK, but is planned for inclusion in the Windows Server code name "Longhorn" SDK.&lt;/p&gt; &lt;p&gt;I've just received a heads-up from the &lt;a href="http://blogs.msdn.com/wpdblog/" target="_blank"&gt;WPD team&lt;/a&gt; that the Server SDK has been published as a Beta (&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=58726ACA-8D84-4683-8959-BE0038DA7084&amp;amp;displaylang=en" target="_blank"&gt;link&lt;/a&gt;). Once you install the Server SDK, you can copy over wpdmtpextensions.h file to your desired SDK (e.g. Vista SDK/Visual Studio SDK) include folder if needed.&amp;nbsp;&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=2659434" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /><category term="MTP" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/MTP/" /></entry><entry><title>Creating a WPD playlist object in C#</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2007/01/08/creating-a-wpd-playlist-object-in-c.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2007/01/08/creating-a-wpd-playlist-object-in-c.aspx</id><published>2007-01-09T02:44:04Z</published><updated>2007-01-09T02:44:04Z</updated><content type="html">&lt;p&gt;This is a C# follow-up post on the &lt;a href="http://blogs.msdn.com/dimeby8/archive/2006/11/08/transferring-playlists-through-wpd.aspx"&gt;earlier C++ playlist creation post&lt;/a&gt;. Be sure to read the earlier post for background information on playlists and how it stores references. We will also re-use the &lt;em&gt;StringToPropVariant&lt;/em&gt; helper function that we defined in our &lt;a href="http://blogs.msdn.com/dimeby8/archive/2007/01/08/creating-wpd-propvariants-in-c-without-using-interop.aspx"&gt;previous post&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;To create a playlist object, the objects to be referenced should already have been transferred previously to the device. We'll write a function that will create a brand new playlist and set the references at creation-time. Setting the references is as easy as setting a property value, specifically the WPD_OBJECT_REFERENCES property.&lt;/p&gt;&lt;pre&gt;static void CreatePlaylistObject(
        PortableDeviceApiLib.PortableDeviceClass pPortableDevice,
        string parentID,
        string filename,
        ref string[] refObjectIDs,
        ref string newObjectID)
{
    // Get content interface required to create object
    PortableDeviceApiLib.IPortableDeviceContent pContent;
    pPortableDevice.Content(out pContent);

    // Create properties collection to send while creating object
    PortableDeviceApiLib.IPortableDeviceValues pValues =
        (PortableDeviceApiLib.IPortableDeviceValues)
            new PortableDeviceTypesLib.PortableDeviceValuesClass();

    pValues.SetStringValue(ref PortableDevicePKeys.WPD_OBJECT_PARENT_ID,
                            parentID);
    pValues.SetStringValue(ref PortableDevicePKeys.WPD_OBJECT_ORIGINAL_FILE_NAME,
                            filename);
    pValues.SetStringValue(ref PortableDevicePKeys.WPD_OBJECT_NAME,
                            filename);
    pValues.SetGuidValue(ref PortableDevicePKeys.WPD_OBJECT_FORMAT,
                            ref PortableDeviceGuids.WPD_OBJECT_FORMAT_WPLPLAYLIST);

    // References are stored in a PropVariantCollection object
    PortableDeviceApiLib.IPortableDevicePropVariantCollection pRefIDs =
        (PortableDeviceApiLib.IPortableDevicePropVariantCollection)
            new PortableDeviceTypesLib.PortableDevicePropVariantCollection();

    // Add the string object IDs to the references property
    for (int i = 0; i &amp;lt; refObjectIDs.Length; i++)
    {
        PortableDeviceApiLib.tag_inner_PROPVARIANT propvarValue;
        StringToPropVariant(refObjectIDs[i], out propvarValue);

        pRefIDs.Add(ref propvarValue);
    }

    // Add the references property to the property collection
    pValues.SetIPortableDevicePropVariantCollectionValue(
        ref PortableDevicePKeys.WPD_OBJECT_REFERENCES, pRefIDs);

    // Create the playlist object using the CreateObjectWithPropertiesOnly API
    // The properties to be set have already been defined in pValues
    pContent.CreateObjectWithPropertiesOnly(pValues, ref newObjectID);
}
&lt;/pre&gt;
&lt;p&gt;Since the playlist object does not require any data to be transferred, we will be using the &lt;em&gt;CreateObjectWithPropertiesOnly&lt;/em&gt; API to create it. The function takes in the parent object i.e. the location at which the playlist object should be created and&amp;nbsp;the name to use for the object. The object IDs that the playlist object will reference are passed in as a string array. The function will return the object ID of the new playlist object on success.&lt;/p&gt;
&lt;p&gt;We create an &lt;em&gt;IPortableDeviceValues&lt;/em&gt; instance - pValues -&amp;nbsp;that will hold the properties of the playlist object to be applied. We set the required parent object ID, name and format properties. To add the references, we convert each string into a PROPVARIANT using our &lt;em&gt;StringToPropVariant&lt;/em&gt; converter. We then add the PROPVARIANT into an &lt;em&gt;IPortableDevicePropVariantCollection &lt;/em&gt;object. &lt;/p&gt;
&lt;p&gt;Once we are done converting the input string array of object IDs into a PropVariant collection, we add the WPD_OBJECT_REFERENCES property to the &lt;em&gt;pValues &lt;/em&gt;values collection with the PropVariant collection as the value (which is the list of object IDs to add as a reference).&lt;/p&gt;
&lt;p&gt;Once the properties have been prepared, we call the &lt;em&gt;CreateObjectWithPropertiesOnly&lt;/em&gt; API and create the object. Once the API completes successfully, a new object ID is returned which is the object ID of the newly created playlist object. &lt;/p&gt;
&lt;p&gt;Invoking this function would look something like this:&lt;/p&gt;&lt;pre&gt;string newObjectID = "";

// Set the playlist to reference objects o1 and o2
string[] refObjectIDs = new string[2];
refObjectIDs[0] = "o1";
refObjectIDs[1] = "o2";

CreatePlaylistObject(pPortableDevice, "s10001", "hits2006.pls",
                            ref refObjectIDs, ref newObjectID);
&lt;/pre&gt;
&lt;p&gt;Creating properties-only objects of other formats follows the same style. An example of another properties-only object is a &lt;em&gt;Contact &lt;/em&gt;object - name, email, phone, etc. are just properties of the object itself. The object doesn't have any data. You can modify the function we have just presented to use the appropriate WPD_OBJECT_FORMAT code and create a contact object.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1436353" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /><category term="C#" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/C_2300_/" /></entry><entry><title>Creating WPD PROPVARIANTs in C# without using interop</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2007/01/08/creating-wpd-propvariants-in-c-without-using-interop.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2007/01/08/creating-wpd-propvariants-in-c-without-using-interop.aspx</id><published>2007-01-09T02:23:38Z</published><updated>2007-01-09T02:23:38Z</updated><content type="html">&lt;p&gt;Previous posts have covered how to create, manage and marshal PROPVARIANTs using interop. Here's a way on how to create a PROPVARIANT without interop. &lt;/p&gt; &lt;p&gt;The &lt;em&gt;IPortableDeviceValues&lt;/em&gt; interface exposes a &lt;em&gt;SetStringValue&lt;/em&gt; method that allows a regular C# string to be added into the collection. IPortableDeviceValues also exposes a &lt;em&gt;GetValue&lt;/em&gt; method which lets us retrieve any added property value as a PROPVARIANT. Armed with these two facts, it's pretty obvious how we can manufacture a string PROPVARIANT.&lt;/p&gt;&lt;pre&gt;static void StringToPropVariant(string value,
        out PortableDeviceApiLib.tag_inner_PROPVARIANT propvarValue)
{
    // We'll use an IPortableDeviceValues object to transform the
    // string into a PROPVARIANT
    PortableDeviceApiLib.IPortableDeviceValues pValues =
        (PortableDeviceApiLib.IPortableDeviceValues)
            new PortableDeviceTypesLib.PortableDeviceValuesClass();

    // We insert the string value into the IPortableDeviceValues object
    // using the SetStringValue method
    pValues.SetStringValue(ref PortableDevicePKeys.WPD_OBJECT_ID, value);

    // We then extract the string into a PROPVARIANT by using the 
    // GetValue method
    pValues.GetValue(ref PortableDevicePKeys.WPD_OBJECT_ID, 
                                out propvarValue);
}
&lt;/pre&gt;
&lt;p&gt;We create an instance of an IPortableDeviceValues object named &lt;em&gt;pValues&lt;/em&gt;. We then call SetStringValue with a dummy property key and the string to be converted. pValues now contains exactly one item - a string value which is internally held as a PROPVARIANT. To extract the internally held PROPVARIANT, we call &lt;em&gt;GetValue&lt;/em&gt; which returns us a PROPVARIANT value.&lt;/p&gt;
&lt;p&gt;This example function can be extended for other PROPVARIANT types. Use &lt;em&gt;Object Browser&lt;/em&gt; and take a look at the Set* methods exposed by IPortableDeviceValues. Follow the same paradigm - use Set*Value for your source target type and then use GetValue to extract it as a PROPVARIANT.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1436283" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /><category term="C#" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/C_2300_/" /></entry><entry><title>Connecting to a WPD device in C#</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/12/21/connecting-to-a-wpd-device-in-c.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/12/21/connecting-to-a-wpd-device-in-c.aspx</id><published>2006-12-22T02:51:27Z</published><updated>2006-12-22T02:51:27Z</updated><content type="html">&lt;p&gt;This is the C# equivalent of an &lt;a href="http://blogs.msdn.com/dimeby8/archive/2006/09/27/774259.aspx"&gt;earlier C++ post&lt;/a&gt; on connecting to a WPD device. We'll concentrate on the implementation here - you may refer back to that earlier&amp;nbsp;post for further explanation.&lt;/p&gt;&lt;pre&gt;// Create our client information collection
PortableDeviceApiLib.IPortableDeviceValues pValues = 
        (PortableDeviceApiLib.IPortableDeviceValues)
            new PortableDeviceTypesLib.PortableDeviceValuesClass();

// We have to provide at the least our name, version, revision
pValues.SetStringValue(
        ref PortableDevicePKeys.WPD_CLIENT_NAME, "Sample Client");
pValues.SetUnsignedIntegerValue(
        ref PortableDevicePKeys.WPD_CLIENT_MAJOR_VERSION, 1);
pValues.SetUnsignedIntegerValue(
        ref PortableDevicePKeys.WPD_CLIENT_MINOR_VERSION, 0);
pValues.SetUnsignedIntegerValue(
        ref PortableDevicePKeys.WPD_CLIENT_REVISION, 2);

// Create a new IPortableDevice instance
PortableDeviceApiLib.PortableDeviceClass pPortableDevice = 
        new PortableDeviceApiLib.PortableDeviceClass();

// We are now ready to open a connection to the device
// We'll assume deviceID contains a valid WPD device path and connect to it
pPortableDevice.Open(deviceID, pValues);
&lt;/pre&gt;
&lt;p&gt;As you can see, it's pretty straight-forward to open a connection to a WPD device. We will set up our client information in a PortableDeviceValues collection and then send in the collection to the &lt;em&gt;Open&lt;/em&gt; API call. If the device ID was valid, then a device connection will be opened and we can begin to use &lt;em&gt;pPortableDevice&lt;/em&gt; to communicate with the device.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1343844" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Marshalling variant properties in C#</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/12/12/marshalling-variant-properties-in-c.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/12/12/marshalling-variant-properties-in-c.aspx</id><published>2006-12-13T02:43:10Z</published><updated>2006-12-13T02:43:10Z</updated><content type="html">&lt;p&gt;This post is a continuation of the one&amp;nbsp;covering &lt;a href="http://blogs.msdn.com/dimeby8/archive/2006/12/11/wpd-property-retrieval-in-c.aspx"&gt;WPD property retrieval in C#&lt;/a&gt;. That post explained how to marshal strings and the basic int properties. Handling other types such as VT_DATE and VT_BOOL is trickier so I'll try to cover them here.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;VT_DATE&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;VT_DATE variants hold the date time value in a field of type &lt;em&gt;double&lt;/em&gt;. So we'll need to update our C# PropVariant definition to include a double field. &lt;/p&gt;&lt;pre&gt;[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct PropVariant
{
    [FieldOffset(0)]
    public short variantType;
    [FieldOffset(8)]
    public IntPtr pointerValue;
    [FieldOffset(8)]
    public byte byteValue;
    [FieldOffset(8)]
    public long longValue;
    &lt;strong&gt;&lt;em&gt;[FieldOffset(8)]
    public double dateValue;&lt;/em&gt;&lt;/strong&gt;
} 
&lt;/pre&gt;
&lt;p&gt;Once we have a field that can hold the value, we need to convert it to something C# can understand. PROPVARIANTs are really OLE automation structures, so we can use the &lt;em&gt;DateTime.FromOADate&lt;/em&gt; method to coerce our double into a &lt;em&gt;DateTime&lt;/em&gt; object.&lt;/p&gt;&lt;pre&gt;DateTime date = DateTime.FromOADate(pvValue.dateValue);
&lt;/pre&gt;
&lt;p&gt;To convert from &lt;em&gt;DateTime&lt;/em&gt; to the double value in the PropVariant, use &lt;em&gt;DateTime.ToOADate&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;VT_BOOL&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;VT_BOOL variants hold the bool value in a field of type &lt;em&gt;short&lt;/em&gt;. So we'll need to update the C# PropVariant definition to include the short field.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;pre&gt;[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct PropVariant
{
    [FieldOffset(0)]
    public short variantType;
    [FieldOffset(8)]
    public IntPtr pointerValue;
    [FieldOffset(8)]
    public byte byteValue;
    [FieldOffset(8)]
    public long longValue;
    [FieldOffset(8)]
    public double dateValue;
    &lt;strong&gt;&lt;em&gt;[FieldOffset(8)]
    public short boolValue;&lt;/em&gt;&lt;/strong&gt;
} 
&lt;/pre&gt;
&lt;p&gt;Once we have the short field, we can convert it to a bool using &lt;em&gt;Convert.ToBoolean&lt;/em&gt;.&lt;/p&gt;&lt;pre&gt;bool b = Convert.ToBoolean(pvValue.boolValue);
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;VT_CLSID&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;VT_CLSID variants hold the GUID value in a field of type &lt;em&gt;CLSID*&lt;/em&gt;. The value itself is a pointer to a blob of memory allocated using CoTaskMemAlloc. Since we are dealing with a pointer, we can use the &lt;em&gt;pointerValue&lt;/em&gt; field to access this.&lt;/p&gt;
&lt;p&gt;We then use &lt;em&gt;PtrToStructure&lt;/em&gt; (similar to how we handled LPWSTR) to marshal this pointer into a Guid we can use ourselves.&lt;/p&gt;&lt;pre&gt;Guid guid = (Guid)Marshal.PtrToStructure(pvValue.pointerValue, typeof(Guid));&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;StructureToPtr&lt;/em&gt; can be used to marshal a Guid back into the PropVariant.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I'll update this post if I encounter other types that require special handling. Feel free to leave a comment on any that you come across as well.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1269114" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /><category term="C#" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/C_2300_/" /></entry><entry><title>WPD Content Enumeration in C#</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/12/12/wpd-content-enumeration-in-c.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/12/12/wpd-content-enumeration-in-c.aspx</id><published>2006-12-12T22:10:13Z</published><updated>2006-12-12T22:10:13Z</updated><content type="html">&lt;p&gt;WPD content enumeration in C# is pretty close to the C++ style. Here's a basic example that enumerates the object IDs of the objects present on the device.&lt;/p&gt;&lt;pre&gt;static void Enumerate(
    ref PortableDeviceApiLib.IPortableDeviceContent pContent,
    string parentID,
    string indent)
{
    //
    // Output object ID
    //
    Console.WriteLine(indent + parentID);

    indent += "    ";

    //
    // Enumerate children (if any)
    //
    PortableDeviceApiLib.IEnumPortableDeviceObjectIDs pEnum;
    pContent.EnumObjects(0, parentID, null, out pEnum);

    uint cFetched = 0;
    do
    {
        string objectID;
        pEnum.Next(1, out objectID, ref cFetched);

        if (cFetched &amp;gt; 0)
        {
            //
            // Recurse into children
            //
            Enumerate(ref pContent, objectID, indent);
        }
    } while (cFetched &amp;gt; 0);
}

static void StartEnumerate(
    ref PortableDeviceApiLib.PortableDeviceClass pPortableDevice)
{
    //
    // Get content interface required to enumerate
    //
    PortableDeviceApiLib.IPortableDeviceContent pContent;
    pPortableDevice.Content(out pContent);

    Enumerate(ref pContent, "DEVICE", "");
}
&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;StartEnumerate&lt;/em&gt;&amp;nbsp;is the entry point into the enumeration. This needs to be called with a valid opened device connection. StartEnumerate then defers to the recursive &lt;em&gt;Enumerate&lt;/em&gt; function which requires the parent object ID to enumerate. Using the &lt;em&gt;EnumObjects&lt;/em&gt; API, we get back the &lt;em&gt;IEnumPortableDeviceObjectIDs&lt;/em&gt; enumerator. For each object in the enumeration, we call &lt;em&gt;Enumerate&lt;/em&gt; again.&lt;/p&gt;
&lt;p&gt;Caveats:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;IEnumPortableDeviceObjectIDs::Next&lt;/em&gt; allows more than one object IDs to be returned. We don't take advantage of this optimization since the default interop doesn't handle the array very well. If you need to use this, you will have to edit the interop by hand and fix up the assembly to handle the array correctly.&lt;/li&gt;&lt;/ul&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1268035" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /><category term="C#" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/C_2300_/" /></entry><entry><title>Setting WPD properties in C#</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/12/11/setting-wpd-properties-in-c.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/12/11/setting-wpd-properties-in-c.aspx</id><published>2006-12-11T23:43:18Z</published><updated>2006-12-11T23:43:18Z</updated><content type="html">&lt;p&gt;Since setting a WPD property requires manipulating a PROPVARIANT structure through interop, we must make use of the marshalling rules we set in our &lt;a href="http://blogs.msdn.com/dimeby8/archive/2006/12/11/wpd-property-retrieval-in-c.aspx"&gt;last post&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;We'll take a look at a function that allows string properties to be set. &lt;/p&gt;&lt;pre&gt;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);
}
&lt;/pre&gt;
&lt;p&gt;The example assumes that a device connection has already been opened. For this function to build, the PropVariant definition needs to be present and &lt;a href="http://blogs.msdn.com/dimeby8/archive/2006/12/06/where-are-the-wpd-property-keys-in-c.aspx"&gt;PortableDeviceConstants.cs&lt;/a&gt; needs to be part of the project.&lt;/p&gt;
&lt;p&gt;We first acquire the &lt;em&gt;IPortableDeviceProperties&lt;/em&gt;&amp;nbsp;interface since that exposes the &lt;em&gt;SetValues&lt;/em&gt; 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 &lt;em&gt;Marshal.StringToCoTaskMemUni&lt;/em&gt;. We next marshal our definition into a pointer and then re-marshal the pointer into the &lt;em&gt;tag_inner_PROPVARIANT&lt;/em&gt; 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 &lt;em&gt;SetValues&lt;/em&gt; API.&lt;/p&gt;
&lt;p&gt;This style of preparing the PROPVARIANT is common across different APIs. e.g. The &lt;em&gt;Delete &lt;/em&gt;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 &lt;em&gt;Delete&lt;/em&gt; API.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1260925" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /><category term="C#" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/C_2300_/" /></entry><entry><title>WPD property retrieval in C#</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/12/11/wpd-property-retrieval-in-c.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/12/11/wpd-property-retrieval-in-c.aspx</id><published>2006-12-11T23:17:53Z</published><updated>2006-12-11T23:17:53Z</updated><content type="html">&lt;p&gt;WPD property values are retrieved as PROPVARIANTs through the WPD API. Unfortunately there is no native support for PROPVARIANTs in C#. To correctly retrieve the property values (or anything that is in a PROPVARIANT) through the WPD API in C#, we must use marshalling.&lt;/p&gt; &lt;p&gt;&lt;em&gt;PortableDeviceApiLib.tag_inner_PROPVARIANT&lt;/em&gt; is the data-type exposed through the WPD Interop layer. This data-type exposes the &lt;em&gt;.vt&lt;/em&gt; member, but it does not expose the anonymous union which is the &lt;em&gt;raison d'être&lt;/em&gt; for the PROPVARIANT structure. &lt;/p&gt; &lt;p&gt;To get to the union, we need to re-define the PROPVARIANT data-type in C#. We can get by with the following definition:&lt;/p&gt;&lt;pre&gt;using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct PropVariant
{
    [FieldOffset(0)]
    public short variantType;
    [FieldOffset(8)]
    public IntPtr pointerValue;
    [FieldOffset(8)]
    public byte byteValue;
    [FieldOffset(8)]
    public long longValue;
} 
&lt;/pre&gt;
&lt;p&gt;This follows exactly the struct layout of a PROPVARIANT. We haven't included all the PROPVARIANT fields, feel free to add them as and when you need them. For our example, we'll manage with the ones above.&lt;/p&gt;
&lt;p&gt;Now that we have a C# specific PROPVARIANT definition, we should be able to convert back and forth to the interop version. This isn't as easy as a simple cast - we'll have to use marshalling to do this.&lt;/p&gt;
&lt;p&gt;To convert from the interop &lt;em&gt;tag_inner_PROPVARIANT&lt;/em&gt; to our C# &lt;em&gt;PropVariant&lt;/em&gt;: &lt;pre&gt;PortableDeviceApiLib.tag_inner_PROPVARIANT ipValue = 
                     new PortableDeviceApiLib.tag_inner_PROPVARIANT();

IntPtr ptrValue = Marshal.AllocHGlobal(Marshal.SizeOf(ipValue));
Marshal.StructureToPtr(ipValue, ptrValue, false);

PropVariant pv = (PropVariant)Marshal.PtrToStructure(p, typeof(PropVariant));
&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;To convert from our C# &lt;em&gt;PropVariant&lt;/em&gt; to the interop &lt;em&gt;tag_inner_PROPVARIANT&lt;/em&gt;: &lt;pre&gt;PropVariant pvValue = new PropVariant();

IntPtr ptrValue = Marshal.AllocHGlobal(Marshal.SizeOf(pvValue));
Marshal.StructureToPtr(pvValue, ptrValue, false);

PortableDeviceApiLib.tag_inner_PROPVARIANT ipValue =
    (PortableDeviceApiLib.tag_inner_PROPVARIANT)
    Marshal.PtrToStructure(ptrValue, typeof(PortableDeviceApiLib.tag_inner_PROPVARIANT));
&lt;/pre&gt;
&lt;p&gt;Essentially what we are doing for both cases is getting a pointer to the original structure and then casting it back (via marshalling) to the second structure.&lt;/p&gt;
&lt;p&gt;Now let's see this in action - we'll attempt to retrieve all the properties for the &lt;em&gt;DEVICE&lt;/em&gt; object and display the value for all string properties.&lt;/p&gt;&lt;pre&gt;static void DisplayProperties(
    PortableDeviceApiLib.PortableDeviceClass pPortableDevice, 
    string objectID)
{
    //
    // 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);

    //
    // Call the GetValues API, we specify null to indicate we
    // want to retrieve all properties
    //
    PortableDeviceApiLib.IPortableDeviceValues pPropValues;
    pProperties.GetValues(objectID, null, out pPropValues);

    //
    // Get count of properties
    //
    uint cPropValues = 0;
    pPropValues.GetCount(ref cPropValues);
    Console.WriteLine("Received " + cPropValues.ToString() + " properties");

    for (uint i = 0; i &amp;lt; cPropValues; i++)
    {
        //
        // Retrieve the property at index 'i'
        //
        PortableDeviceApiLib._tagpropertykey propKey = 
                        new PortableDeviceApiLib._tagpropertykey();
        PortableDeviceApiLib.tag_inner_PROPVARIANT ipValue = 
                        new PortableDeviceApiLib.tag_inner_PROPVARIANT();
        pPropValues.GetAt(i, ref propKey, ref ipValue);

        //
        // Allocate memory for the intermediate marshalled object
        // and marshal it as a pointer
        //
        IntPtr ptrValue = Marshal.AllocHGlobal(Marshal.SizeOf(ipValue));
        Marshal.StructureToPtr(ipValue, ptrValue, false);

        //
        // Marshal the pointer into our C# object
        //
        PropVariant pvValue = 
            (PropVariant)Marshal.PtrToStructure(ptrValue, typeof(PropVariant));

        //
        // Display the property if it a string (VT_LPWSTR is decimal 31)
        //
        if (pvValue.variantType == 31 /*VT_LPWSTR*/)
        {
            Console.WriteLine("{0}: Value is \"{1}\"", 
                (i + 1).ToString(), Marshal.PtrToStringUni(pvValue.pointerValue));
        }
        else
        {
            Console.WriteLine("{0}: Vartype is {1}", 
                (i + 1).ToString(), pvValue.variantType.ToString());
        }
        //PropVariant pv = new PropVariant(ip);
    }
}
&lt;/pre&gt;
&lt;p&gt;The example assumes that a device connection has already been opened. It also assumes that the PropVariant definition (from above) is present somewhere in the class definition. &lt;/p&gt;
&lt;p&gt;We need to start with obtaining the &lt;em&gt;IPortableDeviceProperties &lt;/em&gt;interface. Once we have the interface, we call the &lt;em&gt;GetValues&lt;/em&gt; API to retrieve the properties. This is in a ValuesCollection object, so we use &lt;em&gt;GetAt&lt;/em&gt; to retrieve each property. The retrieved property is of type &lt;em&gt;tag_inner_PROPVARIANT&lt;/em&gt; which we convert to our C# PropVariant definition using marshalling. Once we have the data in our PropVariant structure, we can reference the string by marshalling the pointer value in the PropVariant to a &lt;em&gt;string&lt;/em&gt; object.&lt;/p&gt;
&lt;p&gt;As an exercise, try extending this example to display the value for properties of different vartypes.&amp;nbsp; In our next post, we'll see how we can set property values.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1260807" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /><category term="C#" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/C_2300_/" /></entry><entry><title>Where are the WPD property keys in C#?</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/12/06/where-are-the-wpd-property-keys-in-c.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/12/06/where-are-the-wpd-property-keys-in-c.aspx</id><published>2006-12-06T22:15:20Z</published><updated>2006-12-06T22:15:20Z</updated><content type="html">&lt;p&gt;If you followed the exercise from our &lt;a href="http://blogs.msdn.com/dimeby8/archive/2006/12/05/c-and-the-wpd-api.aspx"&gt;first C# post&lt;/a&gt;,&amp;nbsp;you must have noticed that the PortableDeviceApi and PortableDeviceTypes typelibs don't expose the &lt;a href="http://blogs.msdn.com/dimeby8/archive/2006/09/27/774526.aspx"&gt;WPD property keys&lt;/a&gt; such as WPD_OBJECT_ID, WPD_OBJECT_FORMAT, etc. This can be a major blocker since for starters, we need to specify a basic set of client information properties to open a connection to the device.&lt;/p&gt; &lt;p&gt;If you used C++, the WPD property keys&amp;nbsp;definitions could be brought in by linking against PortableDeviceGuids.lib. Linking against a C lib is not an option for a C# application. It's not even possible to generate a typelib for a C lib. The only option is to manually define each of the property keys all over again in your C# application.&lt;/p&gt; &lt;p&gt;The C++ PROPERTYKEY data type maps to the &lt;em&gt;PortableDeviceApiLib._tagpropertykey&lt;/em&gt; interop data type. We can look up the PROPERTYKEY declaration in PortableDevice.h and use that to generate an equivalent C# property key object.&lt;/p&gt; &lt;p&gt;Let's take a look at WPD_OBJECT_ID - the C++ definition (from PortableDevice.h) is:&lt;/p&gt;&lt;pre&gt;// 
// WPD_OBJECT_ID 
//   [ VT_LPWSTR ] Uniquely identifies object on the Portable Device. 
DEFINE_PROPERTYKEY( WPD_OBJECT_ID , 0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC, 0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C , 2 ); 
&lt;/pre&gt;&lt;br&gt;Mapping this to C# would result in something like: &lt;pre&gt;class PortableDevicePKeys
{
    static PortableDevicePKeys()
    {
        WPD_OBJECT_ID.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC, 0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C);
        WPD_OBJECT_ID.pid = 2;
    }

    public static PortableDeviceApiLib._tagpropertykey WPD_OBJECT_ID;
}
&lt;/pre&gt;
&lt;p&gt;We can then reference the property as PortableDevicePKeys.WPD_OBJECT_ID whenever required.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Making life easier&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Since defining each of these property keys by hand may cause severe &lt;a href="http://en.wikipedia.org/wiki/Repetitive_strain_injury" target="_blank"&gt;RSI&lt;/a&gt;, here's&amp;nbsp;a little script that will generate the definitions for you given PortableDevice.h. In addition to generating the property keys, it will also generate the GUID definitions (such as for WPD_FUNCTIONAL_CATEGORY_STORAGE, WPD_OBJECT_FORMAT_WMA, etc.)&lt;/p&gt;
&lt;p&gt;&lt;textarea style="font-size: 8pt; width: 100%; height: 146px" rows="1" readonly cols="1"&gt;//
// Name: genCSinc.js
// Copyright: Microsoft 2006
// Revision: 1.0
//
// This script can be used to generate a C# .cs file that contains
// the equivalent WPD property-keys and GUIDs as defined in
// portabledevice.h
// This script is provided as-is and Microsoft does not assume any
// liability. This script may be redistributed as long as the file
// contains these terms of use unmodified.
//
// Usage:
// Switch to the folder where portabledevice.h is present and then run
//     cscript //nologo genCSinc.js
// This will generate portabledeviceconstants.cs. This .cs file can
// then be included in a C# project. 
// To use the propertykeys and the GUIDs, add "using PortableDeviceConstants;"
// to the target file.
// Propertykeys can then be referenced as PortableDevicePKeys.desired_prop_name
//     e.g. PortableDevicePKeys.WPD_PROPERTY_COMMON_HRESULT
// GUIDs can be referenced as PortableDeviceGuids.desired_GUID_name
//     e.g. PortableDeviceGuids.WPD_OBJECT_FORMAT_ALL
//

//
// Use FSO to read/write input/output
//
var fso = new ActiveXObject("Scripting.FileSystemObject");

var f = null;
var fOut = null;
var sIn = "portabledevice.h";
var sOut = "portabledeviceconstants.cs";

//
// Check for input file
//
try
{
    f = fso.OpenTextFile(sIn);
}
catch(e)
{
    WScript.Echo("Expected portabledevice.h to be in the current folder!");
}

//
// Check for output file
//
try
{
    fOut = fso.OpenTextFile(sOut, 2, true);
}
catch(e)
{
    WScript.Echo("Cannot open " + sOut + " for writing!");
}

//
// Write out header
//
fOut.Write("\
using System;\r\n\
\r\n\
namespace PortableDeviceConstants\r\n\
{\r\n\
    class PortableDevicePKeys\r\n\
    {\r\n\
        static PortableDevicePKeys()\r\n\
        {\r\n\
");    

//
// RegEx declarations for PKEYs and GUIDs
//
//e.g. DEFINE_PROPERTYKEY( WPD_CLIENT_MINOR_VERSION , 0x204D9F0C, 0x2292, 0x4080, 0x9F, 0x42, 0x40, 0x66, 0x4E, 0x70, 0xF8, 0x59 , 4 ); 
var rePKEY = /\s*DEFINE_PROPERTYKEY\(\s*(\w+)\s*,\s*(.+)\s*,\s*(\d+)\s*\);/;
var arrPKEY = new Array();

//e.g. DEFINE_GUID(WPD_EVENT_DEVICE_RESET, 0x7755CF53, 0xC1ED, 0x44F3, 0xB5, 0xA2, 0x45, 0x1E, 0x2C, 0x37, 0x6B, 0x27 ); 
var reGUID = /\s*DEFINE_GUID\((\w+),\s(.+)\s\);/;
var arrGUID = new Array();

//
// Parse the file
//
while (!f.AtEndOfStream)
{
    var l = f.ReadLine();

    //
    // Check for PKEYs
    //
    if (l.match(rePKEY))
    {
        //
        // Write out initializations for the PKEYs
        //
        var sName = l.replace(rePKEY, "$1");
        var sGUID = l.replace(rePKEY, "$2");
        var sPID = l.replace(rePKEY, "$3");

        //WPD_CLIENT_NAME.fmtid = new Guid(0x204D9F0C, 0x2292, 0x4080, 0x9F, 0x42, 0x40, 0x66, 0x4E, 0x70, 0xF8, 0x59);
        //WPD_CLIENT_NAME.pid = 2; 
        
        fOut.Write("            " + sName + ".fmtid = new Guid( " + sGUID + ");\r\n");
        fOut.Write("            " + sName + ".pid = " + sPID + ";\r\n");
        fOut.Write("\r\n");

        //
        // Save the PKEY name for declaration at class level
        //
        arrPKEY.push(sName);
    }
    else if (l.match(reGUID))
    {
        //
        // Save the GUIDs since they go into a second class
        //
        var sName = l.replace(reGUID, "$1");
        var sGUID = l.replace(reGUID, "$2");

        arrGUID.push("public static Guid " + sName + " = new Guid( " + sGUID + " );");
    }
}

//
// Write out declarations for PKEYs
//
fOut.Write("        }\r\n\r\n");

for (var i = 0; i &amp;lt; arrPKEY.length; i++)
{
    fOut.Write("        " + "public static PortableDeviceApiLib._tagpropertykey " + arrPKEY[i] + ";\r\n");
}

fOut.Write("\
    } // class PortableDevicePKeys\r\n\
");

//
// Write out GUIDs
//
fOut.Write("\r\n\r\n");
fOut.Write("\
    class PortableDeviceGuids\r\n\
    {\r\n\
");

for (var i = 0; i &amp;lt; arrGUID.length; i++)
{
    fOut.Write("        " + arrGUID[i] + "\r\n");
}

//
// Write out footer
//
fOut.Write("\
    } // class PortableDeviceGuids\r\n\
} // namespace PortableDeviceConstants\r\n\
");

WScript.Echo("Done: " + sOut + " now contains C# WPD constants");
&lt;/textarea&gt; &lt;/p&gt;
&lt;p&gt;Copy the script from the text-box above, paste it into Notepad and save it as &lt;em&gt;gencsinc.js&lt;/em&gt;. Copy PortableDevice.h to the same folder as gencsinc.js and then run gencsinc.js using "&lt;em&gt;cscript gencsinc.js&lt;/em&gt;". This will generate a PortableDeviceConstants.cs file which you may add to your C# project.&lt;/p&gt;
&lt;p&gt;Once the generated file is added to your project, add "&lt;em&gt;using PortableDeviceConstants;&lt;/em&gt;" to your target C# source. To reference property keys, you can simply use &lt;em&gt;PortableDevicePKeys.propertyname&lt;/em&gt; (e.g. PortableDevicePKeys.WPD_OBJECT_ID) and to reference GUIDs, you can simply use &lt;em&gt;PortableDeviceGuids.guidname&lt;/em&gt; (e.g. PortableDeviceGuids.WPD_OBJECT_FORMAT_WMA).&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1224815" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /><category term="C#" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/C_2300_/" /></entry><entry><title>Flying Wiimotes</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/12/06/flying-wiimotes.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/12/06/flying-wiimotes.aspx</id><published>2006-12-06T21:23:48Z</published><updated>2006-12-06T21:23:48Z</updated><content type="html">&lt;p&gt;I just bought a Wii and it has totally changed my gamer attitude, and I wasn't even a gamer to start with. No more button mashing anymore -&amp;nbsp;arm-swinging and flailing with some actual hand-eye coordination is what it's all about.&lt;/p&gt; &lt;p&gt;Of course with all the arm-thrashing, the controllers are bound to gain critical velocity and &lt;a href="http://www.wiihaveaproblem.com/" target="_blank"&gt;boldly go where no controller has gone before&lt;/a&gt;. &lt;/p&gt; &lt;p&gt;I guess I now need to bullet-proof my entertainment center,&amp;nbsp;shatter-proof my windows&amp;nbsp;and put padding on my walls.... Or maybe not - here's how &lt;a href="http://www.flickr.com/photos/98046567@N00/sets/72157594405700187/" target="_blank"&gt;a short fishing line can go a long way&lt;/a&gt; towards protecting you from yourself :).&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1224699" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="Misc" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/Misc/" /></entry><entry><title>Enumerating WPD devices in C#</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/12/05/enumerating-wpd-devices-in-c.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/12/05/enumerating-wpd-devices-in-c.aspx</id><published>2006-12-06T04:06:32Z</published><updated>2006-12-06T04:06:32Z</updated><content type="html">&lt;p&gt;Let's take a look at how we can enumerate WPD devices in C#. This post assumes that you already have a project set up using &lt;a href="http://blogs.msdn.com/dimeby8/archive/2006/12/05/c-and-the-wpd-api.aspx"&gt;these instructions&lt;/a&gt;. (Update: You will also need to follow the disassembly/reassembly instructions below for this to work correctly.)&lt;/p&gt;&lt;pre&gt;static void Main(string[] args)
{
    //
    // Get an instance of the device manager
    //
    PortableDeviceApiLib.PortableDeviceManagerClass devMgr 
        = new PortableDeviceApiLib.PortableDeviceManagerClass();

    //
    // Probe for number of devices
    //
    uint cDevices = 1;
    devMgr.GetDevices(null, ref cDevices);

    //
    // Re-allocate if needed
    //
    if (cDevices &amp;gt; 0)
    {
        string[] deviceIDs = new string[cDevices];
        devMgr.GetDevices(deviceIDs, ref cDevices);

        for (int ndxDevices = 0; ndxDevices &amp;lt; cDevices; ndxDevices++)
        {
            Console.WriteLine("Device[{0}]: {1}", 
                    ndxDevices + 1, deviceIDs[ndxDevices]);
        }
    }
    else
    {
        Console.WriteLine("No WPD devices are present!");
    }
}
&lt;/pre&gt;
&lt;p&gt;This code maps almost line-by-line to the &lt;a href="http://blogs.msdn.com/dimeby8/archive/2006/08/15/701442.aspx"&gt;C++ enumeration example&lt;/a&gt;. We manufacture an instance of the device manager using PortableDeviceApiLib.PortableDeviceManagerClass. We then use the instance to probe how many devices are there. If you remember, the &lt;em&gt;GetDevices &lt;/em&gt;API will return back in cDevices the number of devices (actually the size of the string array that should be specified to retrieve all the device IDs). &lt;/p&gt;
&lt;p&gt;Once we know the number of devices, we allocate an appropriately sized array and then call &lt;em&gt;GetDevices&lt;/em&gt; again. This time, the array will contain all the device IDs. To display them, we simply iterate over the array and print them out.&lt;/p&gt;
&lt;p&gt;Gotchas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We could use the &lt;em&gt;string[]&lt;/em&gt;&amp;nbsp; notation since the GetDevices API returned an array of CoTaskMemAlloc'd LPWSTR strings 
&lt;li&gt;We can't use the &lt;em&gt;string&lt;/em&gt; datatype for&amp;nbsp;API calls that require a buffer allocated by the caller. &lt;em&gt;GetDeviceFriendlyName&lt;/em&gt;&amp;nbsp;is an example of this - such API calls require prior allocation of &lt;em&gt;ushort[]&lt;/em&gt; and then on success, character-by-character conversion to a C# &lt;em&gt;string&lt;/em&gt;.&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;Update&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Mike R. brought to my notice that the above sample only enumerates one device even if more than one are connected. This is a marshalling restriction - we can work around it by manually fixing up the generated Interop assembly. Follow the steps below to edit the assembly:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Disassemble the PortableDeviceApi interop using the command -&lt;br&gt;ildasm Interop.PortableDeviceApiLib.dll /out:pdapi.il 
&lt;li&gt;Open the IL in Notepad and search for the following string&lt;br&gt;&lt;em&gt;instance void&amp;nbsp; GetDevices([in][out] string&amp;amp;&amp;nbsp; marshal( lpwstr) pPnPDeviceIDs,&lt;/em&gt; 
&lt;li&gt;Replace all instances of the string above with the following string&lt;br&gt;&lt;em&gt;instance void&amp;nbsp; GetDevices([in][out] string[]&amp;nbsp; marshal([]) pPnPDeviceIDs,&lt;/em&gt; 
&lt;li&gt;Save the IL and reassemble the interop using the command -&lt;br&gt;ilasm pdapi.il /dll /output=Interop.PortableDeviceApiLib.dll&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;You can now rebuild your project. You can now first call GetDevices with a NULL parameter to get the count of devices and then call it again with an array to get the device IDs.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1214381" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>C# and the WPD API</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/12/05/c-and-the-wpd-api.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/12/05/c-and-the-wpd-api.aspx</id><published>2006-12-06T00:45:15Z</published><updated>2006-12-06T00:45:15Z</updated><content type="html">&lt;p&gt;There currently is no managed/C# flavor of the WPD API. This, of course, doesn't mean that you are locked out from using the WPD API if you want to use C#. Since the API is a set of COM interfaces, we simply have to interop across from C#. Granted it isn't as easy as a native C# implementation, but it gets the job done.&lt;/p&gt; &lt;p&gt;This and future posts assume that you are using Visual Studio. I used 2005, but other versions should work fine.&lt;/p&gt; &lt;p&gt;To start, create a new C# Console Application project. Next select the "Project&amp;gt;Add Reference ..." menu option. Click the "COM" tab in the "Add Reference" dialog and make sure the following items are selected and then click OK:&lt;br&gt;&lt;em&gt;PortableDeviceApi 1.0 Type Library&lt;/em&gt;&lt;br&gt;&lt;em&gt;PortableDeviceTypes 1.0 Type Library&lt;/em&gt;  &lt;p&gt;Once the references have been added, you can use the Object Browser (Ctrl+Alt+J) to check out the interesting stuff the interop brought in. You'll see two new nodes - &lt;em&gt;Interop.PortableDeviceApiLib &lt;/em&gt;and &lt;em&gt;Interop.PortableDeviceTypesLib&lt;/em&gt;. Under these nodes, you will find &lt;em&gt;PortableDeviceApiLib&lt;/em&gt; and &lt;em&gt;PortableDeviceTypesLib&lt;/em&gt;. We will need to use the latter names to reference other objects. As an exercise, spend some time comparing the objects and methods exposed through interop versus those available in WPD API through C++.&lt;/p&gt; &lt;p&gt;In our next post, we'll re-visit the &lt;a href="http://blogs.msdn.com/dimeby8/archive/2006/08/15/701442.aspx"&gt;C++ device enumeration example&lt;/a&gt; but with a C# twist.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1214210" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /><category term="C#" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/C_2300_/" /></entry><entry><title>Help! WPD API calls randomly fail with 0x800700AA (ERROR_BUSY)</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/11/08/help-wpd-api-calls-randomly-fail-with-0x800700aa-error-busy.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/11/08/help-wpd-api-calls-randomly-fail-with-0x800700aa-error-busy.aspx</id><published>2006-11-09T07:20:08Z</published><updated>2006-11-09T07:20:08Z</updated><content type="html">&lt;p&gt;If you notice your API calls are randomly failing with HRESULT_FROM_WIN32(ERROR_BUSY) / 0x800700AA / -2147024726, then it's likely that the device&amp;nbsp;driver is indeed busy doing something else. It could be that WMP is syncing content to the device, or maybe Explorer needs to refresh its view and is enumerating content on the device.&lt;/p&gt; &lt;p&gt;So what do you do? Simply deal with it. &lt;/p&gt; &lt;p&gt;The fact is that the driver has a choice when an operation is requested while it is busy doing something else. It can either block the contending request or it can return right away with a special error code (in this case ERROR_BUSY). &lt;/p&gt; &lt;p&gt;The current driver model does not advocate a particular choice; so to play it safe, you (as a WPD client app developer) should check if the error was HRESULT_FROM_WIN32(ERROR_BUSY). In that special case, you can randomly try the operation again in a while. Alternatively, you can put up a dialog telling the user that the device is busy doing something else and the user should try again later. Or maybe you can write an asynchronous wrapper around the API which frees you from worrying about all this. Whichever choice you make, just &lt;em&gt;don't treat it as a hard error&lt;/em&gt; and present the user with an error dialog since the user won't understand why it broke.&lt;/p&gt; &lt;p&gt;While special-handling ERROR_BUSY may seem to be a hack and inelegant, unfortunately the behavior is here to stay. Later iterations of the driver model may enforce/advocate an alternative behavior, but the backward-compatibility cross will still have to be carried.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1042704" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /></entry><entry><title>Transferring playlists through WPD</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/11/08/transferring-playlists-through-wpd.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/11/08/transferring-playlists-through-wpd.aspx</id><published>2006-11-09T06:45:00Z</published><updated>2006-11-09T06:45:00Z</updated><content type="html">&lt;P&gt;A playlist (.WPL, .M3U, etc.) is a text file which contains a list of filenames. This list is the "playlist". So transferring the playlist should be easy right? Well, yes it is - if you just want to transfer it as a text file...&lt;/P&gt;
&lt;P&gt;However if you want the playlist semantics to be sent to the device as well, you need to go an extra step (well, several extra steps :). Let's take a look.&lt;/P&gt;
&lt;P&gt;A .WPL playlist in WPD is an object of format WPD_OBJECT_FORMAT_WPLPLAYLIST. Similar WPD formats exist for .M3U, .MPL, .ASX, etc. To associate tracks with the playlist, you must make use of the WPD_OBJECT_REFERENCES parameter. The WPD_OBJECT_REFERENCES parameter is available for most container-style formats. It is of type IPortableDevicePropVariantCollection. &lt;/P&gt;
&lt;P&gt;Now even though WPD_OBJECT_REFERENCES is of type IPortableDevicePropVariantCollection, it can only legally hold PROPVARIANTs of a specific vartype i.e. VT_LPWSTR. Each string must correspond to a valid WPD object ID - these will be the object IDs that you want to associate with the playlist.&lt;/P&gt;Assume we have the following objects already on the device. &lt;PRE&gt;Store
 |
 |-- o1 (Happy.wma) [WPD_OBJECT_FORMAT_WMA]
 |-- o2 (Sad.wma)   [WPD_OBJECT_FORMAT_WMA]
&lt;/PRE&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;Next we want to add a playlist to the device. So we send the playlist first as a regular object of format WPLPLAYLIST. &lt;PRE&gt;Store
 |
 |-- o1 (Happy.wma) 
 |-- o2 (Sad.wma)
 |-- o3 (Emotions.wpl) [WPD_OBJECT_FORMAT_WPLPLAYLIST]
&lt;/PRE&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;Now that we have a WPLPLAYLIST object (given by handle &lt;EM&gt;o3&lt;/EM&gt;), we can set the WPD_OBJECT_REFERENCES property on it. Let's say we want to include &lt;EM&gt;o1&lt;/EM&gt; and &lt;EM&gt;o2&lt;/EM&gt; in the playlist. &lt;PRE&gt;LPWSTR pwszPlaylistObject = NULL;
if (hr == S_OK)
{
    // IMP: Add your code to transfer an object to the device and get back
    // a WPD object handle. We are assigning o3 just for this example
    pwszPlaylistObject = L"o3";
}

CComPtr&amp;lt;IPortableDeviceProperties&amp;gt; spProperties;
if (hr == S_OK)
{
    // IMP: Add your code to obtain the Properties interface from the
    // Device interface
}

CComPtr&amp;lt;IPortableDeviceValues&amp;gt; spValues;
CComPtr&amp;lt;IPortableDevicePropVariantCollection&amp;gt; spReferences;

if (hr == S_OK)
{
    // IMP: Add your code to CoCreate spValues and spReferences here
}

// Add o1 and o2 for the references 0 - your code will probably have
// a list of object IDs that you obtained prior by enumeration
PROPVARIANT pv = {0};
if (hr == S_OK)    
{
    pv.vt = VT_LPWSTR;
    pv.pwszVal = L"o1";
    
    hr = spReferences-&amp;gt;Add(&amp;amp;pv);
}

if (hr == S_OK)    
{
    pv.vt = VT_LPWSTR;
    pv.pwszVal = L"o2";
    
    hr = spReferences-&amp;gt;Add(&amp;amp;pv);
}

// Add the references to the values collection to send to the device
if (hr == S_OK)
{
    hr = spValues-&amp;gt;SetIPortableDevicePropVariantCollectionValue(
                        WPD_OBJECT_REFERENCES, spReferences);
}

// Set the references property on the Playlist object
CComPtr&amp;lt;IPortableDeviceValues&amp;gt; spResults;
if (hr == S_OK)
{
    hr = spProperties-&amp;gt;SetValues(pwszPlaylistObject, spValues, &amp;amp;spResults);
}
&lt;/PRE&gt;
&lt;P&gt;Briefly - you need to create an instance of a PropVariantCollection and add the object IDs that need to be part of the playlist to that collection. Next add that PropVariantCollection to the Values bag that we need to send down to the device. Be sure to specify the REFERENCES propertykey. Finally, use the SetValues API call to send the new value to the device.&lt;/P&gt;
&lt;P&gt;Note that:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Your objects need to already exist on the device before they can be added to the playlist. So if you are syncing a playlist, sync the referenced content first, get&amp;nbsp;the content object IDs&amp;nbsp;and then sync the playlist object and set the REFERENCES property. 
&lt;LI&gt;Modifying the REFERENCES property will &lt;EM&gt;clobber&lt;/EM&gt; any previous value. If you need to add a new object ID, read the property first, add to the collection and then set the property back. 
&lt;LI&gt;You can also set the references at the same time as playlist object creation. The CreateObject* APIs allow property values to be specified in the same call.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;As a curiosity, when you transfer the playlist object to the device, you can transfer just a 0-sized object using CreateObjectWithPropertiesOnly and skip the data since it isn't going to be processed by the device anyway.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1042606" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /></entry><entry><title>Custom MTP formats in WPD</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/10/24/custom-mtp-formats-in-wpd.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/10/24/custom-mtp-formats-in-wpd.aspx</id><published>2006-10-25T02:43:35Z</published><updated>2006-10-25T02:43:35Z</updated><content type="html">&lt;p&gt;The list of supported WPD formats is available in MSDN &lt;a href="http://windowssdk.msdn.microsoft.com/en-us/library/ms739469.aspx" target="_blank"&gt;here&lt;/a&gt;. But what if your MTP device supports a brand new format that isn't in the supported list? WPD does allow a way to reference custom/vendor extended formats.&lt;/p&gt; &lt;p&gt;If you take a closer look at the GUID values in &lt;em&gt;PortableDevice.h&lt;/em&gt; for all those object formats, you might notice a pattern. &lt;/p&gt;&lt;pre&gt;:
DEFINE_GUID(WPD_OBJECT_FORMAT_MP3, 0x30090000, 0xAE6C, 0x4804, 0x98, 0xBA, 0xC5, 0x7B, 0x46, 0x96, 0x5F, 0xE7 );
DEFINE_GUID(WPD_OBJECT_FORMAT_EXIF, 0x38010000, 0xAE6C, 0x4804, 0x98, 0xBA, 0xC5, 0x7B, 0x46, 0x96, 0x5F, 0xE7 );
DEFINE_GUID(WPD_OBJECT_FORMAT_JFIF, 0x38080000, 0xAE6C, 0x4804, 0x98, 0xBA, 0xC5, 0x7B, 0x46, 0x96, 0x5F, 0xE7 );
:
&lt;/pre&gt;
&lt;p&gt;Other than the upper word of the &lt;em&gt;Data1&lt;/em&gt; field, the GUIDs are identical. &lt;/p&gt;
&lt;p&gt;Now if you knew your PTP/MTP spec inside-out, you'd realise right-away that &lt;em&gt;Data1&lt;/em&gt; contains the MTP format codes.&amp;nbsp;Appendix A.1 in the MTP spec provides a list of MTP object format codes and sure enough -&lt;/p&gt;&lt;pre&gt;MP3  = 0x3009
EXIF = 0x3801
JFIF = 0x3808
&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;So if you had to represent your custom MTP format code in WPD, simply replace the upper word of Data1 with your MTP format code and that will work.&lt;/p&gt;&lt;pre&gt;HRESULT MtpToWpdFormat(WORD MtpOpCode, GUID* pWpdFormat)
{
    HRESULT hr = S_OK;

    // Return the MTP format code in the upper WORD of Data1
    *pWpdFormat = WPD_OBJECT_FORMAT_UNSPECIFIED;
    pWpdFormat-&amp;gt;Data1 = MtpOpCode &amp;lt;&amp;lt; 16;

    return hr;
}
&lt;/pre&gt;
&lt;p&gt;You start off with a real WPD format (just so we don't have to fill in the other GUID fields manually). Then we simply update Data1 with the format that we want.&lt;/p&gt;
&lt;p&gt;Conversion of a WPD format to MTP is even easier. It's simply &lt;em&gt;WpdFormat.Data1&amp;gt;&amp;gt;16&lt;/em&gt;&amp;nbsp;where WpdFormat is your WPD object format GUID.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=870690" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>MTP array properties (AINT*/AUINT*) in WPD</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/10/18/mtp-array-properties-aint-auint-in-wpd.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/10/18/mtp-array-properties-aint-auint-in-wpd.aspx</id><published>2006-10-18T21:42:00Z</published><updated>2006-10-18T21:42:00Z</updated><content type="html">&lt;P&gt;MTP allows for a wide range of array-based data types ranging from an array of bytes (AINT8/AUINT8) to an array of GUIDs (AINT128/AUINT128). Check out section 3.2.1 and 3.2.2 in the MTP specification for a complete list and definition.&lt;/P&gt;
&lt;P&gt;Unfortunately the WPD API is restricted in the kind of arrays it can expose. Specifically, the WPD API can only expose VT_UI1|VT_VECTOR arrays due to marshalling limitations.&amp;nbsp;This means that the rich variety of MTP array data types is "lost in translation". To be accurate, the type information is lost, but the data is still accessible as&amp;nbsp;a VT_UI1|VT_VECTOR array.&lt;/P&gt;
&lt;P&gt;To retrieve and set MTP array data type properties, a WPD application must know the specific MTP data type before-hand. It must then use this information to make sure that the data is handled correctly. For setting an array data type property, the size of the byte array provided at the WPD layer must be an exact multiple of the size of the integral MTP data type.&lt;/P&gt;
&lt;P&gt;Let's look at how the value for an MTP array data type property is retrieved.&amp;nbsp;We'll assume that the device property&amp;nbsp;specified is of MTP data&amp;nbsp;type AUINT32 (array of DWORDs).&lt;/P&gt;&lt;STRONG&gt;Reading an array property&lt;/STRONG&gt; &lt;PRE&gt;// This function returns a DWORD array which must be freed with
// CoTaskMemFree. The number of the elements in the array is given
// by *pdwNumElems
HRESULT GetAUINT32DevPropValue(IPortableDevice* pDevice, 
                               WORD wDevPropCode,
                               DWORD** ppdwArray,
                               DWORD* pdwNumElems)
{
    HRESULT hr = S_OK;

    // Get the Properties interface through the Content interface 
    CComPtr&amp;lt;IPortableDeviceContent&amp;gt; spContent;
    if (hr == S_OK)
    {
        hr = pDevice-&amp;gt;Content(&amp;amp;spContent);
    }

    CComPtr&amp;lt;IPortableDeviceProperties&amp;gt; spProperties;
    if (hr == S_OK)
    {
        hr = spContent-&amp;gt;Properties(&amp;amp;spProperties);
    }

    // Since this is a vendor extended property, we need to use the
    // extension GUID
    PROPERTYKEY pkeyProperty;
    pkeyProperty.fmtid = WPD_PROPERTIES_MTP_VENDOR_EXTENDED_DEVICE_PROPS;
    pkeyProperty.pid = wDevPropCode; 

    // Add this propertykey to spPropertyKeys
    CComPtr&amp;lt;IPortableDeviceKeyCollection&amp;gt; spPropertyKeys;
    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceKeyCollection,
                              (VOID**)&amp;amp;spPropertyKeys);
    }

    if (hr == S_OK)
    {
        hr = spPropertyKeys-&amp;gt;Add(pkeyProperty);
    }

    // Use the GetValues API to get the value of the property
    CComPtr&amp;lt;IPortableDeviceValues&amp;gt; spPropertyValues;
    if (hr == S_OK)
    {
        hr = spProperties-&amp;gt;GetValues(L"DEVICE", spPropertyKeys, &amp;amp;spPropertyValues);
    }

    // Get value of the requested property
    BYTE* pbBuffer = NULL;
    DWORD cbBuffer = 0;
    if (hr == S_OK)
    {               
        hr = spPropertyValues-&amp;gt;GetBufferValue(pkeyProperty, &amp;amp;pbBuffer, &amp;amp;cbBuffer);
    }
    else if (hr == S_FALSE)
    {
        // Get the actual error associated with retrieving the property
        (void) spPropertyValues-&amp;gt;GetErrorValue(pkeyProperty, &amp;amp;hr);
    }

    // Cast retrieved buffer to DWORD and return that
    if (hr == S_OK)
    {
        *ppdwArray = (DWORD*) pbBuffer;
        *pdwNumElems = cbBuffer / sizeof(DWORD);
    }

    return hr;
}
&lt;/PRE&gt;
&lt;P&gt;We have to make use of the extended GUID to construct our vendor extended property key. The actual value is obtained by the &lt;EM&gt;GetValues&lt;/EM&gt; API and is returned in an IPortableDeviceValues collection. Since this is a byte array, we can directly use the GetBufferValue method to get the array. This allocates the byte array using CoTaskMemAlloc and we simply pass this onto the caller after casting it what the caller needed.&lt;/P&gt;
&lt;P&gt;Almost identical code can be written to retrieve MTP object properties of array data type. Simply use the object property vendor extension GUID and supply the appropriate object ID parameter to the GetValues call.&lt;/P&gt;
&lt;P&gt;As an exercise, you can convert this sample function into a template-based one so that it can work with different MTP array data types.&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Next let's take a look at how we'd set the same AUINT32 property used above.&lt;/P&gt;&lt;STRONG&gt;Setting an array property&lt;/STRONG&gt; &lt;PRE&gt;// Requires a DWORD array as an input parameter along with the
// size of the array (in DWORDs, /not/ in bytes)
HRESULT SetAUINT32DevPropValue(IPortableDevice* pDevice, 
                               WORD wDevPropCode,
                               DWORD* pdwArray,
                               DWORD cdwNumElems)
{
    HRESULT hr = S_OK;

    // Get the Properties interface through the Content interface 
    CComPtr&amp;lt;IPortableDeviceContent&amp;gt; spContent;
    if (hr == S_OK)
    {
        hr = pDevice-&amp;gt;Content(&amp;amp;spContent);
    }

    CComPtr&amp;lt;IPortableDeviceProperties&amp;gt; spProperties;
    if (hr == S_OK)
    {
        hr = spContent-&amp;gt;Properties(&amp;amp;spProperties);
    }

    // Since this is a vendor extended property, we need to use the
    // extension GUID
    PROPERTYKEY pkeyProperty;
    pkeyProperty.fmtid = WPD_PROPERTIES_MTP_VENDOR_EXTENDED_DEVICE_PROPS;
    pkeyProperty.pid = wDevPropCode; 

    // Create an IPortableDeviceValues instance to place the new value into
    CComPtr&amp;lt;IPortableDeviceValues&amp;gt; spValues;
    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**)&amp;amp;spValues);
    }

    // Place the new value into the collection using SetBufferValue and casting the supplied
    // array to a BYTE array
    if (hr == S_OK)
    {
        hr = spValues-&amp;gt;SetBufferValue(pkeyProperty, (BYTE*) pdwArray, cdwNumElems * sizeof(DWORD));
    }

    // Update the properties on the DEVICE object
    CComPtr&amp;lt;IPortableDeviceValues&amp;gt; spResults;
    if (hr == S_OK)
    {
        hr = spProperties-&amp;gt;SetValues(L"DEVICE", spValues, &amp;amp;spResults);
        printf("SetValues returned hr=0x%08X\n", hr);
    }

    // Display the results of the operation
    if (SUCCEEDED(hr) &amp;amp;&amp;amp; spResults != NULL)
    {
        HRESULT hrTemp = S_OK;
        HRESULT hrSetValue = S_OK;

        hrTemp = spResults-&amp;gt;GetErrorValue(pkeyProperty, &amp;amp;hrSetValue);
        if (hrTemp == S_OK)
        {
            printf("\tSetting array property returned hr=0x%08X\n", hrSetValue);
            hr = hrSetValue;
        }
    }

    return hr;
}
&lt;/PRE&gt;
&lt;P&gt;Setting MTP array properties through WPD doesn't turn out to be that hard. We simply stuff the supplied DWORD array into a IPortableDeviceValues collection as a BYTE array using SetBufferValue. We then use the &lt;EM&gt;SetValues&lt;/EM&gt; API to send the value to the driver/device. We then check the results to make sure that the value was set.&lt;/P&gt;
&lt;P&gt;Again, it should be a trivial exercise to templatize this function to work with different MTP data types.&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Couple of comments:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Since the typed arrays are always converted into plain BYTE arrays, you can technically set, say,&amp;nbsp;a DWORD array value on a AUINT16 property. You can even read it back as a DWORD array value; but the value on the device will still be maintained and understood as AUINT16.&lt;/LI&gt;
&lt;LI&gt;As an exclusion to the above loophole, you cannot set the value to an array whose size in bytes is not an integral&amp;nbsp;multiple of the base MTP data type. i.e. you cannot set the value of an AUINT32 property to a BYTE array of only 3, 5, 9, etc. elements. It can only be set to BYTE arrays of sizes 4,8,12,...,N*4.&lt;/LI&gt;
&lt;LI&gt;If you&amp;nbsp;&lt;EM&gt;absolutely &lt;/EM&gt;must know the array data type of the MTP property you are trying to set, send the MTP GetDevicePropDesc (or GetObjectPropDesc) command using the raw SendCommand interface and then parse the returned property description dataset (see MTP spec section 5.1.2.1 or section 5.3.2.3 -&amp;nbsp;the datatype is the second WORD in the dataset).&lt;/LI&gt;&lt;/UL&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=836580" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /><category term="MTP" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/MTP/" /></entry><entry><title>Accessing MTP vendor extended properties through WPD</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/10/17/accessing-mtp-vendor-extended-properties-through-wpd.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/10/17/accessing-mtp-vendor-extended-properties-through-wpd.aspx</id><published>2006-10-17T22:05:00Z</published><updated>2006-10-17T22:05:00Z</updated><content type="html">&lt;P&gt;Apart from the standard device and object properties defined in the MTP specification, device vendors are free to add their own properties. The codes for these&amp;nbsp;properties must lie in the vendor-extension range as defined by the MTP spec in section 3.3.1. Specifically, the range for vendor-extended device properties is 0xD000-0xD3FF and the range for vendor-extended object properties is 0xD800-0xDBFF.&lt;/P&gt;
&lt;P&gt;Since these are vendor-extended properties, the WPD API and the MTP driver have no pre-existing mapping of these properties i.e. there isn't a handy pre-defined PROPERTYKEY to reference them with. Instead we have to manipulate the PROPERTYKEY structure to instruct the MTP driver on what properties to retrieve/set.&lt;/P&gt;
&lt;P&gt;A &lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/structures/propertykey.asp" target=_blank mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/structures/propertykey.asp"&gt;PROPERTYKEY&lt;/A&gt; is a structure composed of&amp;nbsp;a GUID and a DWORD.&lt;/P&gt;&lt;PRE&gt;typedef struct {
    GUID fmtid;
    DWORD pid;
} PROPERTYKEY;
&lt;/PRE&gt;
&lt;P&gt;We set the &lt;EM&gt;fmtid&lt;/EM&gt; member to a specific GUID and the &lt;EM&gt;pid&lt;/EM&gt; member to the MTP property code that we are interested in. The GUIDs differ for device and object properties and are discussed further below.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;MTP device properties&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Vendor-extended MTP device properties are identified with the &lt;EM&gt;fmtid&lt;/EM&gt; member set to WPD_PROPERTIES_MTP_VENDOR_EXTENDED_DEVICE_PROPS. The &lt;EM&gt;pid &lt;/EM&gt;member then specifies the actual MTP device property code.&lt;/P&gt;
&lt;P&gt;e.g. Consider the Janus DRM device certificate property on the device. It is defined by the device property code - 0xD101. &lt;/P&gt;
&lt;P&gt;At the WPD level, this property would appear as the property-key "{4D545058-8900-40b3-8F1D-DC246E1E8370}.53505". The first part is the string representation of the actual WPD_PROPERTIES_MTP_VENDOR_EXTENDED_DEVICE_PROPS GUID. The second part (following the period) is the device property code in decimal (0xD101=53505).&lt;/P&gt;
&lt;P&gt;To reference this property in code, we'd use something like:&lt;/P&gt;&lt;PRE&gt;PROPERTYKEY pkeyJanusDevCert;
pkeyJanusDevCert.fmtid = WPD_PROPERTIES_MTP_VENDOR_EXTENDED_DEVICE_PROPS;
pkeyJanusDevCert.pid = 0xD101; 
&lt;/PRE&gt;
&lt;P&gt;&lt;STRONG&gt;MTP object properties&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Vendor-extended MTP object properties are identified with the &lt;EM&gt;fmtid&lt;/EM&gt; member set to WPD_PROPERTIES_MTP_VENDOR_EXTENDED_OBJECT_PROPS. The &lt;EM&gt;pid &lt;/EM&gt;member then specifies the actual MTP object property code.&lt;/P&gt;
&lt;P&gt;e.g. Consider the Janus DRM BuyNow object property. It is defined by the object property code - 0xD901. &lt;/P&gt;
&lt;P&gt;At the WPD level, this property would appear as the property-key {4D545058-4FCE-4578-95C8-8698A9BC0F49}.55553". The first part is the string representation of the actual WPD_PROPERTIES_MTP_VENDOR_EXTENDED_OBJECT_PROPS GUID. The second part (following the period) is the object property code in decimal (0xD901=55553).&lt;/P&gt;
&lt;P&gt;To reference this property in code, we'd use something like:&lt;/P&gt;&lt;PRE&gt;PROPERTYKEY pkeyBuyNow;
pkeyBuyNow.fmtid = WPD_PROPERTIES_MTP_VENDOR_EXTENDED_OBJECT_PROPS;
pkeyBuyNow.pid = 0xD901; 
&lt;/PRE&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Once we have the the PROPERTYKEY representations of the MTP properties that we need, we can simply use these PROPERTYKEYs just like other regular WPD PROPERTYKEYs. They can be placed in IPortableDevicePropertyKeyCollection or IPortableDeviceValues interfaces and then can be used with the GetValues/SetValues APIs.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=835854" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /><category term="MTP" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/MTP/" /></entry><entry><title>Setting WPD properties</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/10/17/setting-wpd-properties.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/10/17/setting-wpd-properties.aspx</id><published>2006-10-17T21:17:42Z</published><updated>2006-10-17T21:17:42Z</updated><content type="html">&lt;p&gt;The &lt;a href="http://msdn.microsoft.com/library/en-us/wpd_sdk/htm/iportabledevicepropertiessetvalues.asp"&gt;IPortableDeviceProperties::SetValues&lt;/a&gt;&amp;nbsp;API can be used to set WPD object properties. The documentation covers the API parameters as well, so we'll skip to the meatier sample.&lt;/p&gt; &lt;p&gt;The SetValues API can be used to set multiple properties at the same time. You can, of course, also just set one property at a time. For illustration, we'll set two properties in one go. We'll pick the WPD_DEVICE_FRIENDLY_NAME and WPD_DEVICE_SYNC_PARTNER properties on the DEVICE object.&lt;/p&gt;&lt;pre&gt;HRESULT UpdateDeviceProperties(IPortableDevice* pDevice,
                               LPCWSTR pwszFriendlyName,
                               LPCWSTR pwszSyncPartner)
{
    HRESULT hr = S_OK;

      // Get the Properties interface through the Content interface 
    CComPtr&amp;lt;IPortableDeviceContent&amp;gt; spContent;
    if (hr == S_OK)
    {
        hr = pDevice-&amp;gt;Content(&amp;amp;spContent);
    }

    CComPtr&amp;lt;IPortableDeviceProperties&amp;gt; spProperties;
    if (hr == S_OK)
    {
        hr = spContent-&amp;gt;Properties(&amp;amp;spProperties);
    }

    // Create an IPortableDeviceValues instance to place the new values into
    CComPtr&amp;lt;IPortableDeviceValues&amp;gt; spValues;
    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**)&amp;amp;spValues);
    }

    // Add the new device friendly name property value to the collection
    if (hr == S_OK)
    {
        hr = spValues-&amp;gt;SetStringValue(WPD_DEVICE_FRIENDLY_NAME, pwszFriendlyName);        
    }

    // Add the new sync partner property value to the collection
    if (hr == S_OK)
    {
        hr = spValues-&amp;gt;SetStringValue(WPD_DEVICE_SYNC_PARTNER, pwszSyncPartner);        
    }

    // Update the properties on the DEVICE object
    CComPtr&amp;lt;IPortableDeviceValues&amp;gt; spResults;
    if (hr == S_OK)
    {
        hr = spProperties-&amp;gt;SetValues(L"DEVICE", spValues, &amp;amp;spResults);
        printf("SetValues returned hr=0x%08X\n", hr);
    }

    // Display the results of the operation
    if (SUCCEEDED(hr) &amp;amp;&amp;amp; spResults != NULL)
    {
        HRESULT hrTemp = S_OK;
        HRESULT hrSetValue = S_OK;

        hrTemp = spResults-&amp;gt;GetErrorValue(WPD_DEVICE_FRIENDLY_NAME, &amp;amp;hrSetValue);
        if (hrTemp == S_OK)
        {
            printf("\tSetting WPD_DEVICE_FRIENDLY_NAME returned hr=0x%08X\n", hrSetValue);
        }

        hrTemp = spResults-&amp;gt;GetErrorValue(WPD_DEVICE_SYNC_PARTNER, &amp;amp;hrSetValue);
        if (hrTemp == S_OK)
        {
            printf("\tSetting WPD_DEVICE_SYNC_PARTNER returned hr=0x%08X\n", hrSetValue);
        }        
    }

    return hr;
}
&lt;/pre&gt;
&lt;p&gt;We first obtain the &lt;em&gt;IPortableDeviceProperties&lt;/em&gt; interface needed to execute the SetValues API. Next we have to create an &lt;em&gt;IPortableDeviceValues &lt;/em&gt;instance to stuff the new values into. If you recall from &lt;a href="http://blogs.msdn.com/dimeby8/archive/2006/09/27/774526.aspx"&gt;the earlier post&lt;/a&gt;, an IPortableDeviceValues interface is just a map of property-keys and values.&lt;/p&gt;
&lt;p&gt;Once we have our collection of values to set ready, we can use the &lt;em&gt;SetValues&lt;/em&gt; API to send them to the driver for processing. The driver returns S_OK if all of the supplied values could be set. If any of the values could not be set, then &lt;em&gt;the driver returns S_FALSE&lt;/em&gt;. S_FALSE does &lt;strong&gt;not&lt;/strong&gt; indicate a catastrophic failure. It means that the client should check the returned IPortableDeviceValues results&amp;nbsp;collection for more information.&lt;/p&gt;
&lt;p&gt;Retrieving the result for each property is fairly easy since we use the standard methods exposed by IPortableDeviceValues. The results collection is always a collection of VT_ERRORs,&amp;nbsp;so we can safely call GetErrorValue for any of the keys we are interested in retrieving the error for. If you have a large number of properties being set, you should use &lt;a href="http://msdn.microsoft.com/library/en-us/wpd_sdk/htm/iportabledevicevaluesgetat.asp" target="_blank"&gt;IPortableDeviceValues::GetAt&lt;/a&gt; in a loop to iterate through the results collection instead of retrieving the error values one at a time.&lt;/p&gt;
&lt;p&gt;Setting property values for other objects follows the same pattern - just use the appropriate object ID parameter for the SetValues call.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=835661" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /></entry><entry><title>How to check if a WPD property can be modified</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/10/16/how-to-check-if-a-wpd-property-can-be-modified.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/10/16/how-to-check-if-a-wpd-property-can-be-modified.aspx</id><published>2006-10-17T03:07:56Z</published><updated>2006-10-17T03:07:56Z</updated><content type="html">&lt;p&gt;We'll create a little helper function that we can use to figure out if a WPD object property can be modified. &lt;/p&gt; &lt;p&gt;All WPD object properties have attributes on them. The MSDN documentation provides a complete list of attributes &lt;a href="http://msdn.microsoft.com/library/en-us/wpd_sdk/htm/wpdattributes.asp" target="_blank"&gt;here&lt;/a&gt;. 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. &lt;/p&gt; &lt;p&gt;The &lt;a href="http://msdn.microsoft.com/library/en-us/wpd_sdk/htm/iportabledevicepropertiesgetpropertyattributes.asp" target="_blank"&gt;IPortableDeviceProperties::GetPropertyAttributes&lt;/a&gt;&amp;nbsp;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.&lt;/p&gt;&lt;pre&gt;// 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&amp;amp; bWritable)
{
    HRESULT hr = S_OK;

    // Get the Properties interface through the Content interface 
    CComPtr&amp;lt;IPortableDeviceContent&amp;gt; spContent;
    if (hr == S_OK)
    {
        hr = pDevice-&amp;gt;Content(&amp;amp;spContent);
    }

    CComPtr&amp;lt;IPortableDeviceProperties&amp;gt; spProperties;
    if (hr == S_OK)
    {
        hr = spContent-&amp;gt;Properties(&amp;amp;spProperties);
    }

    // Execute the GetPropertyAttributes for the supplied parameters
    CComPtr&amp;lt;IPortableDeviceValues&amp;gt; spAttributes;
    if (hr == S_OK)
    {        
        hr = spProperties-&amp;gt;GetPropertyAttributes(pwszObjectID, pkeyProperty, &amp;amp;spAttributes);
    }

    // Check for WPD_PROPERTY_ATTRIBUTE_CAN_WRITE in the returned collection
    if (hr == S_OK)
    {
        hr = spAttributes-&amp;gt;GetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_WRITE, &amp;amp;bWritable);
    }

    return hr;
}
&lt;/pre&gt;
&lt;p&gt;Checking attributes turns out to be pretty easy. The &lt;em&gt;GetPropertyAttributes&lt;/em&gt; API returns us an &lt;em&gt;IPortableDeviceValues&lt;/em&gt; 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 &lt;em&gt;IPortableDeviceProperties&lt;/em&gt; interface instead of obtaining it each time the function is called.&lt;/p&gt;
&lt;p&gt;This is just an example of how &lt;em&gt;GetPropertyAttributes&lt;/em&gt; 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.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=833264" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /></entry><entry><title>Sending MTP commands through WPD (Part 3 - data from device)</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/10/16/sending-mtp-commands-through-wpd-part-3-data-from-device.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/10/16/sending-mtp-commands-through-wpd-part-3-data-from-device.aspx</id><published>2006-10-17T00:26:12Z</published><updated>2006-10-17T00:26:12Z</updated><content type="html">&lt;p&gt;Let's use the GetDevicePropValue command (MTP spec - section D.2.21) to illustrate this. GetDevicePropValue takes one parameter - the device property code that we want to retrieve the current value for. We'll retrieve the BatteryLevel device property (MTP spec - section C.2.2) which is&amp;nbsp;of type&amp;nbsp;UINT8. &lt;/p&gt; &lt;p&gt;From the WPD API, we will need this sequence of commands:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITH_DATA_TO_READ - to initiate the transfer  &lt;li&gt;WPD_COMMAND_MTP_EXT_READ_DATA - to actually transfer the data  &lt;li&gt;WPD_COMMAND_MTP_EXT_END_DATA_TRANSFER - to flag command completion and retrieve the MTP response code&lt;/li&gt;&lt;/ol&gt;&lt;strong&gt;Initiating the sequence&lt;/strong&gt; &lt;pre&gt;#include &amp;lt;portabledevice.h&amp;gt;
#include &amp;lt;portabledeviceapi.h&amp;gt;
#include &amp;lt;wpdmtpextensions.h&amp;gt;

// We'll return the BatteryLevel in the BYREF parameter
HRESULT GetBatteryLevel(IPortableDevice* pDevice, BYTE&amp;amp; bBatteryLevel)
{
    HRESULT hr = S_OK;
    const WORD PTP_OPCODE_GETDEVICEPROPVALUE = 0x1015; 
    const WORD PTP_DEVICEPROPCODE_BATTERYLEVEL = 0x5001; 
    const WORD PTP_RESPONSECODE_OK = 0x2001;     // 0x2001 indicates command success

    // Build basic WPD parameters for the command
    CComPtr&amp;lt;IPortableDeviceValues&amp;gt; spParameters;
    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**)&amp;amp;spParameters);
    }

    // WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITH_DATA_TO_READ is the command we need here
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY, 
                                           WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITH_DATA_TO_READ.fmtid);
    }

    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID, 
                                              WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITH_DATA_TO_READ.pid);
    }

    // Specify the actual MTP op-code that we want to execute here
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetUnsignedIntegerValue(WPD_PROPERTY_MTP_EXT_OPERATION_CODE, 
                                                      (ULONG) PTP_OPCODE_GETDEVICEPROPVALUE);
    }

    // GetDevicePropValue requires the property code as an MTP parameter
    // MTP parameters need to be first put into a PropVariantCollection
    CComPtr&amp;lt;IPortableDevicePropVariantCollection&amp;gt; spMtpParams;
    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
                                  NULL,
                                  CLSCTX_INPROC_SERVER,
                                  IID_IPortableDevicePropVariantCollection,
                                  (VOID**)&amp;amp;spMtpParams);
    }

    PROPVARIANT pvParam = {0};
    pvParam.vt = VT_UI4;

    // Specify the BatteryLevel property as the MTP parameter
    if (hr == S_OK)
    {
        pvParam.ulVal = PTP_DEVICEPROPCODE_BATTERYLEVEL;
        hr = spMtpParams-&amp;gt;Add(&amp;amp;pvParam);
    }

    // Add MTP parameters collection to our main parameter list
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetIPortableDevicePropVariantCollectionValue(
                                          WPD_PROPERTY_MTP_EXT_OPERATION_PARAMS, spMtpParams);
    }  

    // Send the command to initiate the transfer
    CComPtr&amp;lt;IPortableDeviceValues&amp;gt; spResults;
    if (hr == S_OK)
    {
        hr = pDevice-&amp;gt;SendCommand(0, spParameters, &amp;amp;spResults);
    }

    // Check if the driver succeeded in sending the command by interrogating WPD_PROPERTY_COMMON_HRESULT
    HRESULT hrCmd = S_OK;
    if (hr == S_OK)
    {
         hr = spResults-&amp;gt;GetErrorValue(WPD_PROPERTY_COMMON_HRESULT, &amp;amp;hrCmd);
    }

    if (hr == S_OK)
    {
        printf("Driver return code (initiating): 0x%08X\n", hrCmd);
        hr = hrCmd;
    }

    // If the transfer was initiated successfully, the driver will return us a context cookie
    LPWSTR pwszCookie = NULL;
    if (hr == S_OK)
    {
         hr = spResults-&amp;gt;GetStringValue(WPD_PROPERTY_MTP_EXT_TRANSFER_CONTEXT, &amp;amp;pwszContext);
    }

    // The driver will also let us know how many bytes will be transferred. This is important to
    // retrieve since we have to read all the data that the device will send us (even if it
    // isn't the size we were expecting), else we run the risk of the device going out of sync
    // with the driver.
    ULONG cbReportedDataSize = 0;
    if (hr == S_OK)
    {
        hr = pResults-&amp;gt;GetUnsignedIntegerValue(WPD_PROPERTY_MTP_EXT_TRANSFER_TOTAL_DATA_SIZE, 
                                               &amp;amp;cbReportedDataSize);
    }

    // Note: The driver provides an additional property - WPD_PROPERTY_MTP_EXT_OPTIMAL_TRANSFER_BUFFER_SIZE
    // which suggests the chunk size that we should retrieve the data in. If your application will be 
    // transferring a large amount of data (&amp;gt;256K), then you should use this property to break down the
    // transfer into small chunks so that your app is more responsive.
    // We'll skip this here since device properties are never that big (especially BatteryLevel)
&lt;/pre&gt;&lt;strong&gt;Reading the data&lt;/strong&gt; &lt;pre&gt;    // If no data will be transferred we need to skip reading in the data
    BOOL bSkipDataPhase = FALSE;
    if (hr == S_OK &amp;amp;&amp;amp; cbReportedDataSize == 0)
    { 
        hr = S_FALSE;
        bSkipDataPhase = TRUE;
    }

    // WPD_COMMAND_MTP_EXT_READ_DATA is the command where we actually read in the data
    (void) spParameters-&amp;gt;Clear();
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY, 
                                           WPD_COMMAND_MTP_EXT_READ_DATA.fmtid);
    }

    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID, 
                                                      WPD_COMMAND_MTP_EXT_READ_DATA.pid);
    }

    // We need to specify the same context that we received earlier
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetStringValue(WPD_PROPERTY_MTP_EXT_TRANSFER_CONTEXT, pwszContext);   
    }

    // We'll need to also allocate a buffer for the command to read data into - this should 
    // be the same size as the number of bytes we are expecting to read (per chunk if applicable)
    BYTE* pbBufferIn = NULL;
    if (hr == S_OK)
    {
        pbBufferIn = (BYTE*) CoTaskMemAlloc(cbReportedDataSize);
        if (pbBufferIn == NULL)
        {
            hr = E_OUTOFMEMORY;
        }
    }

    // Pass the allocated buffer as a parameter
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetBufferValue(WPD_PROPERTY_MTP_EXT_TRANSFER_DATA, 
                                                        pbBufferIn, cbReportedDataSize);
    }

    // Specify the number of bytes to transfer as a parameter
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetUnsignedIntegerValue(WPD_PROPERTY_MTP_EXT_TRANSFER_NUM_BYTES_TO_READ, 
                                                        cbReportedDataSize);
    }

    // Send the command to transfer the data
    spResults = NULL;
    if (hr == S_OK)
    {
        hr = pDevice-&amp;gt;SendCommand(0, spParameters, &amp;amp;spResults);
    }

    // Check if the driver succeeded in tranferring the data
    HRESULT hrCmd = S_OK;
    if (hr == S_OK)
    {
         hr = spResults-&amp;gt;GetErrorValue(WPD_PROPERTY_COMMON_HRESULT, &amp;amp;hrCmd);
    }

    if (hr == S_OK)
    {
        printf("Driver return code (reading data): 0x%08X\n", hrCmd);
        hr = hrCmd;
    }

    // IMPORTANT: The API doesn't really transfer the data into the buffer we provided earlier.
    // Instead it is available in the results collection.
    BYTE* pbBufferOut = NULL;
    ULONG cbBytesRead = 0;
    if (hr == S_OK)
    {
        hr = pResults-&amp;gt;GetBufferValue(WPD_PROPERTY_MTP_EXT_TRANSFER_DATA, &amp;amp;pbBufferOut, &amp;amp;cbBytesRead);
    }

    // Reset hr to S_OK since we skipped the data phase
    if (hr == S_FALSE &amp;amp;&amp;amp; bSkipDataPhase == TRUE)
    {
        hr = S_OK;
    }
&lt;/pre&gt;&lt;strong&gt;Retrieving the response&lt;/strong&gt; &lt;pre&gt;    // WPD_COMMAND_MTP_EXT_END_DATA_TRANSFER is the command to signal transfer completion
    (void) spParameters-&amp;gt;Clear();
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY, 
                                           WPD_COMMAND_MTP_EXT_END_DATA_TRANSFER.fmtid);
    }

    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID, 
                                                      WPD_COMMAND_MTP_EXT_END_DATA_TRANSFER.pid);
    }

    // We need to specify the same context that we received earlier
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetStringValue(WPD_PROPERTY_MTP_EXT_TRANSFER_CONTEXT, pwszContext);   
    }

    // Send the completion command
    spResults = NULL;
    if (hr == S_OK)
    {
        hr = pDevice-&amp;gt;SendCommand(0, spParameters, &amp;amp;spResults);
    }

    // Check if the driver successfully ended the data transfer
    if (hr == S_OK)
    {
         hr = spResults-&amp;gt;GetErrorValue(WPD_PROPERTY_COMMON_HRESULT, &amp;amp;hrCmd);
    }

    if (hr == S_OK)
    {
        printf("Driver return code (ending transfer): 0x%08X\n", hrCmd);
        hr = hrCmd;
    }

    // If the command was executed successfully, we check the MTP response code to see if the device
    // could handle the command. If the device could not handle the command, the data phase would 
    // have been skipped (detected by cbReportedDataSize==0) and the MTP response will indicate the
    // error. 
    DWORD dwResponseCode;
    if (hr == S_OK)
    {
        hr = spResults-&amp;gt;GetUnsignedIntegerValue(WPD_PROPERTY_MTP_EXT_RESPONSE_CODE, &amp;amp;dwResponseCode);
    }

    if (hr == S_OK)
    {
        printf("MTP Response code: 0x%X\n", dwResponseCode);
        hr = (dwResponseCode == (DWORD) PTP_RESPONSECODE_OK) ? S_OK : E_FAIL;
    }

    // If the command was handled by the device, return the property value in the BYREF property
    if (hr == S_OK)
    {
        if (pbBufferOut != NULL)
        {
            bBatteryLevel = (BYTE)(*pbBufferOut);
        }
        else
        {
            // MTP response code was OK, but no data phase occurred
            hr = E_UNEXPECTED;
        }
    }

    // If response parameters are present, it will be contained in the WPD_PROPERTY_MTP_EXT_RESPONSE_PARAMS 
    // property. GetDevicePropValue does not return additional response parameters, so we skip this code 
    // If required, you may find that code in the post that covered sending MTP commands without data

    // Free up any allocated memory
    CoTaskMemFree(pbBufferIn);
    CoTaskMemFree(pbBufferOut);
    CoTaskMemFree(pwszContext);

    return hr;
}

&lt;/pre&gt;
&lt;p&gt;Initiating the data transfer is pretty easy here. The driver lets us know how much data to expect. To transfer the data, we need to pre-allocate a buffer and provide that in our READ_DATA command parameters. Once the data is successfully read, the data is available in the results collection of the sent command. Once all data is transferred, we send the END_DATA_TRANSFER command and retrieve the response code.&lt;/p&gt;
&lt;p&gt;Things to remember:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Allocate a buffer before sending the READ_DATA command - this is required 
&lt;li&gt;The transferred data is &lt;strong&gt;not&lt;/strong&gt; in the allocated buffer but is, instead, in the results collection of the command 
&lt;li&gt;Watch out for the case when data will not be transferred &lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;[The fact that the transferred data is not available in our allocated buffer but is in a different buffer is a side-effect of how the WPD API uses the WDF framework. I'll ping someone on the WDF team to comment on this but this issue may be addressed in a later iteration of the WPD API.]&lt;/em&gt;&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=833056" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /><category term="MTP" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/MTP/" /></entry><entry><title>Sending MTP commands through WPD (Part 2 - data to the device)</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/10/13/sending-mtp-commands-through-wpd-part-2-data-to-the-device.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/10/13/sending-mtp-commands-through-wpd-part-2-data-to-the-device.aspx</id><published>2006-10-13T23:21:15Z</published><updated>2006-10-13T23:21:15Z</updated><content type="html">&lt;p&gt;We'll pick the SetDevicePropValue MTP command (MTP spec - section D.2.22) to illustrate this example. This command requires the device property code as a parameter. We'll try setting the DateTime property (MTP spec - section C.2.18). The DateTime property is of type STRING and we've already covered in a &lt;a href="http://blogs.msdn.com/dimeby8/archive/2006/10/12/creating-an-mtp-string.aspx"&gt;previous post&lt;/a&gt; on how MTP strings are created. We'll use the PackString function from that post here.&lt;/p&gt; &lt;p&gt;When data is involved, sending MTP commands is broken up into three parts:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Initiate the command (inform the device data is coming or is expected)  &lt;li&gt;Perform data transfer (write or read data)  &lt;li&gt;Flag command completion and retrieve response code&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;From the WPD API, this turns out to be a sequence of three commands:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITH_DATA_TO_WRITE &lt;em&gt;or&lt;/em&gt; WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITH_DATA_TO_READ  &lt;li&gt;WPD_COMMAND_MTP_EXT_WRITE_DATA &lt;em&gt;or&lt;/em&gt; WPD_COMMAND_MTP_EXT_READ_DATA  &lt;li&gt;WPD_COMMAND_MTP_EXT_END_DATA_TRANSFER&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;This example will see the use of WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITH_DATA_TO_WRITE, WPD_COMMAND_MTP_EXT_WRITE_DATA and WPD_COMMAND_MTP_EXT_END_DATA_TRANSFER.&lt;/p&gt;&lt;strong&gt;Initiating the sequence&lt;/strong&gt; &lt;pre&gt;#include &amp;lt;portabledevice.h&amp;gt;
#include &amp;lt;portabledeviceapi.h&amp;gt;
#include &amp;lt;wpdmtpextensions.h&amp;gt;

HRESULT SetDateTime(IPortableDevice* pDevice, LPCWSTR pwszDateTime)
{
    HRESULT hr = S_OK;
    const WORD PTP_OPCODE_SETDEVICEPROPVALUE = 0x1016; 
    const WORD PTP_DEVICEPROPCODE_DATETIME = 0x5011; 
    const WORD PTP_RESPONSECODE_OK = 0x2001;     // 0x2001 indicates command success

    // Build basic WPD parameters for the command
    CComPtr&amp;lt;IPortableDeviceValues&amp;gt; spParameters;
    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**)&amp;amp;spParameters);
    }

    // WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITH_DATA_TO_WRITE is the command we need here
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY, 
                                           WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITH_DATA_TO_WRITE.fmtid);
    }

    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID, 
                                              WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITH_DATA_TO_WRITE.pid);
    }

    // Specify the actual MTP op-code that we want to execute here
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetUnsignedIntegerValue(WPD_PROPERTY_MTP_EXT_OPERATION_CODE, 
                                                      (ULONG) PTP_OPCODE_SETDEVICEPROPVALUE);
    }

    // SetDevicePropValue requires the property code as an MTP parameter
    // MTP parameters need to be first put into a PropVariantCollection
    CComPtr&amp;lt;IPortableDevicePropVariantCollection&amp;gt; spMtpParams;
    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
                                  NULL,
                                  CLSCTX_INPROC_SERVER,
                                  IID_IPortableDevicePropVariantCollection,
                                  (VOID**)&amp;amp;spMtpParams);
    }

    PROPVARIANT pvParam = {0};
    pvParam.vt = VT_UI4;

    // Specify the DateTime property as the MTP parameter
    if (hr == S_OK)
    {
        pvParam.ulVal = PTP_DEVICEPROPCODE_DATETIME;
        hr = spMtpParams-&amp;gt;Add(&amp;amp;pvParam);
    }

    // Add MTP parameters collection to our main parameter list
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetIPortableDevicePropVariantCollectionValue(
                                          WPD_PROPERTY_MTP_EXT_OPERATION_PARAMS, spMtpParams);
    }  

    // Figure out the data that we'll be sending - in this case it will be an MTP string
    BYTE* pbBuffer = NULL;
    DWORD cbBufferSize = 0;
    if (hr == S_OK)
    {
        hr = PackString(pwszDateTime, &amp;amp;pbBuffer, &amp;amp;cbBufferSize);
    }

    // We need to inform the device how much data will arrive - this is a required parameter
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetUnsignedLargeIntegerValue(WPD_PROPERTY_MTP_EXT_TRANSFER_TOTAL_DATA_SIZE, 
                                                        &amp;amp;cbBufferSize);
    }

    // Send the command to initiate the transfer
    CComPtr&amp;lt;IPortableDeviceValues&amp;gt; spResults;
    if (hr == S_OK)
    {
        hr = pDevice-&amp;gt;SendCommand(0, spParameters, &amp;amp;spResults);
    }

    // Check if the driver succeeded in sending the command by interrogating WPD_PROPERTY_COMMON_HRESULT
    HRESULT hrCmd = S_OK;
    if (hr == S_OK)
    {
         hr = spResults-&amp;gt;GetErrorValue(WPD_PROPERTY_COMMON_HRESULT, &amp;amp;hrCmd);
    }

    if (hr == S_OK)
    {
        printf("Driver return code (initiating): 0x%08X\n", hrCmd);
        hr = hrCmd;
    }

    // The driver will return us a context cookie that we will need to use during our data transfer
    LPWSTR pwszCookie = NULL;
    if (hr == S_OK)
    {
         hr = spResults-&amp;gt;GetStringValue(WPD_PROPERTY_MTP_EXT_TRANSFER_CONTEXT, &amp;amp;pwszContext);
    }
&lt;/pre&gt;&lt;strong&gt;Sending the data&lt;/strong&gt; &lt;pre&gt;    // WPD_COMMAND_MTP_EXT_WRITE_DATA is the command where we actually send in the data
    (void) spParameters-&amp;gt;Clear();
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY, 
                                           WPD_COMMAND_MTP_EXT_WRITE_DATA.fmtid);
    }

    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID, 
                                                      WPD_COMMAND_MTP_EXT_WRITE_DATA.pid);
    }

    // We need to specify the same context that we received earlier
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetStringValue(WPD_PROPERTY_MTP_EXT_TRANSFER_CONTEXT, pwszContext);   
    }

    // We need to specify the number of bytes arriving with this command. This allows us to 
    // send the data in chunks if required (multiple WRITE_DATA commands). In this case
    // we will send the data in a single chunk
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetUnsignedIntegerValue(WPD_PROPERTY_MTP_EXT_TRANSFER_NUM_BYTES_TO_WRITE, 
                                                   cbBufferSize);
    }

    // Provide the data that needs to be transferred
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetBufferValue(WPD_PROPERTY_MTP_EXT_TRANSFER_DATA, pbBuffer, cbBufferSize);
    }

    // Send the data to the device
    spResults = NULL;
    if (hr == S_OK)
    {
        hr = pDevice-&amp;gt;SendCommand(0, spParameters, &amp;amp;spResults);
    }

    // Check if the data was sent successfully by interrogating COMMON_HRESULT
    if (hr == S_OK)
    {
         hr = spResults-&amp;gt;GetErrorValue(WPD_PROPERTY_COMMON_HRESULT, &amp;amp;hrCmd);
    }

    if (hr == S_OK)
    {
        printf("Driver return code (sending data): 0x%08X\n", hrCmd);
        hr = hrCmd;
    }

    // The driver will inform us on the number of bytes that were actually transferred. Normally this
    // should be the same as the number that we provided.
    DWORD cbBytesWritten = 0;
    if (hr == S_OK)
    {
        hr = spResults-&amp;gt;GetUnsignedIntegerValue(WPD_PROPERTY_MTP_EXT_TRANSFER_NUM_BYTES_WRITTEN, 
                                                &amp;amp;cbBytesWritten);
    }
&lt;/pre&gt;&lt;strong&gt;Retrieving the response&lt;/strong&gt; &lt;pre&gt;    // WPD_COMMAND_MTP_EXT_END_DATA_TRANSFER is the command to signal transfer completion
    (void) spParameters-&amp;gt;Clear();
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY, 
                                           WPD_COMMAND_MTP_EXT_END_DATA_TRANSFER.fmtid);
    }

    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID, 
                                                      WPD_COMMAND_MTP_EXT_END_DATA_TRANSFER.pid);
    }

    // We need to specify the same context that we received earlier
    if (hr == S_OK)
    {
        hr = spParameters-&amp;gt;SetStringValue(WPD_PROPERTY_MTP_EXT_TRANSFER_CONTEXT, pwszContext);   
    }

    // Send the completion command
    spResults = NULL;
    if (hr == S_OK)
    {
        hr = pDevice-&amp;gt;SendCommand(0, spParameters, &amp;amp;spResults);
    }

    // Check if the driver successfully ended the data transfer
    if (hr == S_OK)
    {
         hr = spResults-&amp;gt;GetErrorValue(WPD_PROPERTY_COMMON_HRESULT, &amp;amp;hrCmd);
    }

    if (hr == S_OK)
    {
        printf("Driver return code (ending transfer): 0x%08X\n", hrCmd);
        hr = hrCmd;
    }

    // If the command was executed successfully, we check the MTP response code to see if the
    // device could handle the command and the data. Note that there is a distinction between the command
    // and the data being successfully sent to the device and the command and data being handled successfully 
    // by the device
    DWORD dwResponseCode;
    if (hr == S_OK)
    {
        hr = spResults-&amp;gt;GetUnsignedIntegerValue(WPD_PROPERTY_MTP_EXT_RESPONSE_CODE, &amp;amp;dwResponseCode);
    }

    if (hr == S_OK)
    {
        printf("MTP Response code: 0x%X\n", dwResponseCode);
        hr = (dwResponseCode == (DWORD) PTP_RESPONSECODE_OK) ? S_OK : E_FAIL;
    }

    // If response parameters are present, it will be contained in the WPD_PROPERTY_MTP_EXT_RESPONSE_PARAMS 
    // property. SetDevicePropValue does not return additional response parameters, so we skip this code 
    // If required, you may find that code in the post that covered sending MTP commands without data

    // Free up any allocated memory
    CoTaskMemFree(pbBuffer);
    CoTaskMemFree(pwszContext);

    return hr;
}
&lt;/pre&gt;
&lt;p&gt;Sending a command with data to the device turns out to be a little more involved than sending a command without data. Things to remember are that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prepare your buffer and calculate your buffer size ahead of time 
&lt;li&gt;Be sure to use the correct buffer sizes with the commands 
&lt;li&gt;Be sure to reuse the context cookie 
&lt;li&gt;Check the ...COMMON_HRESULT error code from the driver for each command&lt;/li&gt;&lt;/ul&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=822848" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="WPD" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/WPD/" /><category term="MTP" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/MTP/" /></entry><entry><title>Save Often!</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/10/13/save-often.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/10/13/save-often.aspx</id><published>2006-10-13T21:46:03Z</published><updated>2006-10-13T21:46:03Z</updated><content type="html">&lt;p&gt;One of the problems of living on the cutting edge is that it tends to make you bleed. I have a post-RC1 Vista build on my main desktop. And I was impressed by the fact that I hadn't required a single reboot in over three weeks. &lt;/p&gt; &lt;p&gt;I worked yesterday on putting together a long post on sending MTP commands with a write data phase in Live Writer. I was almost done and thought I'd continue today. I didn't bother saving it as a draft - there wasn't any reason to. I came back today and found my keyboard and mouse unresponsive. Plugging into different USB hubs didn't help.&lt;/p&gt; &lt;p&gt;No big deal, I told myself. I can always remote desktop into my machine from my XP laptop. I tried that and that didn't work. Hmmm... it must have been configured to accept connections from Vista machines only. No problem there - I switched to a Vista machine and tried the remote desktop again. Still no luck.&lt;/p&gt; &lt;p&gt;I guess I didn't enable remote desktop after all (&lt;em&gt;panic rising)&lt;/em&gt;. That could be easily fixed - using Regedit I connected remotely to my Vista box. Yay, that worked (&lt;em&gt;whew!&lt;/em&gt;). I checked the Remote Desktop settings (&lt;em&gt;HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server&lt;/em&gt; for the curious). &lt;em&gt;fDenyTSConnections&lt;/em&gt; was already set to 0. &lt;/p&gt; &lt;p&gt;Now operating in 'Oh sh*t!' mode, I told myself it had to be the firewall. I looked for a way to configure the firewall remotely, but didn't find&amp;nbsp;one (made sense - why would we expose an attack vector for the firewall?). I figured out I could just disable the firewall and that should work. &lt;em&gt;Sc.exe&lt;/em&gt; was my weapon of choice - &lt;em&gt;sc \\mymachine stop mpssvc&lt;/em&gt;. I tried &lt;em&gt;sc query mpssvc &lt;/em&gt;and I got a 'The RPC server is unavailable' error.&amp;nbsp;Maybe that was a good sign.&lt;/p&gt; &lt;p&gt;Tried Remote Desktop again - it &lt;em&gt;didn't&lt;/em&gt; work. I ended up cold-rebooting my machine and losing the post that was supposed to be here instead of this rant.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=822647" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="Misc" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/Misc/" /></entry><entry><title>Creating an MTP string</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/dimeby8/archive/2006/10/12/creating-an-mtp-string.aspx" /><id>http://blogs.msdn.com/b/dimeby8/archive/2006/10/12/creating-an-mtp-string.aspx</id><published>2006-10-13T01:37:01Z</published><updated>2006-10-13T01:37:01Z</updated><content type="html">&lt;p&gt;In some of our later examples, we'll have to send MTP strings to the device. MTP strings are defined in the MTP spec in section 3.2.3. Briefly, the first byte is the number of Unicode characters to follow (including the NULL terminator), the remaining bytes are the characters.&lt;/p&gt; &lt;p&gt;The conversion is pretty simple and is illustrated below. We'll end up reusing this helper function in our later examples as well.&lt;/p&gt;&lt;strong&gt;Helper to create an MTP-style string&lt;/strong&gt; &lt;pre&gt;#include &amp;lt;portabledevice.h&amp;gt;
#include &amp;lt;portabledeviceapi.h&amp;gt;
#include &amp;lt;wpdmtpextensions.h&amp;gt;

// Helper to pack a C-string into a MTP string buffer - returns memory
// that must be freed using CoTaskMemFree
HRESULT PackString(LPCWSTR pwszString, BYTE** ppbBuffer, DWORD* pcbBufferSize)
{
    HRESULT hr = S_OK;

    *ppbBuffer = NULL;
    *pcbBufferSize = 0;

    // Get the number of characters plus the NULL terminator
    DWORD dwNumChars = wcslen(pwszString) + 1;

    // Allocate memory for the MTP string 
    // 1 leading byte + 2*number of characters
    DWORD cbBufferSize = 1 + (dwNumChars*2);
    BYTE* pbBuffer = NULL;
    pbBuffer = (BYTE*) CoTaskMemAlloc(cbBufferSize);

    if (pbBuffer == NULL)
    {
        hr = E_OUTOFMEMORY;
    }

    if (hr == S_OK)
    {
        // Set the leading byte
        pbBuffer[0] = (BYTE)dwNumChars;

        // Copy the input string into the buffer
        // The input string is Unicode, so it's safe to copy as-is
        memcpy(pbBuffer + 1, pwszString, cbBufferSize - 1);
    }

    if (hr == S_OK)
    {
        *ppbBuffer = pbBuffer;
        *pcbBufferSize = cbBufferSize;
    }
    else
    {
        CoTaskMemFree(pbBuffer);
    }

    return hr;
}
&lt;/pre&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=821051" width="1" height="1"&gt;</content><author><name>darene_l@hotmail.com</name><uri>http://blogs.msdn.com/darene_5F00_l_4000_hotmail.com/ProfileUrlRedirect.ashx</uri></author><category term="MTP" scheme="http://blogs.msdn.com/b/dimeby8/archive/tags/MTP/" /></entry></feed>