2009-04-23 17 views
10

Una vez estaba buscando el número de serie de HDD sin utilizar WMI, y lo encontré. The code I found and posted on StackOverFlow.com funciona muy bien en Windows de 32 bits, tanto XP como Vista. El problema solo comienza cuando trato de obtener el número de serie en los sistemas operativos de 64 bits (Vista Ultimate 64, específicamente). El código devuelve String.Empty, o un Space todo el tiempo.Obtenga el número de serie de HDD (y NO de volumen) en Vista Ultimate 64 bit

¿Alguien tiene una idea de cómo solucionar esto?

EDIT:

que utilizan las herramientas de Dave Cluderay sugirió, con resultados interesantes:

Aquí está la salida de DiskId32, en Windows XP SP2 de 32 bits:

To get all details use "diskid32 /d" 
Trying to read the drive IDs using physical access with admin rights 
Drive 0 - Primary Controller - - Master drive 
Drive Model Number________________: [MAXTOR STM3160215AS] 
Drive Serial Number_______________: [   6RA26XK3] 
Drive Controller Revision Number__: [3.AAD] 
Controller Buffer Size on Drive___: 2097152 bytes 
Drive Type________________________: Fixed 
Drive Size________________________: 160041885696 bytes 

Trying to read the drive IDs using the SCSI back door 

Drive 4 - Tertiary Controller - - Master drive 
Drive Model Number________________: [MAXTOR STM3160215AS] 
Drive Serial Number_______________: [   6RA26XK3] 
Drive Controller Revision Number__: [3.AAD] 
Controller Buffer Size on Drive___: 2097152 bytes 
Drive Type________________________: Fixed 
Drive Size________________________: 160041885696 bytes 

Trying to read the drive IDs using physical access with zero rights 

**** STORAGE_DEVICE_DESCRIPTOR for drive 0 **** 
Vendor Id = [] 
Product Id = [MAXTOR STM3160215AS] 
Product Revision = [3.AAD] 
Serial Number = [] 

**** DISK_GEOMETRY_EX for drive 0 **** 
Disk is fixed 
DiskSize = 160041885696 

Trying to read the drive IDs using Smart 

Drive 0 - Primary Controller - - Master drive 

Drive Model Number________________: [MAXTOR STM3160215AS] 
Drive Serial Number_______________: [   6RA26XK3] 
Drive Controller Revision Number__: [3.AAD] 
Controller Buffer Size on Drive___: 2097152 bytes 
Drive Type________________________: Fixed 
Drive Size________________________: 160041885696 bytes 

Hard Drive Serial Number__________:    6RA26XK3 

Hard Drive Model Number___________: MAXTOR STM3160215AS 

Y DiskId32 ejecutar en Windows Vista Ultimate de 64 bits:

To get all details use "diskid32 /d" 

Trying to read the drive IDs using physical access with admin rights 

Trying to read the drive IDs using the SCSI back door 

Trying to read the drive IDs using physical access with zero rights 

**** STORAGE_DEVICE_DESCRIPTOR for drive 0 **** 
Vendor Id = [MAXTOR S] 
Product Id = [TM3160215AS] 
Product Revision = [3.AA] 
Serial Number = [] 

**** DISK_GEOMETRY_EX for drive 0 **** 
Disk is fixed 
DiskSize = 160041885696 

Trying to read the drive IDs using Smart 

Hard Drive Serial Number__________: 

Hard Drive Model Number___________: 

Observe cuánto menos la información está en Vista y cómo no se devuelve el número de serie. Además, la otra herramienta, EnumDisk, se refiere a mis discos duros en Vista como "SCSI" en lugar de "ATA" en Windows XP.

¿Alguna idea?

EDIT 2:

estoy publicando los resultados de EnumDisks:

En Windows XP SP2 de 32 bits:

Properties for Device 1 

Device ID: IDE\DiskMAXTOR_STM3160215AS_____________________3.AAD___ 

Adapter Properties 
------------------ 
Bus Type  : ATA 
Max. Tr. Length: 0x20000 
Max. Phy. Pages: 0xffffffff 
Alignment Mask : 0x1 

Device Properties 
----------------- 
Device Type  : Direct Access Device (0x0) 
Removable Media : No 
Product ID  : MAXTOR STM3160215AS 
Product Revision: 3.AAD 

Inquiry Data from Pass Through 
------------------------------ 
Device Type: Direct Access Device (0x0) 
Vendor ID : MAXTOR S 
Product ID : TM3160215AS 
Product Rev: 3.AA 
Vendor Str : 



*** End of Device List *** 

Y en Vista 64 final:

Properties for Device 1 

Device ID: SCSI\DiskMAXTOR_STM3160215AS_____3.AA 

