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 »