WPD: Enumerating Content

June 5, 2011

Introduction

The previous post on the Windows Portable Devices API shows how you can enumerate the WPD-compatible devices conntected to your computer.

This post picks up where the previous one left off and shows how you can enumerate the contents (e.g. pictures, movies…) which are stored on such a device.

Just like before I hooked up a digital camera to my PC. Let’s browse through its contents using the WPD API…

Table Of Contents

Source Code

Go to the download page and download the source code of the first part of this series (article #47).

Unzip the file, open the solution in Visual Studio, connect your camera and hit F5 to run the application. It will recognize your WDP device and display its name.

WPD Device Detected

Now let’s extend the code so that we can also enumerate the contents of the device.

Top of page

PortableDeviceObject

Let’s create some class types to represent each folder and file stored on a WPD-compatible device. Add a new class called PortableDeviceObject to the project and add the code shown below to it.

public abstract class PortableDeviceObject
{
    protected PortableDeviceObject(string id, string name)
    {
        this.Id = id;
        this.Name = name;
    }

    public string Id { get; private set; }

    public string Name { get; private set; }
}

Create a descendant of this abstract class to represent a folder.

public class PortableDeviceFolder : PortableDeviceObject
{
    public PortableDeviceFolder(string id, string name) : base(id, name)
    {
        this.Files = new List<PortableDeviceObject>();
    }

    public IList<PortableDeviceObject> Files { get; set; }
}

Add another class to represent a file.

public class PortableDeviceFile : PortableDeviceObject
{
    public PortableDeviceFile(string id, string name) : base(id, name)
    { }
}

A folder and a file both have an id and a name and a folder also contains a collection files and/or other folders. Pretty simple.

Top of page

Enumerating Content

Time to enumerate the contents of the camera. Open the PortableDevice class in Visual Studio and add the GetContents() method to it.

public PortableDeviceFolder GetContents()
{
    var root = new PortableDeviceFolder("DEVICE", "DEVICE");

    IPortableDeviceContent content;
    this._device.Content(out content);
    EnumerateContents(ref content, root);

    return root;
}

We create a new PortableDeviceFolder instance that represent the device itself, “the root” if you will. This is the starting point for enumerating the contents of the device.

Next add the private method EnumerateContents(…) to the PortableDevice class.

private static void EnumerateContents(ref IPortableDeviceContent content, 
    PortableDeviceFolder parent)
{
    // Get the properties of the object
    IPortableDeviceProperties properties;
    content.Properties(out properties);

    // Enumerate the items contained by the current object
    IEnumPortableDeviceObjectIDs objectIds;
    content.EnumObjects(0, parent.Id, null, out objectIds);

    uint fetched = 0;
    do
    {
        string objectId;

        objectIds.Next(1, out objectId, ref fetched);
        if (fetched > 0)
        {
            var currentObject = WrapObject(properties, objectId);

            parent.Files.Add(currentObject);

            if (currentObject is PortableDeviceFolder)
            {
                EnumerateContents(ref content, (PortableDeviceFolder) currentObject);
            }
        }
    } while (fetched > 0);
}

This method enumerates the contents of the current folder (parent) and includes the necessary recursion to parse the contents of any sub-folders. When the GetContents(…) method completes an entire tree structure (root variable) has been built that represents the contents of the device.

The WrapObject(…) method creates an instance of the PortableDeviceFolder or PortableDeviceFile class types depending on the type of the object. For each folder or file it extract the name and type type (folder or file).

private static PortableDeviceObject WrapObject(IPortableDeviceProperties properties, 
    string objectId)
{
    IPortableDeviceKeyCollection keys;
    properties.GetSupportedProperties(objectId, out keys);

    IPortableDeviceValues values;
    properties.GetValues(objectId, keys, out values);

    // Get the name of the object
    string name;
    var property = new _tagpropertykey();
    property.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC,
                                0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C);
    property.pid = 4;
    values.GetStringValue(property, out name);

    // Get the type of the object
    Guid contentType;
    property = new _tagpropertykey();
    property.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC,
                                0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C);
    property.pid = 7;
    values.GetGuidValue(property, out contentType);

    var folderType = new Guid(0x27E2E392, 0xA111, 0x48E0, 0xAB, 0x0C,
                                0xE1, 0x77, 0x05, 0xA0, 0x5F, 0x85);
    var functionalType = new Guid(0x99ED0160, 0x17FF, 0x4C44, 0x9D, 0x98,
                                    0x1D, 0x7A, 0x6F, 0x94, 0x19, 0x21);

    if (contentType == folderType  || contentType == functionalType)
    {
        return new PortableDeviceFolder(objectId, name);
    }

    return new PortableDeviceFile(objectId, name);
}

