External Storage Unit detection with C# in .NET (USB, Card Readers, etc)

It seems that the spanish written post for unit detection is pretty popular between my readers (and particularly Google), so I thought that time had come to upgrade it for a more international language.

This piece of code basically consists in a couple of methods that can enable any application to detect whenever a new storage device has been inserted into the computer, whether it is a CD/DVD, an USB key, a SD/MMC memory card, a external HDD… and to identify the newly inserted device unit’s name. This methods can be modified to fit the requirements of an existent application, or to build a new one that can use this functionality en new and creative ways (such as automated backups of the data on our USB Keys when inserting them and viceversa).

//It IS mandatory to include this reference
using System.Runtime.InteropServices;

//
//   You can add any code you want…
//   HERE …..
//

//Data structure that stores the connection management
[StructLayout(LayoutKind.Sequential)]
public struct DEV_BROADCAST_VOLUME
{
  public int dbcv_size;
  public int dbcv_devicetype;
  public int dbcv_reserved;
  public int dbcv_unitmask;
}

//Method to overwrite that manages the arrival of new storage units
protected override void WndProc(ref Message m)
{
  //This definitions are stored in “dbt.h” and “winuser.h”
  // There has been a change in the devices
  const int WM_DEVICECHANGE = 0×0219;
  // System detects a new device
  const int DBT_DEVICEARRIVAL = 0×8000;
  // Device removal request
  const int DBT_DEVICEQUERYREMOVE = 0×8001;
  // Device removal failed
  const int DBT_DEVICEQUERYREMOVEFAILED = 0×8002;
  // Device removal is pending
  const int DBT_DEVICEREMOVEPENDING = 0×8003;
  // The device has been succesfully removed from the system
  const int DBT_DEVICEREMOVECOMPLETE = 0×8004;
  // Logical Volume (A disk has been inserted, such a usb key or external HDD)
  const int DBT_DEVTYP_VOLUME = 0×00000002;
  switch (m.Msg)
  {
   //If system devices change…
   case WM_DEVICECHANGE:
    switch (m.WParam.ToInt32())
    {
     //If there is a new device…
     case DBT_DEVICEARRIVAL:
     {
      int devType = Marshal.ReadInt32(m.LParam, 4);
      //…and is a Logical Volume (A storage device)
      if (devType == DBT_DEVTYP_VOLUME)
      {
       DEV_BROADCAST_VOLUME vol;
       vol = (DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(
m.LParam, typeof(DEV_BROADCAST_VOLUME));
      MessageBox.Show(
        ”A storage device has been inserted, unit: ” +
        UnitName(vol.dbcv_unitmask));
      }
     }
    break;
    case DBT_DEVICEREMOVECOMPLETE:
     MessageBox.Show(”Device removed from system.”);
    break;
   }
   break;
  }
  //After the custom manager, we want to use the default system’s manager
  base.WndProc(ref m);
}

//Method to detect the unit name (”D:”, “F:”, etc)
char UnitName(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;
  //Convert the mask in an array, and search
  //the index for the first occurrenc (the unit’s name)
  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];
}

I’d be really pleased to know if this was useful 🙂

Publicado en C#. Etiquetas: , , , . 4 Comments »

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 »