2009-04-23 8 views
10

Je cherchais à obtenir le numéro de série du disque dur sans utiliser WMI, et je l'ai trouvé. The code I found and posted on StackOverFlow.com fonctionne très bien sur Windows 32 bits, XP et Vista. Le problème ne commence que lorsque j'essaie d'obtenir le numéro de série sur les systèmes d'exploitation 64 bits (Vista Ultimate 64, en particulier). Le code renvoie String.Empty ou un espace tout le temps.Obtenir le numéro de série du disque dur (et pas le volume) sur Vista Ultimate 64 bits

Quelqu'un a eu une idée pour résoudre ce problème?

EDIT:

J'ai utilisé les outils Dave Cluderay suggérés, avec des résultats intéressants:

Voici la sortie de DiskId32, sous Windows XP SP2 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 

Et DiskId32 fonctionne sous Windows Vista Édition Intégrale 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___________: 

Comment moins L'information est sur Vista, et comment le numéro de série n'est pas retourné. Aussi l'autre outil, EnumDisk, se réfère à mes disques durs sur Vista comme "SCSI" par opposition à "ATA" sur Windows XP.

Des idées?

EDIT 2:

Je souhaite publier les résultats de EnumDisks:

Sous Windows XP SP2 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 *** 

Et sur Vista 64 Édition Intégrale:

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

D'autres informations peuvent être pertinentes: les deux systèmes d'exploitation sur la même machine physique (c'est-à-dire sur le double démarrage) ou sur des machines séparées? Est-ce une machine virtuelle? Tout ce qui est remarquable à propos des disques, par exemple (RAID, etc.)? –

+0

Les deux systèmes d'exploitation sont sur la même machine physique. Aucune machine virtuelle utilisée. Rien de spécial sur mon disque dur, un lecteur typique ATA MAXTOR 160 Go. – TheAgent

+0

Salut à nouveau. Vraisemblablement EnumDisk1 n'a pas réussi à obtenir le numéro de série sur Vista 64? –

Répondre

-1

Vous pouvez utiliser l'API Windows non gérée pour cela:

appelez GetVolumeInformation api avec la structure appropriée et trouvez le champ entier VolumeSerialNumber.

Cette API est là depuis des lustres et travaillait pour moi depuis Windows 98. Malheureusement, je ne peux pas la vérifier sur x64.

Pouvez-vous voir le bon numéro de série en utilisant d'autres outils Windows? Par ailleurs: '0' est un numéro de série valide! Cela peut arriver si l'image disque a été restaurée à partir de la sauvegarde ou quelque chose comme ça.

+0

Merci pour votre réponse, mais je ne cherche pas de numéro de série de volume, mais le numéro de série du disque dur, le numéro de modèle, etc. – TheAgent

2

This code devrait vous donner le numéro de série du disque dur. Il est similaire (ReadPhysicalDriveInNTWithAdminRights) au code auquel vous êtes lié mais avec plusieurs fonctions supplémentaires.

+2

Il travaille dans VB.NET pas C++ – joshperry

2

Vous devez vous assurer que vos définitions de P/Invoke sont sympathiques 64 bits. Vous pouvez également définir le processeur cible des projets de votre solution sur 32 bits. Plus d'informations sur P/Invoke et 64 bits peuvent être trouvés here.

EDIT:

Le code suivant réécrite pourrait fonctionner mieux pour vous - au fond, j'ai rangé le P/Invoke définitions et a ajouté une meilleure gestion des erreurs. Le code fait deux tentatives pour obtenir le numéro de série. Le premier utilise IOCTL_STORAGE_QUERY_PROPERTY et le second utilise 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 

C'est le code pour l'appeler:

' 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 

Je n'ai qu'une seule machine 64 bits pour effectuer le test, mais ce code ne fonctionne là-dessus.

+0

Un Vista 64 bits? Parce que j'ai testé ce code sur mon Vista Ultimate 64 bits et il renvoie toujours une chaîne vide. (J'utilise String.Trim sur le résultat car il y a des caractères espace supplémentaires retournés avant le numéro de série sur les systèmes d'exploitation 32 bits). Aucun code .NET ne semble avoir été capable de renvoyer le SN. Comment puis-je contacter un programmeur de MS pour en discuter? – TheAgent

+0

J'ai testé ce code sur un serveur Windows 7 RC x64 HP ML-115. Il existe des exemples C++ disponibles, que vous pouvez télécharger dans un format exécutable compilé. Les exemples auxquels je pense en particulier sont diskid32 (http://www.winsim.com/diskid32/diskid32.html) et EnumDisk1 (http://support.microsoft.com/kb/264203). Je vous suggère d'essayer d'exécuter les exécutables sur votre machine 64 bits. S'ils sont capables de récupérer le numéro de série, peut-être que moi ou quelqu'un d'autre peut vous aider à implémenter les mêmes techniques dans votre propre code VB.net. –

0

Modifié à partir du code 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(); 
     } 
    } 
} 

Sur mon système exécutant Vista Home Premium 64 bits, me donne une chaîne hexagonale de 40 caractères que je suppose est mon numéro de série. Je vais ouvrir la boîte et confirmer plus tard, mais essayez-le et voir si c'est ce que vous cherchez.

+0

J'ai déjà testé ça. Le WMI pour obtenir le numéro de série du disque dur a quelques problèmes, y compris ne pas travailler sur certaines versions de Windows et les échecs intermittents, de sorte que ce ne serait pas la réponse. – TheAgent

+0

Sur mon ordinateur portable (DELL Latitude E6400 sous XP), l'accès à la propriété "SerialNumber" renvoie une exception "introuvable". – newman

8

Ce code fait trois tentatives pour obtenir le numéro de série:

  1. En utilisant IOCTL_STORAGE_QUERY_PROPERTY.
  2. en utilisant SMART_RCV_DRIVE_DATA.
  3. en utilisant IOCTL_SCSI_PASS_THROUGH.

Ce code fonctionne pour moi sur 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 

Et voici le code pour l'appeler:

' 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 

EDIT - J'ai changé la méthode principale afficher les résultats de chaque tentative de comparaison. Cela illustrera, espérons-le, comment ces techniques peuvent être hasardeuses.

+0

Salut! J'ai converti votre code en C# mais en utilisant SCSI PassThrough, je reçois un résultat étrange (numéro de modèle au lieu du numéro de série). J'ai posté une question ici à ce sujet et je serais plié si vous pouvez jeter un coup d'oeil parce que vous savez probablement ce qui se passe: http://stackoverflow.com/questions/16443215/c-native-reading-hdd-serial- using-scsi-passthrough –

+0

Enfin un peu de code pour lire s/n physique de hdd sous XP. thx – nelek

1

Got travaillant sur 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"] 
     } 

Probablement la classe Win32_PhysicalMedia n'est pas servi sur les plates-formes 64 bits.

Même Disk32, en ce moment, fonctionne (en dehors d'un bogue lors du retournement des octets de numéro de série), car il est basé sur les mêmes concepts.

+0

Je viens d'essayer ce code sur ma machine (XP, en utilisant .NET 4.0) et en accédant à obj.Properties ["SerialiNumber"] jette "non trouvé" exception. – newman

+0

Je viens d'essayer ce code sur ma machine (XP, en utilisant .NET 4.0) et en accédant à obj.Properties ["SerialiNumber"] jette "non trouvé" exception. – newman

+0

Sous Windows XP, vous devez interroger Win32_PhysicalMedia. Ce code fonctionne sur Windows 7. – Luca

Questions connexes