Top of page

Demo Application

Now that we are able to enumerate the contents of the device let’s update the demo application so that it displays the contents. Add the following two methods to the Program class of the console application.

public static void DisplayObject(PortableDeviceObject portableDeviceObject)
{
    Console.WriteLine(portableDeviceObject.Name);
    if (portableDeviceObject is PortableDeviceFolder)
    {
        DisplayFolderContents((PortableDeviceFolder) portableDeviceObject);
    }
}

public static void DisplayFolderContents(PortableDeviceFolder folder)
{
    foreach (var item in folder.Files)
    {
        Console.WriteLine(item.Id);
        if (item is PortableDeviceFolder)
        {
            DisplayFolderContents((PortableDeviceFolder) item);
        }
    }
}

Finally all you need to do is adjust the Main() method as follows:

static void Main()
{
    var collection = new PortableDeviceCollection();

    collection.Refresh();

    foreach(var device in collection)
    {
        device.Connect();
        Console.WriteLine(device.FriendlyName);

        var folder = device.GetContents();
        foreach(var item in folder.Files)
        {
            DisplayObject(item);
        }

        device.Disconnect();
    }

    Console.WriteLine();
    Console.WriteLine("Press any key to continue.");
    Console.ReadKey();
}

Hit F5 to run the application and you’ll receive an output similar to the one below.

WPD Device Contents

Voila, I hope you enjoyed this post on the Windows Portable Devices (WPD) API. You can find the source code for this article on the download page of this blog.

Top of page

Advertisements

