Detección de la inserción de unidades de disco extraíbles con .NET

Para los aficionados al desarrollo de aplicaciones con .NET y C#, aquí voy a dejar un trozo de código muy interesante.

Básicamente consiste en un método para notificar a una aplicación de que se ha insertado un nuevo disco en el sistema (un CD, DVD, llave USB, tarjeta de memoria SD/MMC/etc, Disco duro externo, …), un método para identificar la letra de la nueva unidad, y a partir de ellos construir las aplicaciones que se nos ocurran que puedan aprovecharse de esta funcionalidad.

//Incluir esto es obligatorio
using System.Runtime.InteropServices;

/*
 * Escribe aquí el código de tu programa (¡O donde prefieras!)…
 */

//Estructura de datos que almacena la gestión de conexiones

[StructLayout(LayoutKind.Sequential)]
public struct DEV_BROADCAST_VOLUME
{
    public int dbcv_size;
    public int dbcv_devicetype;
    public int dbcv_reserved;
    public int dbcv_unitmask;
}

//Método a sobreescribir para gestionar la llegada de nuevas unidades de disco
protected override void WndProc(ref Message m)
{
    //Estas definiciones están en dbt.h y winuser.h
    //Se ha producido un cambio en los dispositivos
    const int WM_DEVICECHANGE = 0×0219;
    // El sistema detecta un nuevo dispositivo
    const int DBT_DEVICEARRIVAL = 0×8000;
    //Solicita retirada del dispositivo
    const int DBT_DEVICEQUERYREMOVE = 0×8001;
    //Ha fallado la retirada del dispositivo
    const int DBT_DEVICEQUERYREMOVEFAILED = 0×8002;
    //Pendiente extracción del dispositivo
    const int DBT_DEVICEREMOVEPENDING = 0×8003;
    //Dispositivo extraído del sistema
    const int DBT_DEVICEREMOVECOMPLETE = 0×8004;
    // Volumen lógico (Se ha insertado un disco)
    const int DBT_DEVTYP_VOLUME = 0×00000002;
    switch (m.Msg)
    {
        //Cambian los dispositivos del sistema
        case WM_DEVICECHANGE:
        switch (m.WParam.ToInt32())
        {
            //Llegada de un dispositivo
            case DBT_DEVICEARRIVAL:
            {
                int devType = Marshal.ReadInt32(m.LParam, 4);
                //Si es un volumen lógico..(unidad de disco)
                if (devType == DBT_DEVTYP_VOLUME)
                {
                    DEV_BROADCAST_VOLUME vol;
                    vol = (DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(
                        m.LParam, typeof(DEV_BROADCAST_VOLUME));
                    MessageBox.Show(
                        ”Insertada unida de disco, unidad: ” +
                        LetraUnidad(vol.dbcv_unitmask));
                }
            }
            break;
            case DBT_DEVICEREMOVECOMPLETE:
                MessageBox.Show(”Dispositivo retirado.”);
                break;
        }
        break;
    }
    //Ahora usar el manejador predeterminado
    base.WndProc(ref m);
}

//Método para detectar la letra de unidad
char LetraUnidad(int unitmask)
{
    char[] units ={ ‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’,
        ’H', ‘I’, ‘J’, ‘K’, ‘L’, ‘M’, ‘N’, ‘O’, ‘P’,
        ’Q', ‘R’, ‘S’, ‘T’, ‘U’, ‘V’, ‘W’, ‘X’, ‘Y’, ‘Z’ };
    int i = 0;
    //Convetimos la máscara en un array primario y buscamos
    //el índice de la primera ocurrencia (la letra de unidad)
    System.Collections.BitArray ba = new
        System.Collections.BitArray(System.BitConverter.GetBytes(unitmask));
    foreach (bool var in ba)
    {
        if (var == true)
            break;
        i++;
    }
    return units[i];
}

Aquí os dejo (correctamente formateada) la aportación de Carolina, el código en Visual Basic para la detección de unidades:


‘Estructura de datos que almacena la gestión de conexiones
Public Structure dispositivo
  Public dispTamaño As Integer
  Public dispTipo As Integer
  Public dispReserv As Integer
  Public dispMask As Integer
End Structure

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

  ‘Se ha producido un cambio en los dispositivos
  Const deviceChange As Integer = &H219
  ‘El sistema detecta un nuevo dispositivo
  Const deviceArrival As Integer = &H8000
  ‘Solicita retirada del dispositivo
  Const deviceQueryRemove As Integer = &H8001
  ‘Ha fallado la retirada del dispositivo
  Const devideQueryRemoveFailed As Integer = &H8002
  ‘Pendiente extracción del dispositivo
  Const deviceRemovePending As Integer = &H8003
  ‘Dispositivo extraído del sistema
  Const deviceRemoveComplete As Integer = &H8004
  ‘ Volumen lógico (Se ha insertado un disco)
  Const deviceTypeVolume As Integer = &H2

  Select Case m.Msg
    ‘Cambian los dispositivos del sistema
    Case deviceChange
    Select Case m.WParam.ToInt32
      ‘Llegada de un dispositivo
      Case deviceArrival
      Dim devType As Integer = Marshal.ReadInt32(m.LParam, 4)
      ‘Si es un volumen lógico..(unidad de disco)
      If devType = deviceTypeVolume Then
        Dim vol As dispositivo
        vol = CType(Marshal.PtrToStructure(m.LParam, GetType(dispositivo)), dispositivo)
        MessageBox.Show(”Se insertó un dispositivo en la unidad ” & LetraUnidad(vol.dispMask) & “.”)
        ‘ACA HAGO EL TRATAMIENTO DEL DISPOSITIVO INSERTADO
      End If
    Case deviceRemoveComplete
      MessageBox.Show(”Se retiró el dispositivo.”)
    End Select
  End Select

  ‘Ahora se usa el manejador predeterminado
  MyBase.WndProc(m)
End Sub

Private Function LetraUnidad(ByVal unitmask As Integer) As Char
  Dim units() As Char = {”A”, “B”, “C”, “D”, “E”, “F”, “G”, 
      “H”, “I”, “J”, “K”, “L”, “M”, “N”, “O”, “P”, 
      “Q”, “R”, “S”, “T”, “U”, “V”, “W”, “X”, “Y”, “Z”}
  Dim i As Integer = 0
  ‘Convetimos la máscara en un array primario y buscamos
  ‘el índice de la primera ocurrencia (la letra de unidad)
  Dim ba As System.Collections.BitArray
  ba = New System.Collections.BitArray(System.BitConverter.GetBytes(unitmask))

  For i = 0 To ba.Length
    If ba(i) = True Then
    Exit For
  End If
  Next

  Return units(i)
End Function
Publicado en C#. Etiquetas: , , , . 19 Comments »

19 comentarios to “Detección de la inserción de unidades de disco extraíbles con .NET”

  1. Quake Says:

    Hola quisiera saber realmente como compilar ese codigo, lenjuage y tipo de aplicacion, o en que parte del codigo se debe incluir

    parece un buen proyecto y te agradeceria

  2. Antonius Says:

    ¡Pues incluirlo es lo más sencillo del mundo!
    Crea un proyecto Windows.Forms (Lenguaje C#) cualquiera y a continuación pega el trozo de código tal cual, justo debajo del grupo de líneas using.
    Eso sobreescribe el manejador de la ventana con la función grande del snippet. Básicamente, hace lo mismo que el manejador predeterminado, además de mostrar un mensaje diciendo que se ha insertado una unidad de disco. Como por defecto no se sabía qué unidad era, incluí la función auxiliar LetraUnidad para poder acceder más rápido a la ruta de acceso estándar.

  3. Quake Says:

    Ya ejecute el codigo y si funciona

    EL problema es que solo me funciona para memorias usb y discos compactos

    pero no me funciona para tarjetas de memoria al menos en este equipo

  4. Pachas Says:

    Que tal !!! Quisiera saber si hay una forma de que este mismo
    codigo sirviera para una aplicacion en consola, ya que ahi no acepta
    la palabra ” Message “,
    por otra parte gracias por el codigo.

  5. Antonius Says:

    Perdona el retraso en la respuesta.
    Para una aplicación de consola no puedes usar el método MessageBox.Show(“Mensajito de texto”) porque es exclusivo de aplicaciones winForms. Para ello necesitas incluir las winforms escribiendo using System.Windows.Forms;, o bien cambiar el MessageBox.Show por un equivalente en modo texto como System.Console.WriteLine(“TextoAEscribir”);

  6. giropau Says:

    Gracias, por el código. Funciona de maravilla, pero ¿habría alguna forma de saber que dispositivos USB tengo conectados si necesidad de conectarlos después de iniciar el programa?
    Gracias.

  7. Antonius Says:

    ¡Buena pregunta! En éste código me he centrado en la detección de los eventos de inserción de dispositivo, pero no he investigado cómo ver los dispositivos que ya hay conectados (aunque mi instinto me dice que es bastante más sencillo que este snippet).

  8. LuisVB Says:

    Excelente aporte!! No sabe cuanto tiempo anduve buscando un código como este! Desde ya se lo agradezco mucho. Abusando un poco de su bondad, podria decirme como hacer lo mismo pero en Visual Basic 2005 ?? Muchas gracias por adelantado.

  9. Antonius Says:

    Me temo que Visual Basic no es mi especialidad… (¡Lo siento mucho!)

    PD: Gracias a todos por vuestros comentarios🙂

  10. LuisVB Says:

    Ohh lastima!, bueno igual muchas gracias por responder. Abusando nuevamente de su buena volundad, podría Enviarme a mi email un proyecto de C# con este código implementado correctamente?. Lo que sucede es que C# no es mi especialidad (como lo habrá podido notar en mi nick jejeje), entonces me salen algunos errores al compilar el código (hice paso a paso lo que indicaba usted en un mensaje anterior, uso Visual Studio 2005). Mi correo es luisito666(arroba)gmail.com Se lo agradecería infinitamente.

  11. Carolina Says:

    Hola, yo pude pasar a VB el codigo que publico Antonius (en C#) y me anduvo muy bien. Ademas de agradecer su aporte, queria dejarles el mio para los que necesiten:

    ‘Estructura de datos que almacena la gestión de conexiones

    Public Structure dispositivo
    Public dispTamaño As Integer
    Public dispTipo As Integer
    Public dispReserv As Integer
    Public dispMask As Integer
    End Structure

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

    ‘Se ha producido un cambio en los dispositivos
    Const deviceChange As Integer = &H219
    ‘El sistema detecta un nuevo dispositivo
    Const deviceArrival As Integer = &H8000
    ‘Solicita retirada del dispositivo
    Const deviceQueryRemove As Integer = &H8001
    ‘Ha fallado la retirada del dispositivo
    Const devideQueryRemoveFailed As Integer = &H8002
    ‘Pendiente extracción del dispositivo
    Const deviceRemovePending As Integer = &H8003
    ‘Dispositivo extraído del sistema
    Const deviceRemoveComplete As Integer = &H8004
    ‘ Volumen lógico (Se ha insertado un disco)
    Const deviceTypeVolume As Integer = &H2

    Select Case m.Msg
    ‘Cambian los dispositivos del sistema
    Case deviceChange
    Select Case m.WParam.ToInt32
    ‘Llegada de un dispositivo
    Case deviceArrival
    Dim devType As Integer = Marshal.ReadInt32(m.LParam, 4)
    ‘Si es un volumen lógico..(unidad de disco)
    If devType = deviceTypeVolume Then
    Dim vol As dispositivo
    vol = CType(Marshal.PtrToStructure(m.LParam, GetType(dispositivo)), dispositivo)
    MessageBox.Show(“Se insertó un dispositivo en la unidad ” & LetraUnidad(vol.dispMask) & “.”)
    ‘ACA HAGO EL TRATAMIENTO DEL DISPOSITIVO INSERTADO
    End If
    Case deviceRemoveComplete
    MessageBox.Show(“Se retiró el dispositivo.”)
    End Select
    End Select

    ‘Ahora se usa el manejador predeterminado
    MyBase.WndProc(m)
    End Sub

    Private Function LetraUnidad(ByVal unitmask As Integer) As Char
    Dim units() As Char = {“A”, “B”, “C”, “D”, “E”, “F”, “G”, “H”, “I”, “J”, “K”, “L”, “M”, “N”, “O”, “P”, “Q”, “R”, “S”, “T”, “U”, “V”, “W”, “X”, “Y”, “Z”}
    Dim i As Integer = 0
    ‘Convetimos la máscara en un array primario y buscamos
    ‘el índice de la primera ocurrencia (la letra de unidad)
    Dim ba As System.Collections.BitArray
    ba = New System.Collections.BitArray(System.BitConverter.GetBytes(unitmask))

    For i = 0 To ba.Length
    If ba(i) = True Then
    Exit For
    End If
    Next

    Return units(i)
    End Function

  12. Antonius Says:

    Muchas gracias por tu aporte! (En cuanto tenga algo de tiempo intentaré darle el formato tipo código y añadirlo al post).
    Me alegra mucho que este pequeño trozo de código le sea útil a tantas personas, y espero que tu aportación ayude a muchas otras.

    Gracias!

  13. LuisVB Says:

    Excelente!! muchas gracias

  14. SinId3as Says:

    Hola, tengo una duda en esta parte del códgio de vb
    Dim devType As Integer = Marshal.ReadInt32(m.LParam, 4)
    me subraya la parte de “Marshal”, qué es lo que debo de hacer ahí
    Gracias

  15. Antonius Says:

    El Marshal lo que hace es convertir una lectura de datos de memoria “insegura” a memoria “manejada”(segura) para poder usarlos desde .NET sin que haya problemas.

    Más información aquí: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal(VS.80).aspx

  16. Jorge Says:

    Hola, exelente aporte ese pedaso de código,¿sabes cómo podría hacerlo con C/C++?

  17. Jorge Says:

    Hola, me descarge ya el Microsoft Visual C# 2005 Express para tratar de implementar tu código, pero me da bastantes errores, no sé que es lo que este haciendo mal. No conosco mucho C#, tengo experiencia programando aplicaciones para consola en lenguaje C. Seria mucha molestia que pudieras enviarme el proyecto a mi correo? gracias de antemano.
    jorgeriv@gmail.com

  18. Carlos Says:

    muy buen codigo la vdd

  19. pacm Says:

    Muy bueno los dos códigos, todos los códigos que vi por la red eran con Timers y así me parece mucho mejor, gracias de verdad a los dos.


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

A %d blogueros les gusta esto: