Acceso a dispositivos USB con Windows Portable Devices en C#

Windows Portable Devices es un API que nos permite comunicarnos que dispositivos USB tales como Pen Drives, discos USB, reproductores MP3, etc.

Se trata de un API escrita en C, pero podremos acceder a ella desde .NET dado que se expone a través de COM.

Como siempre, antes de empezar cualquier proyecto, conviene preguntar antes a Google o echar un vistazo a GitHub a ver qué nos pueden contar/ofrecer😉. De los  recursos que he encontrado me gustaría destacar dos:

  • La serie de artículos sobre WPD de Christophe Geers publicados en su blog, así como su wrapper escrito en C# y publicado en GitHub.
  • Un wrapper creado sobre la base del escrito de Christophe y publicado también en GitHub.

Nuestra aplicación de ejemplo estará basada en este último wrapper y tendrá un aspecto como el siguiente:

Windows Portable Devices

En ella mostraremos los dispositivos conectados a nuestro PC en la lista “Devices”. Una vez hecho clic en el botón “Discover”, mostraremos un listado del contenido de nuestro dispositivo. Si hacemos doble clic sobre una carpeta, accederemos al contenido de la misma. En cambio, si lo hacemos sobre un fichero, lo abriremos con la aplicación predeterminada.

Windows Portable Devices

Sencillo y para toda la familia😉.

Show me the code!

A nivel de formulario definimos las siguientes variables:

    public partial class FrmMain : Form
    {
        StandardWindowsPortableDeviceService service = new StandardWindowsPortableDeviceService();
        PortableDeviceFolder currentFolder = null;
        IList<PortableDeviceObject> currentContent = null;
   .....

Donde hacemos uso del API proporcionada por nuestro Wrapper.

Con StandardWindowsPortableDeviceService tenemos acceso a todos los dispositivos conectados que serán objetos de tipo WindowsPortableDevice.

El contenido del dispositivo son objetos de tipo PortableDeviceObject. Bajo esta clase disponemos de las clases específicas PortableDeviceFolder (que representa una carpeta) y PortableDeviceFile (que obviamente hace lo propio con ficheros).

De ahí que definamos los objetos “currentFolder” y “currentContent”.

La carga de nuestros dispositivos USB la haremos de la siguiente forma:


        private void FrmMain_Load(object sender, EventArgs e)
        {
            IList<WindowsPortableDevice> devices = service.Devices;

            devices.ToList().ForEach(device =>
            {
                device.Connect();
                var contents = device.GetContents();
                cmbDevices.Items.Add(contents.Files.First().Name + " " + device.FriendlyName);
                device.Disconnect();
            });

            if (devices.Count > 0)
            {
                cmbDevices.SelectedIndex = 0;
            }

            this.btnDiscover.Enabled = devices.Count > 0;
        }

El botón “Discover” tiene el siguiente código asociado:

        private void btnDiscover_Click(object sender, EventArgs e)
        {
            var device = service.Devices[this.cmbDevices.SelectedIndex];
            device.Connect();

            if (currentFolder == null)
            {
                var rootFolder = device.GetContents().Files;
                currentFolder = device.GetContents((PortableDeviceFolder)rootFolder.First());
                BindList();
            }

            device.Disconnect();
        }

Donde básicamente accedemos al dispositivo seleccionado, y en caso de no tener ninguna “currentFolder”, cargamos el contenido de la carpeta raíz. Por ejemplo, si nuestro dispositivo está asociado a la unidad E:\, cargaremos el contenido de esa unidad. El método “BindList()” tiene el siguiente código:

        private void BindList()
        {
            this.currentContent             = currentFolder.Files;
            this.lstContents.DataSource     = this.currentContent;
            this.lstContents.DisplayMember  = "Name";
            this.lstContents.ValueMember    = "Id";
        }

Por último ya sólo nos falta, manejar el evento doble clic para abrir la carpeta o el archivo seleccionado. Esto lo resolveremos de la siguiente forma:

        private void lstContents_DoubleClick(object sender, EventArgs e)
        {
            var device = service.Devices[this.cmbDevices.SelectedIndex];
            device.Connect();

            if (this.currentContent[this.lstContents.SelectedIndex] is PortableDeviceFolder)
            {
                this.currentFolder = device.GetContents((PortableDeviceFolder)this.currentContent[this.lstContents.SelectedIndex]);
                this.BindList();
            }
            else if (this.currentContent[this.lstContents.SelectedIndex] is PortableDeviceFile)
            {
                var file = this.currentContent[this.lstContents.SelectedIndex] as PortableDeviceFile;
                Process.Start(file.Id);
            }
            
            device.Disconnect();
        }

En este código, comprobamos si el objeto seleccionado es un fichero o una carpeta. Si es una carpeta, cambiamos el “currentFolder” a esta nueva carpeta. Si es un fichero, lo abrimos o ejecutamos con el “Process.Start()”.

Código completo publicado en: https://github.com/programwar/WindowsPortableDevicesExample

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s