Search code examples
c#wmi

How to get the Hard Drive Serial number?


Currently I am using the below code to get the serial number of Hard Drive:

private void GetAllDiskDrives()
{
    var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");

    foreach (ManagementObject wmi_HD in searcher.Get())
    {
        HardDrive hd = new HardDrive();
        hd.Model = wmi_HD["Model"].ToString();
        hd.InterfaceType = wmi_HD["InterfaceType"].ToString();
        hd.SerialNo = wmi_HD.GetPropertyValue("SerialNumber").ToString();//get the serailNumber of diskdrive
        HdCollection.Add(hd);
    }
}

public class HardDrive
{
    public string Model { get; set; }
    public string InterfaceType { get; set; }
    public string SerialNo { get; set; }
}

This code works fine.

But the above code returns all the Drives. I just want to have only specific hard-drive (Not Partition) serial number on which my software is running.

So, how can I get the serial number of hard-drive in which my software is running?


Solution

  • Use SELECT * FROM Win32_PhysicalMedia to find all physical drives.

    To find the physical drive your program is loaded from you would have to extract the drive letter from System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase, then load all partitions using SELECT * FROM Win32_DiskDrive and then somehow map the desired partition to one of the physical drives from SELECT * FROM Win32_PhysicalMedia.


    I looked into your problem a little further. It seems to be impossible to correlate partitions and physical drives using just WMI. But using WinAPI directly it is a piece of cake:

    [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern Boolean GetVolumeNameForVolumeMountPoint(String mountPoint, StringBuilder name, UInt32 bufferLength);
    
    private enum FileAccess : uint
    {
        None = 0
    }
    
    private enum FileShare : uint
    {
        ReadWriteDelete = 0x00000001 | 0x00000002 | 0x00000004 // FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
    }
    
    private enum FileCreation : uint
    {
        OpenExisting = 3 // OPEN_EXISTING
    }
    
    private enum FileFlags : uint
    {
        None = 0
    }
    
    [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CreateFile(String fileName, FileAccess access, FileShare share, IntPtr secAttr,
        FileCreation creation, FileFlags flags, IntPtr templateFile);
    
    [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern Boolean CloseHandle(IntPtr handle);
    
    private enum IoControlCode
    {
        GetVolumeDiskExtents = 0x00560000 // IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
    }
    
    [StructLayout(LayoutKind.Explicit)]
    private struct VolumeDiskExtents
    {
        [FieldOffset(0)]
        public UInt32 numberOfDiskExtents;
        [FieldOffset(8)]
        public UInt32 diskNumber;
        [FieldOffset(16)]
        public Int64 startingOffset;
        [FieldOffset(24)]
        public Int64 extentLength;
    }
    
    [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern Boolean DeviceIoControl(IntPtr device, IoControlCode controlCode, IntPtr inBuffer, UInt32 inBufferSize,
        ref VolumeDiskExtents extents, UInt32 outBufferSize, ref UInt32 bytesReturned, IntPtr overlapped);
    
    public class PhysicalDisk
    {
        public PhysicalDisk(String physicalName, String model, String interfaceType, String serialNumber)
        {
            this.PhysicalName = physicalName;
            this.Model = model;
            this.InterfaceType = interfaceType;
            this.SerialNumber = serialNumber;
        }
        public String PhysicalName { get; private set; }
        public String Model { get; private set; }
        public String InterfaceType { get; private set; }
        public String SerialNumber { get; private set; }
    }
    
    public PhysicalDisk GetPhysicalDiskFromCurrentDrive()
    {
        //
        // Get the drive letter of the drive the executable was loaded from.
        //
        var basePath = System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase.Replace("file:///", "");
        var driveLetter = System.IO.Path.GetPathRoot(basePath);
        // TODO: Validate driveLetter; could also be a UNC path.
    
        //
        // Get the volume name of the drive letter.
        //
        var volumeNameBuffer = new StringBuilder(65536);
        if (!GetVolumeNameForVolumeMountPoint(driveLetter, volumeNameBuffer, (UInt32)volumeNameBuffer.Capacity))
            throw new Win32Exception();
        var volumeName = volumeNameBuffer.ToString().TrimEnd('\\'); // Remove trailing backslash
    
        //
        // Open the volume and retrieve the disk number.
        //
        UInt32 diskNumber;
        IntPtr volume = IntPtr.Zero;
        try
        {
            volume = CreateFile(volumeName, FileAccess.None, FileShare.ReadWriteDelete, IntPtr.Zero,
                FileCreation.OpenExisting, FileFlags.None, IntPtr.Zero);
            if (volume == (IntPtr)(-1)) // INVALID_HANDLE_VALUE
            {
                volume = IntPtr.Zero;
                throw new Win32Exception();
            }
    
            VolumeDiskExtents extents = new VolumeDiskExtents();
            UInt32 bytesReturned = 0;
            if (!DeviceIoControl(volume, IoControlCode.GetVolumeDiskExtents, IntPtr.Zero, 0,
                ref extents, (UInt32)Marshal.SizeOf(extents), ref bytesReturned, IntPtr.Zero))
            {
                // Partitions can span more than one disk, we will ignore this case for now.
                // See http://msdn.microsoft.com/en-us/library/windows/desktop/aa365727(v=vs.85).aspx
                if (Marshal.GetLastWin32Error() != 234 /*ERROR_MORE_DATA*/)
                    throw new Win32Exception();
            }
    
            diskNumber = extents.diskNumber;
        }
        finally
        {
            if (volume != IntPtr.Zero)
            {
                CloseHandle(volume);
                volume = IntPtr.Zero;
            }
        }
    
        //
        // Build the physical disk name from the disk number.
        //
        String physicalName = ("\\\\.\\PHYSICALDRIVE" + diskNumber).Replace("\\", "\\\\");
    
        //
        // Find information about the physical disk using WMI.
        //
        var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive WHERE DeviceID = \"" + physicalName + "\"");
        foreach (ManagementObject obj in searcher.Get())
        {
            return new PhysicalDisk(
                obj["DeviceID"].ToString(),
                obj["Model"].ToString(),
                obj["InterfaceType"].ToString(),
                obj["SerialNumber"].ToString()
                );
        }
    
        throw new InvalidOperationException();
    }