Cómo detectar que un dispositivo USB ha sido enchufado en C#

Necesitaba conectar automáticamente a un escáner USB siempre que estuviese conectado en el PC. El enfoque más simple habría sido ejecutar un temporizador, que al expirar preguntase por la presencia del dispositivo. Pero esto es ineficiente, a menos que el período de repetición del temporizador sea corto, lo que aumentaría el uso de la CPU. Una mejor solución es la de detectar automáticamente cuando el dispositivo está enchufado. Afortunadamente, eso es fácil de hacer tal y como te demostraré a continuación.

Tendrás que entender C #, y tener un conocimiento básico del procesamiento de mensajes de Windows, y dispositivos USB.

El código de ejemplo detecta cuando se conecta un dispositivo con un VID específico (Id. De proveedor) y PID (ID de proceso) y, a continuación, se reinicia.

El primer paso es interceptar el mensaje Windows WM_DEVICECHANGE que se envía siempre que cambia la configuración del dispositivo, por ejemplo, cuando se conecta un dispositivo USB. Esto se realiza de forma sencilla conectando un procedimiento de Windows a una ventana existente de la siguiente manera:

var helper = new System.Windows.Interop.WindowInteropHelper(_hiddenWindow); 
IntPtr hWnd = helper.Handle; 
System.Windows.Interop.HwndSource src = System.Windows.Interop.HwndSource.FromHwnd(hWnd); 
src.AddHook(WndProc);

La variable _hiddenWindow es una instancia de la clase Systems.Windows.Window.

El método WndProc procesa los mensajes de la ventana:

private const int WM_DEVICECHANGE = 537; 
private System.Threading.Tasks.Task _restartDeviceTask = null;

private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    if (msg == WM_DEVICECHANGE)
    {
        if (_configData.AutomaticStartup && 
        (Status == ReaderStatus.Stopped) && (_restartDeviceTask == null))
        {
            // This must be done asynchronously otherwise it screws the message pump

            _restartDeviceTask = new System.Threading.Tasks.Task((Action)delegate 
            { 
                RestartUSBDevice(); 
                _restartDeviceTask = null; 
            });
            _restartDeviceTask.Start();
        }
    }

    handled = false; 
    return 0;
}

Al recibir un mensaje WM_DEVICECHANGE, el método comprueba si necesitamos reiniciar nuestro dispositivo. Si es así, crea una nueva tarea para invocar el método RestartUSBDevice. La tarea de fondo garantiza que el método no bloquee la ejecución del pump de mensajes de Windows, que colgaría nuestra aplicación.

Esta línea simplemente funciona si necesitamos iniciar nuestro dispositivo, y nos aseguramos de que una tarea no se esté ejecutando:

if (_configData.AutomaticStartup && 
(Status == ReaderStatus.Stopped) && (_restartDeviceTask == null))

El identificador de la tarea lo establecemos a null después de reiniciar el dispositivo.

El método RestartUSBDevice busca dispositivos USB enchufados con nuestro ID de proveedor y nuestro ID de proceso y si encuentra uno, lo inicia:

private void RestartUSBDevice()
{
     if (Status != ReaderStatus.Stopped)
     {
         return;
     }
            
     const string VID_OURCOMPANY = "VID_0DB5";
     using (var searcher = new ManagementObjectSearcher_
           (@"SELECT * FROM Win32_PnPEntity WHERE DeviceID LIKE '%" + VID_OURCOMPANY + "%'"))
     {
         const string PID_MERCURY_READER = "PID_3001";
         const string PID_JUPITER_READER = "PID_2003";

         try
         {
             ManagementObjectCollection deviceCollection = searcher.Get();
             foreach (var device in deviceCollection)
             {
                 string deviceID = (string)device.GetPropertyValue("DeviceID");
                 if (
                         deviceID.Contains(PID_MERCURY_READER) ||
                         deviceID.Contains(PID_JUPITER_READER)
                    )
                 {
                     StartUSBDevice();
                     break;
                 }
             }
         }
         catch (Exception)
         {
         }
     }
}

Ten en cuenta que el código utiliza constantes de cadena para cada VID y PID en lugar de utilizar "PID_3001" y "PID_2003" directamente. Esto es para hacer que el código sea más legible, ya que nos indica el significado de cada cadena.

Fuente: Leif Simon Goodwin

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP