Nota del autor

Si la entrada que estás leyendo carece de imágenes, no se ve el vídeo que teóricamente lleva incrustado o el código fuente mostrado aparece sin formato, podéis conocer los motivos aquí. Poco a poco iré restableciendo la normalidad en el blog.
Este blog es un archivo de los artículos situados previamente en Lobosoft.es y ha dejado de ser actualizado. Las nuevas entradas pueden encontrarse en www.lobosoft.es. Un saludo,
Lobosoft.

domingo, 15 de junio de 2008

Obtener el número de serie de un dispositivo USB mediante clases WMI

En el ejemplo de ayer veíamos cómo crear un sistema de protección del software frente a copias ilegales del mismo, y nos basábamos para ello en una sencilla clase que consultaba el número de serie de los dispositivos USB. Al no tener acceso inicialmente al código interno de la misma, íbamos recorriendo todas las unidades del sistema, obteniendo su número de serie y comparándolo con el de referencia, que debería ser, claro está, el de la llave USB que habíamos proporcionado con el programa. Su uso era francamente sencillo, y bastaba con llamar al método GetSerialNumber() para obtener el número de serie de la unidad cuya letra pasáramos como argumento al mismo. Un ejemplo simple de su uso en un WinForm podría ser:


[csharp]
private void btnObtenerNumSerie_Click(object sender, EventArgs e)
{
USBDrives usbsn = new USBDrives();
txtSN.Text = usbsn.GetSerialNumber(txtDrive.Text);
}
[/csharp]

Ya advertimos que no se trataba de una aplicación totalmente segura (aunque podría usarse con éxito en pequeños proyectos, o conjuntamente con otras medidas de seguridad),y su función era ver una utilidad práctica para los conceptos que introduciré hoy: una introducción al manejo de dispositivos USB a través de las consultas sobre WMI (Windows Management Instrumentation, la implementación de Microsoft de WBEM, una iniciativa que pretende establecer una serie de estándares para permitir el acceso y compartir información de administración a través de una red empresarial).


La clase USBDrives contiene varios métodos privados, que se encargan fundamentalmente de parsear la información del dispositivo, tanto a la entrada del método GetSerialNumber () , que recibe la letra del dispositivo a examinar, tanto internamente, eliminando diversos caracteres innecesarios en los identificadores y nombres del mismo. El método que lleva todo el peso de la conversión es GetDriveSerialNumber(), cuyo código veremos a continuación:


[csharp]
///
/// Consulta el ManagementObjectSearcher para obtener el número de serie.
///

private void GetDriveSerialNumber()
{
string[] disks;
string drvNumber = string.Empty;
string drvLetter = string.Empty;

ManagementObjectSearcher objSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_LogicalDiskToPartition");
foreach (ManagementObject diskManagement in objSearcher.Get())
{
disks = null;
drvLetter = ParserValue(diskManagement["Dependent"].ToString());
if (drvLetter == this._driveLetter)
{
disks = ParserValue(diskManagement["Antecedent"].ToString()).Split(',');
drvNumber = disks[0].Remove(0, 6).Trim();

ManagementObjectSearcher disks = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
foreach (ManagementObject disk in disks.Get())
if (disk["Name"].ToString() == ("\\\\.\\PHYSICALDRIVE" + drvNumber) & disk["InterfaceType"].ToString() == "USB")
this._serialNumber = ParserSerial(disk["PNPDeviceID"].ToString());
}
}
}
[/csharp]

Para poder usar el código, debemos importar el ensamblado System.Management, que permite el uso de WMI, y su lenguaje de consulta WQL. Veamos qué hace este código.


Tras un bloque de declaración e inicialización de variables, instancia un objeto de la clase ManagementObjectSearcher, que será el encargado de ejecutar una consulta a algunas clases WMI Win32 para obtener las particiones lógicas y las unidades del sistema. Tras esto, iremos recorriéndolas, recuperando el árbol de unidades existentes en el equipo. Establecemos una comparación entre la letra de dispositivo (a partir de su ID correspondiente) y la que nos pasaron para obtener, y examinamos el valor del InterfaceType para saber si se trata de un dispositivo USB. Si en algún momento coincide todo ello, tenemos nuestra unidad localizada. El número de serie se obtiene a partir del PNPDeviceID, eliminando caracteres como "\" y "&".


Tal y como hemos visto, se trata de un método cómodo, elegante y organizado de obtener información de un dispositivo a partir de WMI, en lugar de tener que hacerlo mediante llamadas directas a la API de Windows (librería win32.dll en este caso) o usando código no manejado.


Para saber más:

8 comentarios:

  1. Me "autocomento" para indicaros una URL que contiene código con una funcionalidad similar, pero desarrollado en lenguaje C.

    http://www.intel.com/intelpress/usb/examples/DUSBVC.PDF

    Ciertamente, es menos elegante que el código en C#, pero es igualmente válido, y como material de referencia es interesante.

    Saludos.

    ResponderEliminar
  2. hola

    Oye muy buen articulo eee felicidades me gustaria ams conocer de esto pero aver primero tendre ke conocer otras cosas antes.

    ResponderEliminar
  3. Muy buenas, Marco.

    Muchas gracias por tu interés en el artículo. Tal vez te interesen un par de recursos que habré mencionado alguna vez en el blog:

    La descarga del Scriptomatic 2.0, de Microsoft:

    http://www.microsoft.com/technet/scriptcenter/tools/scripto2.mspx

    y de PowerShell Scriptomatic:

    http://www.microsoft.com/technet/scriptcenter/tools/psomatic.mspx

    Un saludo.

    ResponderEliminar
  4. Tengo varias memorias usb de la misma marca y necesito diferenciarlas pero al obtener el PNPDeviceID siempre es el mismo.
    Hay forma de saber el id unico por memoria

    ResponderEliminar
  5. Buenas Camilo.

    Añado un artículo interesante de un blog a los recursos que comentaba un poco más arriba. Puede que te resuelva el problema, usando en lugar de WMI la API de Win32:

    http://www.emmet-gray.com/Articles/USB_SerialNumbers.htm

    Un cordial saludo.

    ResponderEliminar
  6. disculpa, me marca un error al reonocer cualquier clase de System.Managment, em podrias ayudar por favor.

    gracias espero su comentario

    ResponderEliminar
  7. Buenas.

    Ese espacio de nombres está contenido en la DLL de System.Core, por lo que no debería darte problemas instanciar objetos de las clases que contiene. Revisa que tienes bien incluida la referencia, y que el using está bien incluído. Si ves que sigue sin funcionarte, intenta incluir el bloque de código que te da problemas.

    Un saludo.

    ResponderEliminar
  8. hola,
    he visto tu código pero ParserValue no lo reconoce, estoy haciendo algo mal?

    ResponderEliminar