title: How to get Hard-Disk SerialNumber in C# (no WMI)?
body: <p>I know there are two articles in CodeProject (one uses WMI and the other no WMI but in C++). I tried the WMI way, not only it's slow, but it is also unreliable. So, that's why I decided not to pursue that way. I want to do it in C# through pInvoke. I tried it but got stuck in DeviceIoControl API. Can anybody give me a hint? Here is my code:</p>
  
  <pre><code>using System;
  using System.ComponentModel;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using System.Runtime.InteropServices;
  using Microsoft.Win32.SafeHandles;
  
  namespace Chemulator.Common
  {
      public class HDSerialNumber
      {
          [StructLayout(LayoutKind.Sequential)]
          private struct IDEREGS
          {
              public byte bFeaturesReg;
              public byte bSectorCountReg;
              public byte bSectorNumberReg;
              public byte bCylLowReg;
              public byte bCylHighReg;
              public byte bDriveHeadReg;
              public byte bCommandReg;
              public byte bReserved;
          }
  
          [StructLayout(LayoutKind.Sequential)]
          private struct SENDCMDINPARAMS
          {
              public Int32 cBufferSize;
              public IDEREGS irDriveRegs;
              public byte bDriveNumber;
              [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
              public byte[] bReserved;
              [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
              public Int32[] dwReserved;
              [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
              public byte[] bBuffer;
          }
  
  
          [StructLayout(LayoutKind.Sequential)]
          private struct DRIVERSTATUS
          {
              public byte bDriverError;
              public byte bIDEError;
              [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
              public byte[] bReserved;
              [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
              public Int32[] dwReserved;
          }
  
          [StructLayout(LayoutKind.Sequential)]
          private struct SENDCMDOUTPARAMS
          {
              public Int32 cBufferSize;
              public DRIVERSTATUS DriverStatus;
              [MarshalAs(UnmanagedType.ByValArray, SizeConst = IDENTIFY_BUFFER_SIZE)]
              public byte[] bBuffer;
          }
  
          [StructLayout(LayoutKind.Sequential)]
          private struct GETVERSIONOUTPARAMS
          {
              public byte bVersion;
              public byte bRevision;
              public byte bReserved;
              public byte bIDEDeviceMap;
              public Int32 fCapabilities;
              [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
              public Int32 dwReserved;
          }
  
          [StructLayout(LayoutKind.Sequential)]
          private struct STORAGE_PROPERTY_QUERY
          {
              public Int32 PropertyId;
              public Int32 QueryType;
              [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
              public byte[] AdditionalParameters;
          }
  
          [StructLayout(LayoutKind.Sequential)]
          private struct STORAGE_DEVICE_DESCRIPTOR
          {
              public Int32 Version;
              public Int32 Size;
              public byte DeviceType;
              public byte DeviceTypeModifier;
              public byte RemovableMedia;
              public byte CommandQueueing;
              public Int32 VendorIdOffset;
              public Int32 ProductIdOffset;
              public Int32 ProductRevisionOffset;
              public Int32 SerialNumberOffset;
              public byte BusType;
              public Int32 RawPropertiesLength;
              [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10240)]
              public byte[] RawDeviceProperties;
          }
  
          [DllImport("kernel32.dll", SetLastError = true)]
          private static extern SafeFileHandle CreateFile(string lpFileName, Int32 dwDesiredAccess, Int32 dwShareMode, IntPtr lpSecurityAttributes, Int32 dwCreationDisposition, Int32 dwFlagsAndAttributes, IntPtr hTemplateFile);
  
          [DllImport("kernel32")]
          private static extern bool DeviceIoControl(SafeFileHandle hDevice, uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, IntPtr lpOutBuffer, uint nOutBufferSize, ref uint lpBytesReturned, IntPtr lpOverlapped);
  
  
          private const Int32 OPEN_EXISTING = 3;
          private const Int32 GENERIC_READ = unchecked((int)0x80000000);
          private const Int32 GENERIC_WRITE = 0x40000000;
          private const Int32 FILE_SHARE_READ = 0x1;
          private const Int32 FILE_SHARE_WRITE = 0x2;
          private const Int32 FILE_SHARE_DELETE = 0x4;
          private const Int32 SMART_GET_VERSION = 0x74080;
          private const Int32 SMART_RCV_DRIVE_DATA = 0x7C088;
          private const Int32 ID_CMD = 0xEC;
          private const Int32 IDENTIFY_BUFFER_SIZE = 512;
          private const Int32 CAP_SMART_CMD = 0x4;
          private const Int32 IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400;
          private const Int32 PropertyStandardQuery = 0;
          private const Int32 StorageDeviceProperty = 0;
  
          public static string GetSerialNumber(int diskNumber)
          {
              string str = GetSerialNumberUsingStorageQuery(diskNumber);
              if (string.IsNullOrEmpty(str))
                 str = GetSerialNumberUsingSmart(diskNumber);
              return str;
          }
  
          public static string GetSerialNumberUsingStorageQuery(int diskNumber)
          {
              using (SafeFileHandle hDisk = OpenDisk(diskNumber))
              {
                  uint iBytesReturned = 0;
                  var spq = new STORAGE_PROPERTY_QUERY();
                  var sdd = new STORAGE_DEVICE_DESCRIPTOR();
                  spq.PropertyId = StorageDeviceProperty;
                  spq.QueryType = PropertyStandardQuery;
                  if (DeviceIoControl(hDisk, IOCTL_STORAGE_QUERY_PROPERTY, spq, (uint)Marshal.SizeOf(spq), sdd, (uint)Marshal.SizeOf(sdd), ref iBytesReturned, IntPtr.Zero))
                      throw CreateWin32Exception(Marshal.GetLastWin32Error(), "DeviceIoControl(IOCTL_STORAGE_QUERY_PROPERTY)");
  
                  var result = new StringBuilder();
                  if (sdd.SerialNumberOffset &gt; 0)
                  {
                      var rawDevicePropertiesOffset = Marshal.SizeOf(sdd) - sdd.RawDeviceProperties.Length;
                      int pos = sdd.SerialNumberOffset - rawDevicePropertiesOffset;
                      while (pos &lt; iBytesReturned &amp;&amp; sdd.RawDeviceProperties[pos] != 0)
                      {
                          result.Append(Encoding.ASCII.GetString(sdd.RawDeviceProperties, pos, 1));
                          pos += 1;
                      }
                  }
                  return result.ToString();
              }
          }
  
          public static string GetSerialNumberUsingSmart(int diskNumber)
          {
              using (SafeFileHandle hDisk = OpenDisk(diskNumber))
              {
                  if (IsSmartSupported(hDisk))
                  {
                      Int32 iBytesReturned = 0;
                      var sci = new SENDCMDINPARAMS();
                      var sco = new SENDCMDOUTPARAMS();
                      sci.irDriveRegs.bCommandReg = ID_CMD;
                      sci.bDriveNumber = (byte)diskNumber;
                      sci.cBufferSize = IDENTIFY_BUFFER_SIZE;
                      if (DeviceIoControl(hDisk, SMART_RCV_DRIVE_DATA, sci, (uint)Marshal.SizeOf(sci), sco, (uint)Marshal.SizeOf(sco), ref iBytesReturned, IntPtr.Zero))
                          throw CreateWin32Exception(Marshal.GetLastWin32Error(), "DeviceIoControl(SMART_RCV_DRIVE_DATA)");
  
                      var result = new StringBuilder();
                      for (int index = 20; index &lt; 39; index += 2)
                      {
                          result.Append(Encoding.ASCII.GetString(sco.bBuffer, index + 1, 1));
                          result.Append(Encoding.ASCII.GetString(sco.bBuffer, index, 1));
                      }
                      return result.ToString();
                  }
                  return string.Empty;
              }
          }
  
          private static Win32Exception CreateWin32Exception(Int32 errorCode, string context)
          {
              var win32Exception = new Win32Exception(errorCode);
              win32Exception.Data["Context"] = context;
              return win32Exception;
          }
  
          private static SafeFileHandle OpenDisk(int diskNumber)
          {
              SafeFileHandle hDevice = CreateFile(string.Format(@"\\.\PhysicalDrive{0}", diskNumber), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
              if (!hDevice.IsInvalid)
                  return hDevice;
              else
                  throw CreateWin32Exception(Marshal.GetLastWin32Error(), "CreateFile");
          }
  
          private static bool IsSmartSupported(SafeFileHandle hDisk)
          {
              uint iBytesReturned = 0;
              var gvo = new GETVERSIONOUTPARAMS();
              IntPtr pGVO = Marshal.AllocHGlobal(512);
              if (DeviceIoControl(hDisk, SMART_GET_VERSION, IntPtr.Zero, 0, pGVO, 512, ref iBytesReturned, IntPtr.Zero))
                  return false;
              return (gvo.fCapabilities &amp; CAP_SMART_CMD) &gt; 0;
          }
      }
  }
  </code></pre>