21 Responses to “WPD: Enumerating Content”

  1. Jason Says:

    Thank you so much!
    This is the best example of using WPD I’ve found, a great help!

  2. Jaran Nilsen Says:

    Thanks for your very useful guides. Hope you don’t mind me building on these implementations in iTunes Agent 2.0 (http://ita.sf.net)

  3. anton van til Says:

    problem when try to get second photo from cameramin Download(file,savetopath)
    err in line resources.getstream(file.id ref property, 0 ref optimalTransfireSize, out wpdStream); give COMeception 0x800700AA

  4. Andrew Says:

    Thank you for the tutorial. I am trying to use the send command built into WPD to take a picture using a digital camera attached to my comp. I can get this working using c++ but would like it to work in c#.

    The c++ example is found here: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375691%28v=vs.85%29.aspx

    thanks for any help…

  5. paul smietan Says:

    Great article? What is the method call to delete files on a WPD camera? I’m able to download the picture onto my local hard disk and would like to clean up after the download.

    thanks!

    -Paul Smietan

  6. Dan Hinsley Says:

    I’m trying to use this on my Zune, and when I get to

    // Get the name of the object
    string name;
    var property = new _tagpropertykey();
    property.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC,
    0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C);
    property.pid = 4;
    values.GetStringValue(property, out name);

    for everything past the first level (“Storage”), I get an element not found. But if I hack the registry so that I can see it in File Explorer, I see folders under directory (even though all the Music folders are just called music).

    I’ve looked at PortableDevice.h to see if I could find a different GUID/Pid that would return a better value, but don’t see anything.

    Anyone have a tip about how I can get to the folder names?

    Dan

  7. Dan Hinsley Says:

    I figured out the issue, for some reason for directories, the name field is blank, but the original name field (pid=12) has the info.

    The only thing left is to figure out how to tell if a file is played or not. I looped through all the string and boolean fields, but can’t find one that is different for a played file versus non-played. Anyone have any ideas?

    Dan

  8. Jan Says:

    Christophe, this is really amazing. I have been searching for days, until I found this page, and it was the first page that was really helpful. 🙂
    Now I am able to find files and folders on my WPD device.

    Next thing is copying files from my PC to my WPD, and deleting files from my WPD.
    Can you please help me here?

    Thanks in advance,
    Jan

  9. supriya Says:

    Hi Chriistophe thanks alot for this great tutorial..its really helpful..i need some help in this.this tutorial is showing all files of the device what i want is “search for a particular file in the device and if it is found i want to return the path of that” please please help me ASAP.any help will be great.i am stuck on this thing from many days….

    Thanks in advance….!!

  10. Jan Says:

    Hi supriya, I have found out a lot about WPD stuff (files and folders). You may have a copy of it, but it is all in Delphi.
    Let me know if you are interested. You can contact me here: janboersma (at) janboersma.nl

  11. supriya Says:

    Hi jan,
    I dont know anything about Delphi.i need this thing in Vb.net or max c#(which i can convert in vb.net later).
    But still i can have a look.can you mail me links on supsabhi@gmail.com

  12. Jan Says:

    Hi supriya, I have sent you an email with an attachment, containing all kinds of WPD functions, that I have created myself. Hope it will be useful.

  13. Andy Says:

    Getting an error when an iPhone is connected

    Element not found. (Exception from HRESULT: 0x80070490)

    on this bit of code

    values.GetStringValue(property, out name);

    Unfortunately the error code is unknown, any ideas? i tried closing anything that could be accessing the phone, itunes, dropbox, etc.

    Great tutorials btw

  14. Jeremy Sutka Says:

    I have implemented a version of this code to copy files from a digital camera to the local computer. When implementing on another computer, the GetContents method just gets a base folder with nothing in it. The deviceID is different on one computer vs. another:

    On unsuccessful computer: \\?\wpdbusenumroot#umb#2&37c186b&0&storage#volume#_??_usbstor#disk&ven_generic-&prod_compact_flash&rev_1.00#20060413092100000&0##{6ac27878-a6fa-4155-ba85-f98f491d4f33}

    On successful computer: \\?\wpdbusenumroot#umb#2&37c186b&0&storage#volume#_??_usbstor#disk&ven_icatchte&prod_icatchtek_spca53&rev_1.00#01.00.00&0##{6ac27878-a6fa-4155-ba85-f98f491d4f33}

    You can see that it is the same device, but it appears there may be a difference in the USB driver software.

    Are you aware of any differences in how the code would work based on USB driver?

    Thank you for any thoughts on this. It has been a problem for months now, and I cannot find anything else that could be causing the issue.

  15. Yossi Pali Says:

    Great article!
    i’m trying to use the above but when reaching to the content.EnumObjects i get no output.
    i’m using the same: PortableDeviceFolder(“DEVICE”, “DEVICE”) as in your example. tried to pass the deviceID but this get the program to held.

    can you tell me what i’m doing wrong.

    p.s. when browsing my device i have to ‘Drives’ the sd card and the phone itself. maybe this is the issue?

  16. Jules Bartow Says:

    Has anyone done this in Powershell?

  17. Joe Somma Says:

    Anyone reply regarding the iPhone issue that Andy posted…same for me…any suggestions…

    Andy Says:

    December 11, 2012 at 17:09u
    Getting an error when an iPhone is connected

    Element not found. (Exception from HRESULT: 0×80070490)

    on this bit of code

    values.GetStringValue(property, out name);

    Unfortunately the error code is unknown, any ideas? i tried closing anything that could be accessing the phone, itunes, dropbox, etc.

    Great tutorials btw

  18. Demo Says:

    Many thanks for this nice article!!!! I have no coding background but it’s easy to understand.

    Unfortunately I have the same issue with my iPhone as mentioned some posts before. Is there already a fix available?

    Many many thanks!


Comments are closed.

%d bloggers like this: