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:
- Clases WMI usadas en el ejemplo, Win32_LogicalDiskToPartition y Win32_DiskDrive.
- Introducción a WMI.
- Conociendo un poco más WMI.
- CMI.
- WQL.
Me "autocomento" para indicaros una URL que contiene código con una funcionalidad similar, pero desarrollado en lenguaje C.
ResponderEliminarhttp://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.
hola
ResponderEliminarOye muy buen articulo eee felicidades me gustaria ams conocer de esto pero aver primero tendre ke conocer otras cosas antes.
Muy buenas, Marco.
ResponderEliminarMuchas 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.
Tengo varias memorias usb de la misma marca y necesito diferenciarlas pero al obtener el PNPDeviceID siempre es el mismo.
ResponderEliminarHay forma de saber el id unico por memoria
Buenas Camilo.
ResponderEliminarAñ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.
disculpa, me marca un error al reonocer cualquier clase de System.Managment, em podrias ayudar por favor.
ResponderEliminargracias espero su comentario
Buenas.
ResponderEliminarEse 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.
hola,
ResponderEliminarhe visto tu código pero ParserValue no lo reconoce, estoy haciendo algo mal?