Adapter Properties 
------------------ 
Bus Type  : FIBRE 
Max. Tr. Length: 0x20000 
Max. Phy. Pages: 0x11 
Alignment Mask : 0x0 

Device Properties 
----------------- 
Device Type  : Direct Access Device (0x0) 
Removable Media : No 
Vendor ID  : MAXTOR S 
Product ID  : TM3160215AS 
Product Revision: 3.AA 

Inquiry Data from Pass Through 
------------------------------ 
Device Type: Direct Access Device (0x0) 
Vendor ID : MAXTOR S 
Product ID : TM3160215AS 
Product Rev: 3.AA 
Vendor Str : 



*** End of Device List *** 
+0

Algunas piezas más de información que pueden ser relevantes: ¿son los dos sistemas operativos en la misma máquina física (es decir, arranque dual) o máquinas separadas? ¿Es una máquina virtual? Cualquier cosa notable sobre los discos, p. (INCURSIÓN, etc.)? –

+0

Ambos sistemas operativos están en la misma máquina física. No se usó ninguna máquina virtual. Nada especial sobre mi disco duro, un típico disco ATA MAXTOR de 160 GB. – TheAgent

+0

Hola de nuevo. Es de suponer que EnumDisk1 tampoco pudo obtener el número de serie en Vista 64? –

Respuesta

-1

Es posible que desee utilizar la API no administrada de Windows para hacer esto:

llama a GetVolumeInformation api con la estructura apropiada y encuentra el campo entero VolumeSerialNumber.

Esta API existe durante años y me funcionaba desde Windows 98. Lamentablemente, no puedo verificarla en x64.

¿Puede ver el número de serie correcto con otras herramientas de Windows? Por cierto: '0' es un número de serie válido. Esto podría suceder si la imagen del disco se restauró desde la copia de seguridad o algo así.

+0

Gracias por su respuesta, pero no estoy buscando el número de serie del Volumen, sino el número de serie del disco duro, el número de modelo, etc. – TheAgent

2

This code debería darle el número de serie del disco duro. Es similar (ReadPhysicalDriveInNTWithAdminRights) al código al que está vinculado pero con varias funciones adicionales.

+2

Está trabajando en VB.NET no C++ – joshperry

2

es necesario asegurarse de que sus P/Invoke definiciones son de 64 bits de usar. Alternativamente, intente configurar la CPU objetivo de los proyectos en su solución a 32 bits. Se puede encontrar más información sobre P/Invoke y 64-bit here.

EDIT:

El siguiente código reescrito podría funcionar mejor para usted - básicamente me he puesto en orden la relación P/Invoke definiciones y añadió una mejor gestión de errores. El código hace dos intentos para obtener el número de serie. El primero usa IOCTL_STORAGE_QUERY_PROPERTY y el segundo usa SMART_RCV_DRIVE_DATA.

' PhysicalDrive.vb 

Option Strict On 
Option Explicit On 

Imports System.Runtime.InteropServices 
Imports System.Text 
Imports System.ComponentModel 
Imports Microsoft.Win32.SafeHandles 

Public Class PhysicalDrive 

#Region "Win32 Definitions" 
    <StructLayout(LayoutKind.Sequential)> _ 
    Private Structure IDEREGS 
     Public bFeaturesReg As Byte 
     Public bSectorCountReg As Byte 
     Public bSectorNumberReg As Byte 
     Public bCylLowReg As Byte 
     Public bCylHighReg As Byte 
     Public bDriveHeadReg As Byte 
     Public bCommandReg As Byte 
     Public bReserved As Byte 
    End Structure 

    <StructLayout(LayoutKind.Sequential)> _ 
    Private Structure SENDCMDINPARAMS 
     Public cBufferSize As Int32 
     Public irDriveRegs As IDEREGS 
     Public bDriveNumber As Byte 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=3)> _ 
     Public bReserved As Byte() 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=4)> _ 
     Public dwReserved As Int32() 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)> _ 
     Public bBuffer As Byte() 
    End Structure 

    <StructLayout(LayoutKind.Sequential)> _ 
    Private Structure DRIVERSTATUS 
     Public bDriverError As Byte 
     Public bIDEError As Byte 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> _ 
     Public bReserved As Byte() 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> _ 
     Public dwReserved As Int32() 
    End Structure 

    <StructLayout(LayoutKind.Sequential)> _ 
    Private Structure SENDCMDOUTPARAMS 
     Public cBufferSize As Int32 
     Public DriverStatus As DRIVERSTATUS 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=IDENTIFY_BUFFER_SIZE)> _ 
     Public bBuffer As Byte() 
    End Structure 

    <StructLayout(LayoutKind.Sequential)> _ 
    Private Structure GETVERSIONOUTPARAMS 
     Public bVersion As Byte 
     Public bRevision As Byte 
     Public bReserved As Byte 
     Public bIDEDeviceMap As Byte 
     Public fCapabilities As Int32 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=4)> _ 
     Public dwReserved As Int32() 
    End Structure 

    <StructLayout(LayoutKind.Sequential)> _ 
    Private Structure STORAGE_PROPERTY_QUERY 
     Public PropertyId As Int32 
     Public QueryType As Int32 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)> _ 
     Public AdditionalParameters As Byte() 
    End Structure 

    <StructLayout(LayoutKind.Sequential)> _ 
    Private Structure STORAGE_DEVICE_DESCRIPTOR 
     Public Version As Int32 
     Public Size As Int32 
     Public DeviceType As Byte 
     Public DeviceTypeModifier As Byte 
     Public RemovableMedia As Byte 
     Public CommandQueueing As Byte 
     Public VendorIdOffset As Int32 
     Public ProductIdOffset As Int32 
     Public ProductRevisionOffset As Int32 
     Public SerialNumberOffset As Int32 
     Public BusType As Byte 
     Public RawPropertiesLength As Int32 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=10240)> _ 
     Public RawDeviceProperties As Byte() 
    End Structure 

    <DllImport("kernel32.dll", SetLastError:=True)> _ 
    Private Shared Function CreateFile(ByVal lpFileName As String, ByVal dwDesiredAccess As Int32, ByVal dwShareMode As Int32, ByVal lpSecurityAttributes As IntPtr, ByVal dwCreationDisposition As Int32, ByVal dwFlagsAndAttributes As Int32, ByVal hTemplateFile As IntPtr) As SafeFileHandle 
    End Function 

    <DllImport("kernel32.dll", SetLastError:=True)> _ 
    Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, <[In](), Out()> ByRef lpInBuffer As SENDCMDINPARAMS, ByVal nInBufferSize As Int32, <[In](), Out()> ByRef lpOutBuffer As SENDCMDOUTPARAMS, ByVal nOutBufferSize As Int32, ByRef lpBytesReturned As Int32, ByVal lpOverlapped As Int32) As Int32 
    End Function 

    <DllImport("kernel32.dll", SetLastError:=True)> _ 
    Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, ByVal lpInBuffer As IntPtr, ByVal nInBufferSize As Int32, <[In](), Out()> ByRef lpOutBuffer As GETVERSIONOUTPARAMS, ByVal nOutBufferSize As Int32, ByRef lpBytesReturned As Int32, ByVal lpOverlapped As Int32) As Int32 
    End Function 

    <DllImport("kernel32.dll", SetLastError:=True)> _ 
    Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, <[In](), Out()> ByRef lpInBuffer As STORAGE_PROPERTY_QUERY, ByVal nInBufferSize As Int32, <[In](), Out()> ByRef lpOutBuffer As STORAGE_DEVICE_DESCRIPTOR, ByVal nOutBufferSize As Int32, ByRef lpBytesReturned As Int32, ByVal lpOverlapped As Int32) As Int32 
    End Function 

    Private Const OPEN_EXISTING As Int32 = 3 
    Private Const GENERIC_READ As Int32 = &H80000000 
    Private Const GENERIC_WRITE As Int32 = &H40000000 
    Private Const FILE_SHARE_READ As Int32 = &H1 
    Private Const FILE_SHARE_WRITE As Int32 = &H2 
    Private Const FILE_SHARE_DELETE As Int32 = &H4 
    Private Const SMART_GET_VERSION As Int32 = &H74080 
    Private Const SMART_RCV_DRIVE_DATA As Int32 = &H7C088 
    Private Const ID_CMD As Int32 = &HEC 
    Private Const IDENTIFY_BUFFER_SIZE As Int32 = 512 
    Private Const CAP_SMART_CMD As Int32 = &H4 
    Private Const IOCTL_STORAGE_QUERY_PROPERTY As Int32 = &H2D1400 
    Private Const PropertyStandardQuery As Int32 = 0 
    Private Const StorageDeviceProperty As Int32 = 0 
#End Region 

    Public Shared Function GetSerialNumber(ByVal diskNumber As Integer) As String 
     Dim result As String = GetSerialNumberUsingStorageQuery(diskNumber) 
     If String.IsNullOrEmpty(result) Then 
      result = GetSerialNumberUsingSmart(diskNumber) 
     End If 
     Return result 
    End Function 

    Public Shared Function GetSerialNumberUsingStorageQuery(ByVal diskNumber As Integer) As String 
     Using hDisk As SafeFileHandle = OpenDisk(diskNumber) 
      Dim iBytesReturned As Int32 
      Dim spq As New STORAGE_PROPERTY_QUERY() 
      Dim sdd As New STORAGE_DEVICE_DESCRIPTOR() 
      spq.PropertyId = StorageDeviceProperty 
      spq.QueryType = PropertyStandardQuery 

      If DeviceIoControl(hDisk, IOCTL_STORAGE_QUERY_PROPERTY, spq, Marshal.SizeOf(spq), sdd, Marshal.SizeOf(sdd), iBytesReturned, 0) = 0 Then 
       Throw CreateWin32Exception(Marshal.GetLastWin32Error(), "DeviceIoControl(IOCTL_STORAGE_QUERY_PROPERTY)") 
      End If 

      Dim result As New StringBuilder() 
      If sdd.SerialNumberOffset > 0 Then 
       Dim rawDevicePropertiesOffset As Integer = Marshal.SizeOf(sdd) - sdd.RawDeviceProperties.Length 
       Dim pos As Integer = sdd.SerialNumberOffset - rawDevicePropertiesOffset 
       While pos < iBytesReturned And sdd.RawDeviceProperties(pos) <> 0 
        result.Append(Encoding.ASCII.GetString(sdd.RawDeviceProperties, pos, 1)) 
        pos += 1 
       End While 
      End If 
      Return result.ToString() 
     End Using 
    End Function 

    Public Shared Function GetSerialNumberUsingSmart(ByVal diskNumber As Integer) As String 
     Using hDisk As SafeFileHandle = OpenDisk(diskNumber) 
      If IsSmartSupported(hDisk) Then 
       Dim iBytesReturned As Int32 
       Dim sci As New SENDCMDINPARAMS 
       Dim sco As New SENDCMDOUTPARAMS 
       sci.irDriveRegs.bCommandReg = ID_CMD 
       sci.bDriveNumber = CByte(diskNumber) 
       sci.cBufferSize = IDENTIFY_BUFFER_SIZE 
       If DeviceIoControl(hDisk, SMART_RCV_DRIVE_DATA, sci, Marshal.SizeOf(sci), sco, Marshal.SizeOf(sco), iBytesReturned, 0) = 0 Then 
        Throw CreateWin32Exception(Marshal.GetLastWin32Error(), "DeviceIoControl(SMART_RCV_DRIVE_DATA)") 
       End If 
       Dim result As New StringBuilder() 
       For index As Integer = 20 To 39 Step 2 
        result.Append(Encoding.ASCII.GetString(sco.bBuffer, index + 1, 1)) 
        result.Append(Encoding.ASCII.GetString(sco.bBuffer, index, 1)) 
       Next 
       Return result.ToString() 
      Else 
       Return String.Empty 
      End If 
     End Using 
    End Function 

    Private Shared Function CreateWin32Exception(ByVal errorCode As Int32, ByVal context As String) As Win32Exception 
     Dim win32Exception As New Win32Exception(errorCode) 
     win32Exception.Data("Context") = context 
     Return win32Exception 
    End Function 

    Private Shared Function OpenDisk(ByVal diskNumber As Integer) As SafeFileHandle 
     Dim hDevice As SafeFileHandle = CreateFile(String.Format("\\.\PhysicalDrive{0}", diskNumber), GENERIC_READ Or GENERIC_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE Or FILE_SHARE_DELETE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero) 
     If (Not hDevice.IsInvalid) Then 
      Return hDevice 
     Else 
      Throw CreateWin32Exception(Marshal.GetLastWin32Error(), "CreateFile") 
     End If 
    End Function 

    Private Shared Function IsSmartSupported(ByVal hDisk As SafeFileHandle) As Boolean 
     Dim iBytesReturned As Int32 
     Dim gvo As New GETVERSIONOUTPARAMS 
     If DeviceIoControl(hDisk, SMART_GET_VERSION, IntPtr.Zero, 0, gvo, Marshal.SizeOf(gvo), iBytesReturned, 0) = 0 Then 
      Return False 
     End If 
     Return (gvo.fCapabilities And CAP_SMART_CMD) > 0 
    End Function 

End Class 

Este es el código para llamarlo:

' MainModule.vb 

Module MainModule 

    Sub Main() 
     Console.WriteLine("{0}-bit runtime.", IntPtr.Size * 8) 
     For drive As Integer = 0 To 4 
      Try 
       Console.WriteLine("Drive {0} - serial number: [{1}]", drive, PhysicalDrive.GetSerialNumber(drive)) 
      Catch ex As Exception 
       If ex.Data("Context") IsNot Nothing Then Console.Error.Write("{0} failed: ", ex.Data("Context")) 
       Console.Error.WriteLine(ex.Message) 
      End Try 
     Next 
    End Sub 

End Module 

sólo tengo una máquina de 64 bits para poner a prueba en contra, pero este código no trabajar en él.

+0

¿Vista de 64 bits? Porque probé este código en mi Vista Ultimate de 64 bits y todavía devuelve una cadena vacía. (Utilizo String.Trim en el resultado ya que hay caracteres de espacio adicionales devueltos antes del número de serie real en sistemas operativos de 32 bits). No parece que el código .NET haya podido devolver el SN. ¿Cómo puedo contactar a un programador de MS para discutir esto? – TheAgent

+0

Probé este código en un servidor Windows 7 RC x64 HP ML-115. Hay algunas muestras de C++ disponibles, que puede descargar en forma ejecutable compilada. Las muestras en las que estoy pensando en particular son diskid32 (http://www.winsim.com/diskid32/diskid32.html) y EnumDisk1 (http://support.microsoft.com/kb/264203). Te sugiero que intentes ejecutar los ejecutables en tu máquina de 64 bits. Si pueden recuperar el número de serie, tal vez yo u otra persona pueda ayudarlo a implementar las mismas técnicas en su propio código de VB.net. –

0

modificado a partir del código here:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Management; 
using System.Text; 

namespace Console_DiskDrive 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      String query = "SELECT * FROM Win32_DiskDrive"; 

      foreach (ManagementObject item in new ManagementObjectSearcher(query).Get()) 
      { 
       string serialNumber = Convert.ToString(item["SerialNumber"]); 

       Console.WriteLine(serialNumber); 
      } 

      Console.ReadLine(); 
     } 
    } 
} 

En mi sistema con Vista Home Premium x64, me da una cadena hexadecimal de 40 caracteres que estoy suponiendo que es mi número de serie. Abriré la caja y confirmaré más tarde, pero pruébalo y veré si es lo que estás buscando.

+0

Lo he probado antes. El WMI para obtener el número de serie de HDD tiene algunos problemas, como no trabajar en algunas versiones de Windows y fallas intermitentes, por lo que esa no sería la respuesta. – TheAgent

+0

En mi computadora portátil (DELL Latitude E6400 con XP), acceder a la propiedad "SerialNumber" arroja la excepción "no encontrado". – newman

8

Este código hace tres intentos de obtener el número de serie:

  1. Usando IOCTL_STORAGE_QUERY_PROPERTY.
  2. Usando SMART_RCV_DRIVE_DATA.
  3. Usando IOCTL_SCSI_PASS_THROUGH.

Este código funciona para mí en 64 bits:

' PhysicalDrive.vb 

Option Strict On 
Option Explicit On 

Imports System.Runtime.InteropServices 
Imports System.Text 
Imports System.ComponentModel 
Imports Microsoft.Win32.SafeHandles 

Public Class PhysicalDrive 

#Region "Win32 Definitions" 
    <StructLayout(LayoutKind.Sequential)> _ 
    Private Structure IDEREGS 
     Public bFeaturesReg As Byte 
     Public bSectorCountReg As Byte 
     Public bSectorNumberReg As Byte 
     Public bCylLowReg As Byte 
     Public bCylHighReg As Byte 
     Public bDriveHeadReg As Byte 
     Public bCommandReg As Byte 
     Public bReserved As Byte 
    End Structure 

    <StructLayout(LayoutKind.Sequential)> _ 
    Private Structure SENDCMDINPARAMS 
     Public cBufferSize As Int32 
     Public irDriveRegs As IDEREGS 
     Public bDriveNumber As Byte 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=3)> _ 
     Public bReserved As Byte() 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=4)> _ 
     Public dwReserved As Int32() 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)> _ 
     Public bBuffer As Byte() 
    End Structure 

    <StructLayout(LayoutKind.Sequential)> _ 
    Private Structure DRIVERSTATUS 
     Public bDriverError As Byte 
     Public bIDEError As Byte 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> _ 
     Public bReserved As Byte() 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> _ 
     Public dwReserved As Int32() 
    End Structure 

    <StructLayout(LayoutKind.Sequential)> _ 
    Private Structure SENDCMDOUTPARAMS 
     Public cBufferSize As Int32 
     Public DriverStatus As DRIVERSTATUS 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=IDENTIFY_BUFFER_SIZE)> _ 
     Public bBuffer As Byte() 
    End Structure 

    <StructLayout(LayoutKind.Sequential)> _ 
    Private Structure GETVERSIONINPARAMS 
     Public bVersion As Byte 
     Public bRevision As Byte 
     Public bReserved As Byte 
     Public bIDEDeviceMap As Byte 
     Public fCapabilities As Int32 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=4)> _ 
     Public dwReserved As Int32() 
    End Structure 

    <StructLayout(LayoutKind.Sequential)> _ 
    Private Structure STORAGE_PROPERTY_QUERY 
     Public PropertyId As Int32 
     Public QueryType As Int32 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)> _ 
     Public AdditionalParameters As Byte() 
    End Structure 

    <StructLayout(LayoutKind.Sequential)> _ 
    Private Structure STORAGE_DEVICE_DESCRIPTOR 
     Public Version As Int32 
     Public Size As Int32 
     Public DeviceType As Byte 
     Public DeviceTypeModifier As Byte 
     Public RemovableMedia As Byte 
     Public CommandQueueing As Byte 
     Public VendorIdOffset As Int32 
     Public ProductIdOffset As Int32 
     Public ProductRevisionOffset As Int32 
     Public SerialNumberOffset As Int32 
     Public BusType As Byte 
     Public RawPropertiesLength As Int32 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=10240)> _ 
     Public RawDeviceProperties As Byte() 
    End Structure 

    <StructLayout(LayoutKind.Sequential)> _ 
    Private Structure SCSI_PASS_THROUGH 
     Public Length As Int16 
     Public ScsiStatus As Byte 
     Public PathId As Byte 
     Public TargetId As Byte 
     Public Lun As Byte 
     Public CdbLength As Byte 
     Public SenseInfoLength As Byte 
     Public DataIn As Byte 
     Public DataTransferLength As Int32 
     Public TimeOutValue As Int32 
     Public DataBufferOffset As IntPtr 
     Public SenseInfoOffset As Int32 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=16)> _ 
     Public Cdb As Byte() 
    End Structure 

    <StructLayout(LayoutKind.Sequential)> _ 
    Private Structure SCSI_PASS_THROUGH_WITH_BUFFER 
     Public Spt As SCSI_PASS_THROUGH 
     Public Filler As Int32 
     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=64)> _ 
     Public Buffer As Byte() 
    End Structure 

    <DllImport("kernel32.dll", SetLastError:=True)> _ 
    Private Shared Function CreateFile(ByVal lpFileName As String, ByVal dwDesiredAccess As Int32, ByVal dwShareMode As Int32, ByVal lpSecurityAttributes As IntPtr, ByVal dwCreationDisposition As Int32, ByVal dwFlagsAndAttributes As Int32, ByVal hTemplateFile As IntPtr) As SafeFileHandle 
    End Function 

    <DllImport("kernel32.dll", SetLastError:=True)> _ 
    Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, <[In]()> ByRef lpInBuffer As SENDCMDINPARAMS, ByVal nInBufferSize As Int32, <[In](), Out()> ByRef lpOutBuffer As SENDCMDOUTPARAMS, ByVal nOutBufferSize As Int32, ByRef lpBytesReturned As Int32, ByVal lpOverlapped As Int32) As Int32 
    End Function 

    <DllImport("kernel32.dll", SetLastError:=True)> _ 
    Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, ByVal lpInBuffer As IntPtr, ByVal nInBufferSize As Int32, <[In](), Out()> ByRef lpOutBuffer As GETVERSIONINPARAMS, ByVal nOutBufferSize As Int32, ByRef lpBytesReturned As Int32, ByVal lpOverlapped As Int32) As Int32 
    End Function 

    <DllImport("kernel32.dll", SetLastError:=True)> _ 
    Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, <[In]()> ByRef lpInBuffer As STORAGE_PROPERTY_QUERY, ByVal nInBufferSize As Int32, <[In](), Out()> ByRef lpOutBuffer As STORAGE_DEVICE_DESCRIPTOR, ByVal nOutBufferSize As Int32, ByRef lpBytesReturned As Int32, ByVal lpOverlapped As Int32) As Int32 
    End Function 

    <DllImport("kernel32.dll", SetLastError:=True)> _ 
    Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, <[In]()> ByRef lpInBuffer As SCSI_PASS_THROUGH_WITH_BUFFER, ByVal nInBufferSize As Int32, <[In](), Out()> ByRef lpOutBuffer As SCSI_PASS_THROUGH_WITH_BUFFER, ByVal nOutBufferSize As Int32, ByRef lpBytesReturned As Int32, ByVal lpOverlapped As Int32) As Int32 
    End Function 

    Private Const OPEN_EXISTING As Int32 = 3 
    Private Const GENERIC_READ As Int32 = &H80000000 
    Private Const GENERIC_WRITE As Int32 = &H40000000 
    Private Const FILE_SHARE_READ As Int32 = &H1 
    Private Const FILE_SHARE_WRITE As Int32 = &H2 
    Private Const FILE_SHARE_DELETE As Int32 = &H4 
    Private Const SMART_GET_VERSION As Int32 = &H74080 
    Private Const SMART_RCV_DRIVE_DATA As Int32 = &H7C088 
    Private Const ID_CMD As Int32 = &HEC 
    Private Const IDENTIFY_BUFFER_SIZE As Int32 = 512 
    Private Const CAP_SMART_CMD As Int32 = &H4 
    Private Const IOCTL_STORAGE_QUERY_PROPERTY As Int32 = &H2D1400 
    Private Const IOCTL_SCSI_PASS_THROUGH As Int32 = &H4D004 
    Private Const SCSI_IOCTL_DATA_IN As Int32 = &H1 
    Private Const PropertyStandardQuery As Int32 = 0 
    Private Const StorageDeviceProperty As Int32 = 0 
    Private Const ERROR_INVALID_FUNCTION As Int32 = &H1 
#End Region 

    Public Shared Function GetSerialNumberUsingStorageQuery(ByVal diskNumber As Integer) As String 
     Using hDisk As SafeFileHandle = OpenDisk(diskNumber) 
      Dim iBytesReturned As Int32 
      Dim spq As New STORAGE_PROPERTY_QUERY() 
      Dim sdd As New STORAGE_DEVICE_DESCRIPTOR() 
      spq.PropertyId = StorageDeviceProperty 
      spq.QueryType = PropertyStandardQuery 

      If DeviceIoControl(hDisk, IOCTL_STORAGE_QUERY_PROPERTY, spq, Marshal.SizeOf(spq), sdd, Marshal.SizeOf(sdd), iBytesReturned, 0) = 0 Then 
       Throw CreateWin32Exception(Marshal.GetLastWin32Error(), "DeviceIoControl(IOCTL_STORAGE_QUERY_PROPERTY)") 
      End If 

      Dim result As New StringBuilder() 
      If sdd.SerialNumberOffset > 0 Then 
       Dim rawDevicePropertiesOffset As Integer = Marshal.SizeOf(sdd) - sdd.RawDeviceProperties.Length 
       Dim pos As Integer = sdd.SerialNumberOffset - rawDevicePropertiesOffset 
       While pos < iBytesReturned And sdd.RawDeviceProperties(pos) <> 0 
        result.Append(Encoding.ASCII.GetString(sdd.RawDeviceProperties, pos, 1)) 
        pos += 1 
       End While 
      End If 
      Return result.ToString().Trim() 
     End Using 
    End Function 

    Public Shared Function GetSerialNumberUsingSmart(ByVal diskNumber As Integer) As String 
     Using hDisk As SafeFileHandle = OpenDisk(diskNumber) 
      If IsSmartSupported(hDisk) Then 
       Dim iBytesReturned As Int32 
       Dim sci As New SENDCMDINPARAMS 
       Dim sco As New SENDCMDOUTPARAMS 
       sci.irDriveRegs.bCommandReg = ID_CMD 
       sci.bDriveNumber = CByte(diskNumber) 
       sci.cBufferSize = IDENTIFY_BUFFER_SIZE 
       If DeviceIoControl(hDisk, SMART_RCV_DRIVE_DATA, sci, Marshal.SizeOf(sci), sco, Marshal.SizeOf(sco), iBytesReturned, 0) = 0 Then 
        Throw CreateWin32Exception(Marshal.GetLastWin32Error(), "DeviceIoControl(SMART_RCV_DRIVE_DATA)") 
       End If 
       Dim result As New StringBuilder() 
       For index As Integer = 20 To 39 Step 2 
        result.Append(Encoding.ASCII.GetString(sco.bBuffer, index + 1, 1)) 
        result.Append(Encoding.ASCII.GetString(sco.bBuffer, index, 1)) 
       Next 
       Return result.ToString().Trim() 
      Else 
       Return String.Empty 
      End If 
     End Using 
    End Function 

    Public Shared Function GetSerialNumberUsingScsiPassThrough(ByVal diskNumber As Integer) As String 
     Using hDisk As SafeFileHandle = OpenDisk(diskNumber) 
      Dim iBytesReturned As Int32 
      Dim spt As New SCSI_PASS_THROUGH_WITH_BUFFER 
      spt.Spt.Length = CShort(Marshal.SizeOf(spt.Spt)) 
      spt.Spt.CdbLength = 16 
      spt.Spt.DataIn = SCSI_IOCTL_DATA_IN 
      spt.Spt.DataTransferLength = 64 
      spt.Spt.DataBufferOffset = New IntPtr(Marshal.SizeOf(spt) - 64) 
      spt.Spt.TimeOutValue = 60 
      Dim cdb(15) As Byte 
      cdb(0) = &H12 ' INQUIRY 
      cdb(1) = &H1 ' EVPD bit 
      cdb(2) = &H80 ' Page code (indicates Serial Number) 
      cdb(4) = 64 ' Allocation length 
      spt.Spt.Cdb = cdb 
      If DeviceIoControl(hDisk, IOCTL_SCSI_PASS_THROUGH, spt, Marshal.SizeOf(spt), spt, Marshal.SizeOf(spt), iBytesReturned, 0) = 0 Then 
       Dim iErrorCode As Int32 = Marshal.GetLastWin32Error() 
       If iErrorCode <> ERROR_INVALID_FUNCTION Then 
        Throw CreateWin32Exception(iErrorCode, "DeviceIoControl(IOCTL_SCSI_PASS_THROUGH)") 
       End If 
      End If 
      Dim result As New StringBuilder() 
      Dim pos As Integer = IntPtr.Size 
      While pos < spt.Spt.DataTransferLength And spt.Buffer(pos) <> 0 
       result.Append(Encoding.ASCII.GetString(spt.Buffer, pos, 1)) 
       pos += 1 
      End While 
      Return result.ToString().Trim() 
     End Using 
    End Function 

    Private Shared Function CreateWin32Exception(ByVal errorCode As Int32, ByVal context As String) As Win32Exception 
     Dim win32Exception As New Win32Exception(errorCode) 
     win32Exception.Data("Context") = context 
     Return win32Exception 
    End Function 

    Private Shared Function OpenDisk(ByVal diskNumber As Integer) As SafeFileHandle 
     Dim hDevice As SafeFileHandle = CreateFile(String.Format("\\.\PhysicalDrive{0}", diskNumber), GENERIC_READ Or GENERIC_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE Or FILE_SHARE_DELETE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero) 
     If (Not hDevice.IsInvalid) Then 
      Return hDevice 
     Else 
      Throw CreateWin32Exception(Marshal.GetLastWin32Error(), "CreateFile") 
     End If 
    End Function 

    Private Shared Function IsSmartSupported(ByVal hDisk As SafeFileHandle) As Boolean 
     Dim iBytesReturned As Int32 
     Dim gvi As New GETVERSIONINPARAMS 
     If DeviceIoControl(hDisk, SMART_GET_VERSION, IntPtr.Zero, 0, gvi, Marshal.SizeOf(gvi), iBytesReturned, 0) = 0 Then 
      Return False 
     End If 
     Return (gvi.fCapabilities And CAP_SMART_CMD) > 0 
    End Function 

End Class 

Y aquí está el código para llamarlo:

' MainModule.vb 

Module MainModule 

    Sub Main() 
     Console.WriteLine("{0}-bit runtime.", IntPtr.Size * 8) 
     For drive As Integer = 0 To 4 
      Try 
       Console.WriteLine("Drive {0}, SMART:    [{1}]", drive, PhysicalDrive.GetSerialNumberUsingSmart(drive)) 
       Console.WriteLine("Drive {0}, Storage Query:  [{1}]", drive, PhysicalDrive.GetSerialNumberUsingStorageQuery(drive)) 
       Console.WriteLine("Drive {0}, SCSI Pass Through: [{1}]", drive, PhysicalDrive.GetSerialNumberUsingScsiPassThrough(drive)) 
      Catch ex As Exception 
       If ex.Data("Context") IsNot Nothing Then Console.Error.Write("{0} failed: ", ex.Data("Context")) 
       Console.Error.WriteLine(ex.Message) 
      End Try 
     Next 
    End Sub 

End Module 

EDITAR - He cambiado el método principal para mostrar los resultados de cada intento de comparación. Con suerte, esto ilustrará cuán al azar estas técnicas pueden ser.

+0

Hola! Convertí tu código en C#, pero con SCSI PassThrough obtengo un resultado extraño (número de modelo en lugar de número de serie). Publiqué una pregunta aquí sobre eso y me preguntaron si puedes echarle un vistazo porque probablemente sabes lo que está sucediendo: http://stackoverflow.com/questions/16443215/c-native-reading-hdd-serial- using-scsi-passthrough –

+0

Finalmente, un código para leer s/n físico de disco duro bajo xp. thx – nelek

1

consiguió trabajar en Windows 7 64:

 ManagementObjectSearcher mos = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive"); 

     foreach (ManagementObject obj in mos.Get()) { 
      Trace.TraceInformation("Information about disk drive {0}:", obj["Name"]); 
      Trace.Indent(); 
      foreach (PropertyData pd in obj.Properties) 
       Trace.TraceInformation("Name \"{0}\": \"{1}\"", pd.Name, pd.Value); 
      Trace.Unindent(); 

      obj.Properties["SerialNumber"] 
     } 

Probablemente la clase Win32_PhysicalMedia no se sirve en las plataformas de 64 bits.

Incluso Disk32, en este momento, está funcionando (aparte de un error al voltear bytes de número de serie), porque se basa en los mismos conceptos.

+0

Acabo de probar este código en mi máquina (XP, utilizando .NET 4.0) y el acceso a obj.Properties ["SerialiNumber"] arroja la excepción "no encontrado". – newman

+0

Acabo de probar este código en mi máquina (XP, utilizando .NET 4.0) y el acceso a obj.Properties ["SerialiNumber"] lanza la excepción "no encontrado". – newman

+0

En Windows XP consultará Win32_PhysicalMedia. Este código funciona en Windows 7. – Luca

Cuestiones relacionadas