Reading S.M.A.R.T. Attributes

Experts,
    I am trying to read out S.M.A.R.T. (Self Monitoring and Reporting Technology) attributes off of my HD as part of an application I'm working on to report the drive temperature. I'm able to identify the drive just fine and I get the Model, Serial number, and Firware Revision back correctly. However, when I try to read the SMART attributes (like Temperature) I'm not having any luck interpreting what I'm looking at. If anyone could help me find a better way to do this and get all the attributes I would appreciate your help! I'm using C# by the way...

Yeavis
// set the IDE registers to issue an attribute read command to get the attributes
                    scip.bDriveNumber = (byte)Convert.ToInt32(DeviceName);
                    scip.cBufferSize = READ_ATTRIBUTE_BUFFER_SIZE;
                    scip.irDriveRegs.bFeaturesReg = SMART_READ_ATTRIBUTE_VALUES;
                    scip.irDriveRegs.bSectorCountReg = 1;
                    scip.irDriveRegs.bSectorNumberReg = 1;
                    scip.irDriveRegs.bCylLowReg = SMART_CYL_LOW;
                    scip.irDriveRegs.bCylHighReg = SMART_CYL_HIGH;
                    scip.irDriveRegs.bDriveHeadReg = (byte)(0xA0 | ((Convert.ToInt32(DeviceName) & 1) << 4));
                    scip.irDriveRegs.bCommandReg = IDE_EXECUTE_SMART_FUNCTION;
 
                    // if the call to DeviceIoControl is successful
                    if (DeviceIoControl(handle, DFP_RECEIVE_DRIVE_DATA, ref scip, (Marshal.SizeOf(scip) - 1), bSMARTAttribute, OUTPUT_DATA_SIZE, ref bytRv, 0))
                    {
                        for (int i = 0; i < (NUM_ATTRIBUTE_STRUCTS - 1); i++)
                        {
                            if (bSMARTAttribute[i] > 0)
                            {
                                di.Attributes.AttrID = bSMARTAttribute[i];
                                di.Attributes.AttrName = GetSMARTAttrName(bSMARTAttribute[i]);
                                di.Attributes.AttrValue = bSMARTAttribute[i];
                                di.Attributes.WorstValue = bSMARTAttribute[i];
                                di.NumAttributes += 1;
                            }
                        }
                    }

Open in new window

YeavisAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

JimBrandleyCommented:
There's a mapping here that should help, though you may need to look elsewhere for vendor-specific attributes for other brands.

http://www.t13.org/Documents/UploadedDocuments/docs2005/e05148r0-ACS-SMARTAttributesAnnex.pdf

Jim
0
YeavisAuthor Commented:
Thanks, that's great information! However, I'm looking for some "coding" help in regards to this. I have used the routine above and I do get back data, however I don't think I'm doing it right because my struct only seems to have one set of attributes in it. I've converted this code over from C++ and I just don't think I'm doing something correctly...
0
JimBrandleyCommented:
If you set a breakpoint here:
          if (bSMARTAttribute[i] > 0)
and quickwatch bSMARTAttribute[i], what do you see? Is the organization in that object clear?

Jim
0
Newly released Acronis True Image 2019

In announcing the release of the 15th Anniversary Edition of Acronis True Image 2019, the company revealed that its artificial intelligence-based anti-ransomware technology – stopped more than 200,000 ransomware attacks on 150,000 customers last year.

JimBrandleyCommented:
If you can post your code, maybe I can see where the problems are by running it.

Jim
0
YeavisAuthor Commented:
I'll do that first thing in the morning (dont have access to it from home). I appreciate your time in helping me figure this out...
0
JimBrandleyCommented:
I enjoy problem solving.

Jim
0
YeavisAuthor Commented:
Here's the code I'm using to attempt to read the smart attributes. It would be nice if I could marshal the byte arrays into the structures, but so far I haven't had any luck getting this to work. Thanks again for your help, your time is appreciated!
[StructLayout(LayoutKind.Sequential)]
        internal struct DRIVE_INFO
        {
            public byte bDriveType;
            public string SerialNumber;
            public string Model;
            public string FirmWare;
            public long Cylinders;
            public long Heads;
            public long SecPerTrack;
            public long BytesPerSector;
            public long BytesPerTrack;
            public byte NumAttributes;
            public ATTR_DATA Attributes;
        }
 
        [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 512)]
        internal struct ATTR_DATA
        { 
            public byte AttrID;
            public string AttrName;
            public byte AttrValue;
            public byte ThresholdValue;
            public byte WorstValue;
            public byte RawValue;
            public STATUS_FLAGS StatusFlag;
        }
 
        [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 512)]
        internal struct ATTR_THRESHOLD
        {
            public byte AttrID;
            public byte WarrantyThreshold;
            public byte[] bReserved;
        }
 
        [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 512)]
        internal struct IDSECTOR
        {
            public ushort wGenConfig;
            public ushort wNumCyls;
            public ushort wReserved;
            public ushort wNumHeads;
            public ushort wBytesPerTrack;
            public ushort wBytesPerSector;
            public ushort wSectorsPerTrack;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
            public ushort[] wVendorUnique;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
            public byte[] sSerialNumber;
            public ushort wBufferType;
            public ushort wBufferSize;
            public ushort wECCSize;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
            public byte[] sFirmwareRev;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
            public byte[] sModelNumber;
            public ushort wMoreVendorUnique;
            public ushort wDoubleWordIO;
            public ushort wCapabilities;
            public ushort wReserved1;
            public ushort wPIOTiming;
            public ushort wDMATiming;
            public ushort wBS;
            public ushort wNumCurrentCyls;
            public ushort wNumCurrentHeads;
            public ushort wNumCurrentSectorsPerTrack;
            public uint ulCurrentSectorCapacity;
            public ushort wMultSectorStuff;
            public uint ulTotalAddressableSectors;
            public ushort wSingleWordDMA;
            public ushort wMultiWordDMA;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
            public byte[] bReserved;
        }
 
        [StructLayout (LayoutKind.Sequential)] 
        internal struct DRIVERSTATUS
        {  
            public byte bDriverError;
            public byte bIDEStatus;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst=2)]
            public byte [] bReserved; 
            [MarshalAs(UnmanagedType.ByValArray, SizeConst=2)]
            public uint [] dwReserved;
        } 
 
        [StructLayout(LayoutKind.Sequential)]
        public struct GETVERSIONINPARAMS
        {
            public byte bVersion;
            public byte bRevision;
            public byte bReserved;
            public byte bIDEDeviceMap;
            public ulong fCapabilities;
            public IntPtr dwReserved;
        }
 
        [StructLayout (LayoutKind.Sequential)] 
        internal struct GETVERSIONOUTPARAMS 
        {  
            public byte bVersion; 
            public byte bRevision; 
            public byte bReserved;
            public byte bIDEDeviceMap;
            public uint fCapabilities;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)] 
            public uint [] dwReserved; // For future use.
        }
 
        [StructLayout (LayoutKind.Sequential)] 
        internal struct SENDCMDINPARAMS 
        {
            public uint cBufferSize;
            public IDEREGS irDriveRegs;
            public byte bDriveNumber; 
            [MarshalAs(UnmanagedType.ByValArray, SizeConst=3 )] 
            public byte [] bReserved;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst=4 )]
            public uint [] dwReserved;
            public byte bBuffer;
        } 
 
        [StructLayout (LayoutKind.Sequential)] 
        internal struct SENDCMDOUTPARAMS
        {  
            public uint cBufferSize; 
            public DRIVERSTATUS DriverStatus;
            public IDSECTOR IDS;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst=513)] 
            public byte [] bBuffer; 
        }
 
 
        public string ReadSMARTAttributes(string DeviceName)
        {
            // device handle
            SafeFileHandle handle = null;
 
            // structures
            SENDCMDINPARAMS scip = new SENDCMDINPARAMS();
            SENDCMDOUTPARAMS scop = new SENDCMDOUTPARAMS();
 
            // string builder
            StringBuilder s = new StringBuilder();
 
            // unsigned integers
            uint bytRv = 0;
 
            // byte arrays
            byte[] bSMARTAttribute = new byte[OUTPUT_DATA_SIZE - 1];
            byte[] bSMARTThreshold = new byte[OUTPUT_DATA_SIZE - 1];
 
            try
            {
                // open a handle to the device
                openHandle(ref handle, DeviceName);
 
                // if handle is invalid throw exception
                if (handle.IsInvalid)
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
                else
                {
                    // split the physical device id into a single value (e.g. 1)
                    DeviceName = DeviceName.Split('E')[1];
 
                    // set the IDE registers to issue an attribute read command to get the attributes
                    scip.bDriveNumber = (byte)Convert.ToInt32(DeviceName);
                    scip.cBufferSize = READ_ATTRIBUTE_BUFFER_SIZE;
                    scip.irDriveRegs.bFeaturesReg = SMART_READ_ATTRIBUTE_VALUES;
                    scip.irDriveRegs.bSectorCountReg = 1;
                    scip.irDriveRegs.bSectorNumberReg = 1;
                    scip.irDriveRegs.bCylLowReg = SMART_CYL_LOW;
                    scip.irDriveRegs.bCylHighReg = SMART_CYL_HIGH;
                    scip.irDriveRegs.bDriveHeadReg = (byte)(0xA0 | ((Convert.ToInt32(DeviceName) & 1) << 4));
                    scip.irDriveRegs.bCommandReg = IDE_EXECUTE_SMART_FUNCTION;
 
                    // if the call to DeviceIoControl is successful
                    if (DeviceIoControl(handle, DFP_RECEIVE_DRIVE_DATA, ref scip, Marshal.SizeOf(scip), bSMARTAttribute, (Marshal.SizeOf(scop) + READ_ATTRIBUTE_BUFFER_SIZE - 1), ref bytRv, 0))
                    {
                        for (int i = 0; i < (NUM_ATTRIBUTE_STRUCTS - 1); i++)
                        {
                            if (bSMARTAttribute[i] > 0)
                            {
                                di.Attributes.AttrID = bSMARTAttribute[i];
                                di.Attributes.AttrName = GetSMARTAttrName(bSMARTAttribute[i]);
                                di.Attributes.AttrValue = bSMARTAttribute[3];
                                di.Attributes.WorstValue = bSMARTAttribute[4];
                                di.NumAttributes += 1;
                            }
                        }
                    }
 
                    // set the IDE registers to issue a threshold attribute read command
                    scip.bDriveNumber = (byte)Convert.ToInt32(DeviceName);
                    scip.cBufferSize = READ_THRESHOLD_BUFFER_SIZE;
                    scip.irDriveRegs.bFeaturesReg = SMART_READ_ATTRIBUTE_THRESHOLDS;
                    scip.irDriveRegs.bSectorCountReg = 1;
                    scip.irDriveRegs.bSectorNumberReg = 1;
                    scip.irDriveRegs.bCylLowReg = SMART_CYL_LOW;
                    scip.irDriveRegs.bCylHighReg = SMART_CYL_HIGH;
                    scip.irDriveRegs.bDriveHeadReg = (byte)(0xA0 | ((Convert.ToInt32(DeviceName) & 1) << 4));
                    scip.irDriveRegs.bCommandReg = IDE_EXECUTE_SMART_FUNCTION;
 
                    //int nBytes = Marshal.SizeOf(typeof(ATTR_DATA));
                    //IntPtr ptrLPOutBuffer = Marshal.AllocHGlobal(nBytes);
 
                    // if the call to DeviceIoControl is successful
                    if (DeviceIoControl(handle, DFP_RECEIVE_DRIVE_DATA, ref scip, (Marshal.SizeOf(scip) - 1), bSMARTThreshold, OUTPUT_DATA_SIZE + READ_THRESHOLD_BUFFER_SIZE - 1, ref bytRv, 0))
                    {
                        // have not gotten this far yet
                    }
 
                    return s.ToString();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return string.Empty;
            }
        }

Open in new window

0
JimBrandleyCommented:
Thanks. I have a busy day at work today, but will try to get this running as soon as I can.

Jim
0
YeavisAuthor Commented:
No problem.
0
JimBrandleyCommented:
I have been trying to get this to compile so I can take a look at it. In the code you posted,
IDEREGS and STATUS_FLAGS are undefined. I was able to track down a structure definition for IDEREGS, but I have been unable to locate STATUS_FLAGS.

Jim
0
YeavisAuthor Commented:
Sorry about that, I didn't realize I hadn't posted everything. Here's everything that I have...  some of it is probably not necessary, but I figured I'd post it just in case. Thanks!
[StructLayout(LayoutKind.Sequential)]
        public 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;
        }
 
[Flags]
        internal enum ATTRIBUTE_ID : int
        {
            ATTR_INVALID = 0,
            ATTR_READ_ERROR_RATE = 1,
            ATTR_THROUGHPUT_PERF = 2,
            ATTR_SPIN_UP_TIME = 3,
            ATTR_START_STOP_COUNT = 4,
            ATTR_REALLOC_SECTOR_COUNT = 5,
            ATTR_READ_CHANNEL_MARGIN = 6,
            ATTR_SEEK_ERROR_RATE = 7,
            ATTR_SEEK_TIME_PERF = 8,
            ATTR_POWER_ON_HRS_COUNT = 9,
            ATTR_SPIN_RETRY_COUNT = 10,
            ATTR_CALIBRATION_RETRY_COUNT = 11,
            ATTR_POWER_CYCLE_COUNT = 12,
            ATTR_SOFT_READ_ERROR_RATE = 13,
            ATTR_G_SENSE_ERROR_RATE = 191,
            ATTR_POWER_OFF_RETRACT_CYCLE = 192,
            ATTR_LOAD_UNLOAD_CYCLE_COUNT = 193,
            ATTR_TEMPERATURE = 194,
            ATTR_REALLOCATION_EVENTS_COUNT = 196,
            ATTR_CURRENT_PENDING_SECTOR_COUNT = 197,
            ATTR_UNCORRECTABLE_SECTOR_COUNT = 198,
            ATTR_ULTRADMA_CRC_ERROR_RATE = 199,
            ATTR_WRITE_ERROR_RATE = 200,
            ATTR_DISK_SHIFT = 220,
            ATTR_G_SENSE_ERROR_RATEII = 221,
            ATTR_LOADED_HOURS = 222,
            ATTR_LOAD_UNLOAD_RETRY_COUNT = 223,
            ATTR_LOAD_FRICTION = 224,
            ATTR_LOAD_UNLOAD_CYCLE_COUNTII = 225,
            ATTR_LOAD_IN_TIME = 226,
            ATTR_TORQUE_AMPLIFICATION_COUNT = 227,
            ATTR_POWER_OFF_RETRACT_COUNT = 228,
            ATTR_GMR_HEAD_AMPLITUDE = 230,
            ATTR_TEMPERATUREII = 231,
            ATTR_READ_ERROR_RETRY_RATE = 250,
        }
 
        [Flags]
        internal enum DRIVER_ERRORS : int
        {
            SMART_NO_ERROR = 0, //' No error
            SMART_IDE_ERROR = 1, // ' Error from IDE controller
            SMART_INVALID_FLAG = 2, // ' Invalid command flag
            SMART_INVALID_COMMAND = 3, // ' Invalid command byte
            SMART_INVALID_BUFFER = 4, // ' Bad buffer (null, invalid addr..)
            SMART_INVALID_DRIVE = 5, // ' Drive number not valid
            SMART_INVALID_IOCTL = 6, // ' Invalid IOCTL
            SMART_ERROR_NO_MEM = 7, // ' Could not lock user's buffer
            SMART_INVALID_REGISTER = 8, // ' Some IDE Register not valid
            SMART_NOT_SUPPORTED = 9, //' Invalid cmd flag set
            SMART_NO_IDE_DEVICE = 10, // ' Cmd issued to device not present although drive number is valid 11-255 reserved
        }
 
        [Flags]
        internal enum IDE_DRIVE_NUMBER : int
        {
            PRIMARY_MASTER = 1,
            PRIMARY_SLAVE = 2,
            SECONDARY_MASTER = 3,
            SECONDARY_SLAVE = 4,
        }
 
        [Flags]
        internal enum STATUS_FLAGS : int
        {
            PRE_FAILURE_WARRANTY = 0x01,
            ON_LINE_COLLECTION = 0x02,
            PERFORMANCE_ATTRIBUTE = 0x04,
            ERROR_RATE_ATTRIBUTE = 0x08,
            EVENT_COUNT_ATTRIBUTE = 0x10,
            SELF_PRESERVING_ATTRIBUTE = 0x20,
        }

Open in new window

0
JimBrandleyCommented:
Thanks.

Jim
0
JimBrandleyCommented:
Adding that gave me a raft of errors. What other assemblies are you referencing in this project besides:
System.Runtime.InteropServices

Jim
0
YeavisAuthor Commented:
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.IO;
using System.Threading;
using System.Security.Cryptography;
using System.Management;
using Microsoft.Win32.SafeHandles;
0
YeavisAuthor Commented:
Here's some of the P/Invoke stuff just in case...
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, uint lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, int hTemplateFile);
 
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern bool DeviceIoControl(SafeFileHandle hDevice, EIOControlCode dwIoControlCode, IntPtr lpInBuffer, int nInBufferSize, IntPtr lpOutBuffer, int nOutBufferSize, out int lpBytesReturned, IntPtr lpOverlapped);
 
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern bool DeviceIoControl(IntPtr hDevice, int dwIoControlCode, IntPtr lpInBuffer, int nInBufferSize, IntPtr lpOutBuffer, int nOutBufferSize, out int lpBytesReturned, IntPtr lpOverlapped);
 
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern bool DeviceIoControl(SafeFileHandle hDevice, uint dwIoControlCode, ref SENDCMDINPARAMS lpInBuffer, int nInBufferSize, IntPtr lpOutBuffer, int nOutBufferSize, ref uint lpBytesReturned, int lpOverlapped);
 
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern bool DeviceIoControl(SafeFileHandle hDevice, uint dwIoControlCode, ref SENDCMDINPARAMS lpInBuffer, int nInBufferSize, byte[] lpOutBuffer, int nOutBufferSize, ref uint lpBytesReturned, int lpOverlapped);
 
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern bool DeviceIoControl(SafeFileHandle hDevice, uint dwIoControlCode, ref SENDCMDINPARAMS lpInBuffer, int nInBufferSize, ref SENDCMDOUTPARAMS lpOutBuffer, int nOutBufferSize, ref uint lpbytesReturned, int lpOverlapped);
 
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern bool DeviceIoControl(SafeFileHandle hDevice, uint dwIoControlCode, int lpInBuffer, int nInBufferSize, ref GETVERSIONOUTPARAMS lpOutBuffer, int nOutBufferSize, ref uint lpbytesReturned, int lpOverlapped);

Open in new window

0
JimBrandleyCommented:
I am still getting a bunch of undefined constants:
EIOControlCode
OUTPUT_DATA_SIZE
READ_ATTRIBUTE_BUFFER_SIZE
SMART_READ_ATTRIBUTE_VALUES
(more)...

One object:
di

Methods:
openHandle()
GetSMARTAttrName()

If you can post your file or files, this might be easier.

Jim
 
0
YeavisAuthor Commented:
di is just a structure (DRIVE_INFO) that you already have. I made it global, so that's easy to fix. I've attached all the other stuff...
      // from winuser.h
        internal const int WM_DEVICECHANGE = 0x0219;
        internal const int INVALID_HANDLE_VALUE = -1;
 
        // CreateFile
        internal const uint GENERIC_READ = 0x80000000;
        internal const uint GENERIC_WRITE = 0x40000000;
        internal const uint GENERIC_EXECUTE = 0x20000000;
        internal const uint GENERIC_ALL = 0x10000000;
 
        // Share constants - CreateFile
        internal const uint FILE_SHARE_READ = 0x00000001;
        internal const uint FILE_SHARE_WRITE = 0x00000002;
        internal const uint FILE_SHARE_DELETE = 0x00000004;
 
        // CreationDisposition constants - CreateFile
        internal const uint CREATE_NEW = 1;
        internal const uint CREATE_ALWAYS = 2;
        internal const uint OPEN_EXISTING = 3;
        internal const uint OPEN_ALWAYS = 4;
        internal const uint TRUNCATE_EXISTING = 5;
        internal const int BUFFER_SIZE = 2048; /*0x3ffff;*/
        internal const int MAXIMUM_USB_STRING_LENGTH = 255;
 
        // fmifs.dll
        internal const int FMIFS_HARDDISK = 0xC;
        internal const int FMIFS_REMOVABLE = 0xB;
        internal const int FMIFS_FLOPPY = 0x8;
        internal const int USB_DEVICE_DESCRIPTOR_TYPE = 0x1;
        internal const int USB_STRING_DESCRIPTOR_TYPE = 0x3;
 
        // usb ioctl codes
        internal const int IOCTL_USB_GET_ROOT_HUB_NAME = 0x220408;
        internal const int IOCTL_USB_GET_NODE_INFORMATION = 0x220408;
        internal const int IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX = 0x220448;
        internal const int IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME = 0x220420;
        internal const int IOCTL_GET_HCD_DRIVERKEY_NAME = 0x220424;
        internal const int IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION = 0x220410;
        internal const int IOCTL_USB_GET_NODE_CONNECTION_NAME = 0x220414;
 
        // from winerror.h
        internal const int ERROR_NO_MORE_ITEMS = 259;
        internal const int ERROR_INSUFFICIENT_BUFFER = 122;
        internal const int ERROR_INVALID_DATA = 13;
 
        // from winioctl.h
        internal const string GUID_DEVINTERFACE_VOLUME = "53f5630d-b6bf-11d0-94f2-00a0c91efb8b";
        internal const string GUID_DEVINTERFACE_DISK = "53f56307-b6bf-11d0-94f2-00a0c91efb8b";
        internal const string GUID_DEVINTERFACE_HUBCONTROLLER = "3abf6f2d-71c4-462a-8a92-1e6861e6af27";
        internal const int IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS = 0x00560000;
        internal const string REGSTR_KEY_USB = "USB";
        internal const int REG_SZ = 1;
 
        // from setupapi.h
        internal const int DIGCF_PRESENT = (0x00000002);
        internal const int DIGCF_DEVICEINTERFACE = (0x00000010);
        internal const int SPDRP_DEVICEDESC = 0x00000000;
        internal const int SPDRP_CAPABILITIES = 0x0000000F;
        internal const int SPDRP_CLASS = 0x00000007;
        internal const int SPDRP_CLASSGUID = 0x00000008;
        internal const int SPDRP_FRIENDLYNAME = 0x0000000C;
        internal const int DIGCF_ALLCLASSES = 0x4;
        internal const int SPDRP_DRIVER = 0x9;
 
        // smart
        internal const uint DFP_GET_VERSION = 0x00074080;
        internal const uint DFP_RECEIVE_DRIVE_DATA = 0x0007c088;
        internal const int IDE_ATAPI_IDENTIFY = 0xA1;        
        internal const int IDE_ATA_IDENTIFY = 0xEC;         
        internal const int IDE_EXECUTE_SMART_FUNCTION = 0xb0;
        internal const int IDENTIFY_BUFFER_SIZE = 512;
        internal const int OUTPUT_DATA_SIZE = IDENTIFY_BUFFER_SIZE + 16;
        internal const int CAP_ATA_ID_CMD = 1;
        internal const int CAP_ATAPI_ID_CMD = 2;
        internal const int CAP_SMART_CMD = 4;
        internal const int READ_ATTRIBUTE_BUFFER_SIZE = 512;
        internal const int READ_THRESHOLD_BUFFER_SIZE = 512;
        internal const int SMART_GET_VERSION = 0x074080;
        internal const int SMART_SEND_DRIVE_COMMAND = 0x07C084;
        internal const int SMART_RCV_DRIVE_DATA = 0x07C088;
        internal const int SMART_CYL_LOW = 0x4f;
        internal const int SMART_CYL_HIGH = 0xc2;
        internal const int SMART_READ_ATTRIBUTE_VALUES = 0xD0;
        internal const int SMART_READ_ATTRIBUTE_THRESHOLDS = 0xD1;
        internal const int SMART_ENABLE_DISABLE_ATTRIBUTE_AUTOSAVE = 0xD2;
        internal const int SMART_SAVE_ATTRIBUTE_VALUES = 0xD3;
        internal const int SMART_EXECUTE_OFFLINE_IMMEDIATE = 0xD4;
        internal const int SMART_ENABLE_SMART_OPERATIONS = 0xD8;
        internal const int SMART_DISABLE_SMART_OPERATIONS = 0xD9;
        internal const int SMART_RETURN_SMART_STATUS = 0xDA;
        internal const int NUM_ATTRIBUTE_STRUCTS = 30;
 
      [Flags]
        internal enum EMethod : uint
        {
            Buffered = 0,
            InDirect = 1,
            OutDirect = 2,
            Neither = 3
        }
 
        [Flags]
        internal enum EFileDevice : uint
        {
            Beep = 0x00000001,
            CDRom = 0x00000002,
            CDRomFileSytem = 0x00000003,
            Controller = 0x00000004,
            Datalink = 0x00000005,
            Dfs = 0x00000006,
            Disk = 0x00000007,
            DiskFileSystem = 0x00000008,
            FileSystem = 0x00000009,
            InPortPort = 0x0000000a,
            Keyboard = 0x0000000b,
            Mailslot = 0x0000000c,
            MidiIn = 0x0000000d,
            MidiOut = 0x0000000e,
            Mouse = 0x0000000f,
            MultiUncProvider = 0x00000010,
            NamedPipe = 0x00000011,
            Network = 0x00000012,
            NetworkBrowser = 0x00000013,
            NetworkFileSystem = 0x00000014,
            Null = 0x00000015,
            ParellelPort = 0x00000016,
            PhysicalNetcard = 0x00000017,
            Printer = 0x00000018,
            Scanner = 0x00000019,
            SerialMousePort = 0x0000001a,
            SerialPort = 0x0000001b,
            Screen = 0x0000001c,
            Sound = 0x0000001d,
            Streams = 0x0000001e,
            Tape = 0x0000001f,
            TapeFileSystem = 0x00000020,
            Transport = 0x00000021,
            Unknown = 0x00000022,
            Video = 0x00000023,
            VirtualDisk = 0x00000024,
            WaveIn = 0x00000025,
            WaveOut = 0x00000026,
            Port8042 = 0x00000027,
            NetworkRedirector = 0x00000028,
            Battery = 0x00000029,
            BusExtender = 0x0000002a,
            Modem = 0x0000002b,
            Vdm = 0x0000002c,
            MassStorage = 0x0000002d,
            Smb = 0x0000002e,
            Ks = 0x0000002f,
            Changer = 0x00000030,
            Smartcard = 0x00000031,
            Acpi = 0x00000032,
            Dvd = 0x00000033,
            FullscreenVideo = 0x00000034,
            DfsFileSystem = 0x00000035,
            DfsVolume = 0x00000036,
            Serenum = 0x00000037,
            Termsrv = 0x00000038,
            Ksec = 0x00000039
        }
 
        [Flags]
        internal enum EIOControlCode : uint
        {
            // STORAGE
            StorageBase = EFileDevice.MassStorage,
            StorageCheckVerify = (StorageBase << 16) | (0x0200 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            StorageCheckVerify2 = (StorageBase << 16) | (0x0200 << 2) | EMethod.Buffered | (0 << 14), // FileAccess.Any
            StorageMediaRemoval = (StorageBase << 16) | (0x0201 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            StorageEjectMedia = (StorageBase << 16) | (0x0202 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            StorageLoadMedia = (StorageBase << 16) | (0x0203 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            StorageLoadMedia2 = (StorageBase << 16) | (0x0203 << 2) | EMethod.Buffered | (0 << 14),
            StorageReserve = (StorageBase << 16) | (0x0204 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            StorageRelease = (StorageBase << 16) | (0x0205 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            StorageFindNewDevices = (StorageBase << 16) | (0x0206 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            StorageEjectionControl = (StorageBase << 16) | (0x0250 << 2) | EMethod.Buffered | (0 << 14),
            StorageMcnControl = (StorageBase << 16) | (0x0251 << 2) | EMethod.Buffered | (0 << 14),
            StorageGetMediaTypes = (StorageBase << 16) | (0x0300 << 2) | EMethod.Buffered | (0 << 14),
            StorageGetMediaTypesEx = (StorageBase << 16) | (0x0301 << 2) | EMethod.Buffered | (0 << 14),
            StorageResetBus = (StorageBase << 16) | (0x0400 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            StorageResetDevice = (StorageBase << 16) | (0x0401 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            StorageGetDeviceNumber = (StorageBase << 16) | (0x0420 << 2) | EMethod.Buffered | (0 << 14),
            StoragePredictFailure = (StorageBase << 16) | (0x0440 << 2) | EMethod.Buffered | (0 << 14),
            StorageObsoleteResetBus = (StorageBase << 16) | (0x0400 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            StorageObsoleteResetDevice = (StorageBase << 16) | (0x0401 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            // DISK
            DiskBase = EFileDevice.Disk,
            DiskGetLengthInfo = (DiskBase << 16) | (0x0017 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            DiskGetDriveGeometry = (DiskBase << 16) | (0x0000 << 2) | EMethod.Buffered | (0 << 14),
            DiskGetPartitionInfo = (DiskBase << 16) | (0x0001 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            DiskSetPartitionInfo = (DiskBase << 16) | (0x0002 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            DiskGetDriveLayout = (DiskBase << 16) | (0x0003 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            DiskSetDriveLayout = (DiskBase << 16) | (0x0004 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            DiskCreateDisk = (DiskBase << 16) | (0x00016 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            DiskVerify = (DiskBase << 16) | (0x0005 << 2) | EMethod.Buffered | (0 << 14),
            DiskFormatTracks = (DiskBase << 16) | (0x0006 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            DiskReassignBlocks = (DiskBase << 16) | (0x0007 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            DiskPerformance = (DiskBase << 16) | (0x0008 << 2) | EMethod.Buffered | (0 << 14),
            DiskIsWritable = (DiskBase << 16) | (0x0009 << 2) | EMethod.Buffered | (0 << 14),
            DiskLogging = (DiskBase << 16) | (0x000a << 2) | EMethod.Buffered | (0 << 14),
            DiskFormatTracksEx = (DiskBase << 16) | (0x000b << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            DiskHistogramStructure = (DiskBase << 16) | (0x000c << 2) | EMethod.Buffered | (0 << 14),
            DiskHistogramData = (DiskBase << 16) | (0x000d << 2) | EMethod.Buffered | (0 << 14),
            DiskHistogramReset = (DiskBase << 16) | (0x000e << 2) | EMethod.Buffered | (0 << 14),
            DiskRequestStructure = (DiskBase << 16) | (0x000f << 2) | EMethod.Buffered | (0 << 14),
            DiskRequestData = (DiskBase << 16) | (0x0010 << 2) | EMethod.Buffered | (0 << 14),
            DiskControllerNumber = (DiskBase << 16) | (0x0011 << 2) | EMethod.Buffered | (0 << 14),
            DiskSmartGetVersion = 0x074080, //(DiskBase<<16)|(0x0020<<2)|EMethod.Buffered|(FileAccess.Read<<14), 
            DiskSmartSendDriveCommand = (DiskBase << 16) | (0x0021 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            DiskSmartRcvDriveData = 0x07C088, //(DiskBase << 16) | (0x0022 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14), 
            DiskUpdateDriveSize = (DiskBase << 16) | (0x0032 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            DiskGrowPartition = (DiskBase << 16) | (0x0034 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            DiskGetCacheInformation = (DiskBase << 16) | (0x0035 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            DiskSetCacheInformation = (DiskBase << 16) | (0x0036 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            DiskDeleteDriveLayout = (DiskBase << 16) | (0x0040 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            DiskFormatDrive = (DiskBase << 16) | (0x00f3 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            DiskSenseDevice = (DiskBase << 16) | (0x00f8 << 2) | EMethod.Buffered | (0 << 14),
            DiskCheckVerify = (DiskBase << 16) | (0x0200 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            DiskMediaRemoval = (DiskBase << 16) | (0x0201 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            DiskEjectMedia = (DiskBase << 16) | (0x0202 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            DiskLoadMedia = (DiskBase << 16) | (0x0203 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            DiskReserve = (DiskBase << 16) | (0x0204 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            DiskRelease = (DiskBase << 16) | (0x0205 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            DiskFindNewDevices = (DiskBase << 16) | (0x0206 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            DiskGetMediaTypes = (DiskBase << 16) | (0x0300 << 2) | EMethod.Buffered | (0 << 14),
            //Changer
            ChangerBase = EFileDevice.Changer,
            ChangerGetParameters = (ChangerBase << 16) | (0x0000 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            ChangerGetStatus = (ChangerBase << 16) | (0x0001 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            ChangerGetProductData = (ChangerBase << 16) | (0x0002 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            ChangerSetAccess = (ChangerBase << 16) | (0x0004 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            ChangerGetElementStatus = (ChangerBase << 16) | (0x0005 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            ChangerInitializeElementStatus = (ChangerBase << 16) | (0x0006 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            ChangerSetPosition = (ChangerBase << 16) | (0x0007 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            ChangerExchangeMedium = (ChangerBase << 16) | (0x0008 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            ChangerMoveMedium = (ChangerBase << 16) | (0x0009 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            ChangerReinitializeTarget = (ChangerBase << 16) | (0x000A << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            ChangerQueryVolumeTags = (ChangerBase << 16) | (0x000B << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            FsctlRequestOplockLevel1 = (EFileDevice.FileSystem << 16) | (0 << 2) | EMethod.Buffered | (0 << 14),
            FsctlRequestOplockLevel2 = (EFileDevice.FileSystem << 16) | (1 << 2) | EMethod.Buffered | (0 << 14),
            FsctlRequestBatchOplock = (EFileDevice.FileSystem << 16) | (2 << 2) | EMethod.Buffered | (0 << 14),
            FsctlOplockBreakAcknowledge = (EFileDevice.FileSystem << 16) | (3 << 2) | EMethod.Buffered | (0 << 14),
            FsctlOpBatchAckClosePending = (EFileDevice.FileSystem << 16) | (4 << 2) | EMethod.Buffered | (0 << 14),
            FsctlOplockBreakNotify = (EFileDevice.FileSystem << 16) | (5 << 2) | EMethod.Buffered | (0 << 14),
            FsctlLockVolume = (EFileDevice.FileSystem << 16) | (6 << 2) | EMethod.Buffered | (0 << 14),
            FsctlUnlockVolume = (EFileDevice.FileSystem << 16) | (7 << 2) | EMethod.Buffered | (0 << 14),
            FsctlDismountVolume = (EFileDevice.FileSystem << 16) | (8 << 2) | EMethod.Buffered | (0 << 14),
            FsctlIsVolumeMounted = (EFileDevice.FileSystem << 16) | (10 << 2) | EMethod.Buffered | (0 << 14),
            FsctlIsPathnameValid = (EFileDevice.FileSystem << 16) | (11 << 2) | EMethod.Buffered | (0 << 14),
            FsctlMarkVolumeDirty = (EFileDevice.FileSystem << 16) | (12 << 2) | EMethod.Buffered | (0 << 14),
            FsctlQueryRetrievalPointers = (EFileDevice.FileSystem << 16) | (14 << 2) | EMethod.Neither | (0 << 14),
            FsctlGetCompression = (EFileDevice.FileSystem << 16) | (15 << 2) | EMethod.Buffered | (0 << 14),
            FsctlSetCompression = (EFileDevice.FileSystem << 16) | (16 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            FsctlMarkAsSystemHive = (EFileDevice.FileSystem << 16) | (19 << 2) | EMethod.Neither | (0 << 14),
            FsctlOplockBreakAckNo2 = (EFileDevice.FileSystem << 16) | (20 << 2) | EMethod.Buffered | (0 << 14),
            FsctlInvalidateVolumes = (EFileDevice.FileSystem << 16) | (21 << 2) | EMethod.Buffered | (0 << 14),
            FsctlQueryFatBpb = (EFileDevice.FileSystem << 16) | (22 << 2) | EMethod.Buffered | (0 << 14),
            FsctlRequestFilterOplock = (EFileDevice.FileSystem << 16) | (23 << 2) | EMethod.Buffered | (0 << 14),
            FsctlFileSystemGetStatistics = (EFileDevice.FileSystem << 16) | (24 << 2) | EMethod.Buffered | (0 << 14),
            FsctlGetNtfsVolumeData = (EFileDevice.FileSystem << 16) | (25 << 2) | EMethod.Buffered | (0 << 14),
            FsctlGetNtfsFileRecord = (EFileDevice.FileSystem << 16) | (26 << 2) | EMethod.Buffered | (0 << 14),
            FsctlGetVolumeBitmap = (EFileDevice.FileSystem << 16) | (27 << 2) | EMethod.Neither | (0 << 14),
            FsctlGetRetrievalPointers = (EFileDevice.FileSystem << 16) | (28 << 2) | EMethod.Neither | (0 << 14),
            FsctlMoveFile = (EFileDevice.FileSystem << 16) | (29 << 2) | EMethod.Buffered | (0 << 14),
            FsctlIsVolumeDirty = (EFileDevice.FileSystem << 16) | (30 << 2) | EMethod.Buffered | (0 << 14),
            FsctlGetHfsInformation = (EFileDevice.FileSystem << 16) | (31 << 2) | EMethod.Buffered | (0 << 14),
            FsctlAllowExtendedDasdIo = (EFileDevice.FileSystem << 16) | (32 << 2) | EMethod.Neither | (0 << 14),
            FsctlReadPropertyData = (EFileDevice.FileSystem << 16) | (33 << 2) | EMethod.Neither | (0 << 14),
            FsctlWritePropertyData = (EFileDevice.FileSystem << 16) | (34 << 2) | EMethod.Neither | (0 << 14),
            FsctlFindFilesBySid = (EFileDevice.FileSystem << 16) | (35 << 2) | EMethod.Neither | (0 << 14),
            FsctlDumpPropertyData = (EFileDevice.FileSystem << 16) | (37 << 2) | EMethod.Neither | (0 << 14),
            FsctlSetObjectId = (EFileDevice.FileSystem << 16) | (38 << 2) | EMethod.Buffered | (0 << 14),
            FsctlGetObjectId = (EFileDevice.FileSystem << 16) | (39 << 2) | EMethod.Buffered | (0 << 14),
            FsctlDeleteObjectId = (EFileDevice.FileSystem << 16) | (40 << 2) | EMethod.Buffered | (0 << 14),
            FsctlSetReparsePoint = (EFileDevice.FileSystem << 16) | (41 << 2) | EMethod.Buffered | (0 << 14),
            FsctlGetReparsePoint = (EFileDevice.FileSystem << 16) | (42 << 2) | EMethod.Buffered | (0 << 14),
            FsctlDeleteReparsePoint = (EFileDevice.FileSystem << 16) | (43 << 2) | EMethod.Buffered | (0 << 14),
            FsctlEnumUsnData = (EFileDevice.FileSystem << 16) | (44 << 2) | EMethod.Neither | (0 << 14),
            FsctlSecurityIdCheck = (EFileDevice.FileSystem << 16) | (45 << 2) | EMethod.Neither | (FileAccess.Read << 14),
            FsctlReadUsnJournal = (EFileDevice.FileSystem << 16) | (46 << 2) | EMethod.Neither | (0 << 14),
            FsctlSetObjectIdExtended = (EFileDevice.FileSystem << 16) | (47 << 2) | EMethod.Buffered | (0 << 14),
            FsctlCreateOrGetObjectId = (EFileDevice.FileSystem << 16) | (48 << 2) | EMethod.Buffered | (0 << 14),
            FsctlSetSparse = (EFileDevice.FileSystem << 16) | (49 << 2) | EMethod.Buffered | (0 << 14),
            FsctlSetZeroData = (EFileDevice.FileSystem << 16) | (50 << 2) | EMethod.Buffered | (FileAccess.Write << 14),
            FsctlQueryAllocatedRanges = (EFileDevice.FileSystem << 16) | (51 << 2) | EMethod.Neither | (FileAccess.Read << 14),
            FsctlEnableUpgrade = (EFileDevice.FileSystem << 16) | (52 << 2) | EMethod.Buffered | (FileAccess.Write << 14),
            FsctlSetEncryption = (EFileDevice.FileSystem << 16) | (53 << 2) | EMethod.Neither | (0 << 14),
            FsctlEncryptionFsctlIo = (EFileDevice.FileSystem << 16) | (54 << 2) | EMethod.Neither | (0 << 14),
            FsctlWriteRawEncrypted = (EFileDevice.FileSystem << 16) | (55 << 2) | EMethod.Neither | (0 << 14),
            FsctlReadRawEncrypted = (EFileDevice.FileSystem << 16) | (56 << 2) | EMethod.Neither | (0 << 14),
            FsctlCreateUsnJournal = (EFileDevice.FileSystem << 16) | (57 << 2) | EMethod.Neither | (0 << 14),
            FsctlReadFileUsnData = (EFileDevice.FileSystem << 16) | (58 << 2) | EMethod.Neither | (0 << 14),
            FsctlWriteUsnCloseRecord = (EFileDevice.FileSystem << 16) | (59 << 2) | EMethod.Neither | (0 << 14),
            FsctlExtendVolume = (EFileDevice.FileSystem << 16) | (60 << 2) | EMethod.Buffered | (0 << 14),
            FsctlQueryUsnJournal = (EFileDevice.FileSystem << 16) | (61 << 2) | EMethod.Buffered | (0 << 14),
            FsctlDeleteUsnJournal = (EFileDevice.FileSystem << 16) | (62 << 2) | EMethod.Buffered | (0 << 14),
            FsctlMarkHandle = (EFileDevice.FileSystem << 16) | (63 << 2) | EMethod.Buffered | (0 << 14),
            FsctlSisCopyFile = (EFileDevice.FileSystem << 16) | (64 << 2) | EMethod.Buffered | (0 << 14),
            FsctlSisLinkFiles = (EFileDevice.FileSystem << 16) | (65 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            FsctlHsmMsg = (EFileDevice.FileSystem << 16) | (66 << 2) | EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
            FsctlNssControl = (EFileDevice.FileSystem << 16) | (67 << 2) | EMethod.Buffered | (FileAccess.Write << 14),
            FsctlHsmData = (EFileDevice.FileSystem << 16) | (68 << 2) | EMethod.Neither | ((FileAccess.Read | FileAccess.Write) << 14),
            FsctlRecallFile = (EFileDevice.FileSystem << 16) | (69 << 2) | EMethod.Neither | (0 << 14),
            FsctlNssRcontrol = (EFileDevice.FileSystem << 16) | (70 << 2) | EMethod.Buffered | (FileAccess.Read << 14),
            // VOLUME
            VolumeBase = EFileDevice.DfsVolume,
            VolumeGetDiskExtents = 0x00560000
        }
 
 
 
 
internal void openHandle(ref SafeFileHandle hndle, string DeviceName)
            {
                try
                {
                    // use createfile to get a handle to the device
                    hndle = CreateFile(DeviceName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
 
                    // if handle came back invalid throw exception
                    if (hndle.IsInvalid)
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }
                }
                catch (Exception ex)
                {
                    // log the exception the event log
                    LogError(ex.ToString());
                }
            }
 
       public string ReadSMARTAttributes(string DeviceName)
        {
            // device handle
            SafeFileHandle handle = null;
 
            // structures
            SENDCMDINPARAMS scip = new SENDCMDINPARAMS();
            SENDCMDOUTPARAMS scop = new SENDCMDOUTPARAMS();
 
            // string builder
            StringBuilder s = new StringBuilder();
 
            // unsigned integers
            uint bytRv = 0;
 
            // byte arrays
            byte[] bSMARTAttribute = new byte[OUTPUT_DATA_SIZE - 1];
            byte[] bSMARTThreshold = new byte[OUTPUT_DATA_SIZE - 1];
 
            try
            {
                // open a handle to the device
                openHandle(ref handle, DeviceName);
 
                // if handle is invalid throw exception
                if (handle.IsInvalid)
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
                else
                {
                    // split the physical device id into a single value (e.g. 1)
                    DeviceName = DeviceName.Split('E')[1];
 
                    // set the IDE registers to issue an attribute read command to get the attributes
                    scip.bDriveNumber = (byte)Convert.ToInt32(DeviceName);
                    scip.cBufferSize = READ_ATTRIBUTE_BUFFER_SIZE;
                    scip.irDriveRegs.bFeaturesReg = SMART_READ_ATTRIBUTE_VALUES;
                    scip.irDriveRegs.bSectorCountReg = 1;
                    scip.irDriveRegs.bSectorNumberReg = 1;
                    scip.irDriveRegs.bCylLowReg = SMART_CYL_LOW;
                    scip.irDriveRegs.bCylHighReg = SMART_CYL_HIGH;
                    scip.irDriveRegs.bDriveHeadReg = (byte)(0xA0 | ((Convert.ToInt32(DeviceName) & 1) << 4));
                    scip.irDriveRegs.bCommandReg = IDE_EXECUTE_SMART_FUNCTION;
 
                    // if the call to DeviceIoControl is successful
                    if (DeviceIoControl(handle, DFP_RECEIVE_DRIVE_DATA, ref scip, Marshal.SizeOf(scip), bSMARTAttribute, (Marshal.SizeOf(scop) + READ_ATTRIBUTE_BUFFER_SIZE - 1), ref bytRv, 0))
                    {
                        for (int i = 0; i < (NUM_ATTRIBUTE_STRUCTS - 1); i++)
                        {
                            if (bSMARTAttribute[i] > 0)
                            {
                                di.Attributes.AttrID = bSMARTAttribute[i];
                                di.Attributes.AttrName = GetSMARTAttrName(bSMARTAttribute[i]);
                                di.Attributes.AttrValue = bSMARTAttribute[3];
                                di.Attributes.WorstValue = bSMARTAttribute[4];
                                di.NumAttributes += 1;
                            }
                        }
                    }
 
                    // set the IDE registers to issue a threshold attribute read command
                    scip.bDriveNumber = (byte)Convert.ToInt32(DeviceName);
                    scip.cBufferSize = READ_THRESHOLD_BUFFER_SIZE;
                    scip.irDriveRegs.bFeaturesReg = SMART_READ_ATTRIBUTE_THRESHOLDS;
                    scip.irDriveRegs.bSectorCountReg = 1;
                    scip.irDriveRegs.bSectorNumberReg = 1;
                    scip.irDriveRegs.bCylLowReg = SMART_CYL_LOW;
                    scip.irDriveRegs.bCylHighReg = SMART_CYL_HIGH;
                    scip.irDriveRegs.bDriveHeadReg = (byte)(0xA0 | ((Convert.ToInt32(DeviceName) & 1) << 4));
                    scip.irDriveRegs.bCommandReg = IDE_EXECUTE_SMART_FUNCTION;
 
                    //int nBytes = Marshal.SizeOf(typeof(ATTR_DATA));
                    //IntPtr ptrLPOutBuffer = Marshal.AllocHGlobal(nBytes);
 
                    // if the call to DeviceIoControl is successful
                    if (DeviceIoControl(handle, DFP_RECEIVE_DRIVE_DATA, ref scip, (Marshal.SizeOf(scip) - 1), bSMARTThreshold, OUTPUT_DATA_SIZE + READ_THRESHOLD_BUFFER_SIZE - 1, ref bytRv, 0))
                    {
                        // have not gotten this far yet
                    }
 
                    return s.ToString();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return string.Empty;
            }
        }
 
 
        private string GetSMARTAttrName(byte wID)
        {
            switch (wID)
            {
                case 0x1:
                    return "Raw Read Error Rate";
                case 0x2:
                    return "Throughput Performance";
                case 0x3:
                    return "Spin Up Time";
                case 0x4:
                    return "Start/Stop Count";
                case 0x5:
                    return "Reallocated Sector Count";
                case 0x6:
                    return "Read Channel Margin";
                case 0x7:
                    return "Seek Error Rate";
                case 0x8:
                    return "Seek Time Performance";
                case 0x9:
                    return "Power On Hours Count";
                case 0x0A:
                    return "Spin Retry Count";
                case 0x0B:
                    return "Calibration Retry Count";
                case 0x0C:
                    return "Power Cycle Count";
                case 0xC2:
                    return "Temperature";
                case 0xC5:
                    return "Current Pending Sector Count";
                case 0xC6:
                    return "Off-line Scan Incorrect. Sector Count";
                case 0xC7:
                    return "Ultra ATA CRC Error Count";
                case 0xC8:
                    return "Write Error Count";
                default:
                    return "Unknown";
            }
        }
 
        private static void ChangeByteOrder(byte[] charArray)
        {
            // bytes
            byte temp;
 
            try
            {
                for (int i = 0; i < charArray.Length; i += 2)
                {
                    temp = charArray[i];
                    charArray[i] = charArray[i + 1];
                    charArray[i + 1] = temp;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {
                // clean up memory
                temp = 0x00;
            }
        }   

Open in new window

0
YeavisAuthor Commented:
Where you see the variable DeviceName, just replace it with 0 or 1 (depending on how many hard drives your system has)
0
JimBrandleyCommented:
Thanks.

Jim
0
JimBrandleyCommented:
I finally got it running. There is data coming back (see below). The problem is in how you are running the loop to read it here:

// The loop will read the first 30 bytes only, not the entire byte array.
// Next problem, it's incrementing by 1, not the number of bytes in an attribute.
for (int i = 0; i < (NUM_ATTRIBUTE_STRUCTS - 1); i++)
{
   if (bSMARTAttribute[i] > 0)
   {
      di.Attributes.AttrID = bSMARTAttribute[i];
      di.Attributes.AttrName = GetSMARTAttrName(bSMARTAttribute[i]);
      di.Attributes.AttrValue = bSMARTAttribute[3];   // <-- Always looks in the same location - should be offset from attribute start
      di.Attributes.WorstValue = bSMARTAttribute[4];  // <-- Always looks in the same location - should be offset from attribute start
      di.NumAttributes += 1;
   }
}

Jim

bSMARTAttribute   {Dimensions:[527]}   byte[]
[0]   0  byte
[1]   2  byte
[2]   0  byte
[3]   0  byte
[4]   208   byte
[5]   1  byte
[6]   1  byte
[7]   79 byte
[8]   194   byte
[9]   160   byte
[10]  176   byte
[11]  0  byte
[12]  0  byte
[13]  0  byte
[14]  0  byte
[15]  0  byte
[16]  16 byte
[17]  0  byte
[18]  1  byte
[19]  11 byte
[20]  0  byte
[21]  200   byte
[22]  200   byte
[23]  0  byte
[24]  0  byte
[25]  0  byte
[26]  0  byte
[27]  0  byte
[28]  0  byte
[29]  0  byte
[30]  3  byte
[31]  7  byte
[32]  0  byte
[33]  121   byte
[34]  119   byte
[35]  91 byte
[36]  25 byte
[37]  0  byte
[38]  0  byte
[39]  0  byte
[40]  0  byte
[41]  0  byte
[42]  4  byte
[43]  50 byte
[44]  0  byte
[45]  100   byte
[46]  100   byte
[47]  76 byte
[48]  0  byte
[49]  0  byte
[50]  0  byte
[51]  0  byte
[52]  0  byte
[53]  0  byte
[54]  5  byte
[55]  51 byte
[56]  0  byte
[57]  200   byte
[58]  200   byte
[59]  0  byte
[60]  0  byte
[61]  0  byte
[62]  0  byte
[63]  0  byte
[64]  0  byte
[65]  0  byte
[66]  7  byte
[67]  11 byte
[68]  0  byte
[69]  200   byte
[70]  200   byte
[71]  0  byte
[72]  0  byte
[73]  0  byte
[74]  0  byte
[75]  0  byte
[76]  0  byte
[77]  0  byte
[78]  9  byte
[79]  50 byte
[80]  0  byte
[81]  69 byte
[82]  69 byte
[83]  161   byte
[84]  88 byte
[85]  0  byte
[86]  0  byte
[87]  0  byte
[88]  0  byte
[89]  0  byte
[90]  10 byte
[91]  19 byte
[92]  0  byte
[93]  100   byte
[94]  253   byte
[95]  0  byte
[96]  0  byte
[97]  0  byte
[98]  0  byte
[99]  0  byte
[100] 0  byte
[101] 0  byte
[102] 11 byte
[103] 19 byte
[104] 0  byte
[105] 100   byte
[106] 253   byte
[107] 0  byte
[108] 0  byte
[109] 0  byte
[110] 0  byte
[111] 0  byte
[112] 0  byte
[113] 0  byte
[114] 12 byte
[115] 50 byte
[116] 0  byte
[117] 100   byte
[118] 100   byte
[119] 76 byte
[120] 0  byte
[121] 0  byte
[122] 0  byte
[123] 0  byte
[124] 0  byte
[125] 0  byte
[126] 194   byte
[127] 34 byte
[128] 0  byte
[129] 115   byte
[130] 95 byte
[131] 35 byte
[132] 0  byte
[133] 0  byte
[134] 0  byte
[135] 0  byte
[136] 0  byte
[137] 0  byte
[138] 196   byte
[139] 50 byte
[140] 0  byte
[141] 200   byte
[142] 200   byte
[143] 0  byte
[144] 0  byte
[145] 0  byte
[146] 0  byte
[147] 0  byte
[148] 0  byte
[149] 0  byte
[150] 197   byte
[151] 18 byte
[152] 0  byte
[153] 200   byte
[154] 200   byte
[155] 0  byte
[156] 0  byte
[157] 0  byte
[158] 0  byte
[159] 0  byte
[160] 0  byte
[161] 0  byte
[162] 198   byte
[163] 18 byte
[164] 0  byte
[165] 200   byte
[166] 200   byte
[167] 0  byte
[168] 0  byte
[169] 0  byte
[170] 0  byte
[171] 0  byte
[172] 0  byte
[173] 0  byte
[174] 199   byte
[175] 10 byte
[176] 0  byte
[177] 200   byte
[178] 253   byte
[179] 0  byte
[180] 0  byte
[181] 0  byte
[182] 0  byte
[183] 0  byte
[184] 0  byte
[185] 0  byte
[186] 200   byte
[187] 9  byte
[188] 0  byte
[189] 200   byte
[190] 200   byte
[191] 0  byte
[192] 0  byte
[193] 0  byte
[194] 0  byte
[195] 0  byte
[196] 0  byte
[197] 0  byte
[198] 0  byte
[199] 0  byte
[200] 0  byte
[201] 0  byte
[202] 0  byte
[203] 0  byte
[204] 0  byte
[205] 0  byte
[206] 0  byte
[207] 0  byte
[208] 0  byte
[209] 0  byte
[210] 0  byte
[211] 0  byte
[212] 0  byte
[213] 0  byte
[214] 0  byte
[215] 0  byte
[216] 0  byte
[217] 0  byte
[218] 0  byte
[219] 0  byte
[220] 0  byte
[221] 0  byte
[222] 0  byte
[223] 0  byte
[224] 0  byte
[225] 0  byte
[226] 0  byte
[227] 0  byte
[228] 0  byte
[229] 0  byte
[230] 0  byte
[231] 0  byte
[232] 0  byte
[233] 0  byte
[234] 0  byte
[235] 0  byte
[236] 0  byte
[237] 0  byte
[238] 0  byte
[239] 0  byte
[240] 0  byte
[241] 0  byte
[242] 0  byte
[243] 0  byte
[244] 0  byte
[245] 0  byte
[246] 0  byte
[247] 0  byte
[248] 0  byte
[249] 0  byte
[250] 0  byte
[251] 0  byte
[252] 0  byte
[253] 0  byte
[254] 0  byte
[255] 0  byte
[256] 0  byte
[257] 0  byte
[258] 0  byte
[259] 0  byte
[260] 0  byte
[261] 0  byte
[262] 0  byte
[263] 0  byte
[264] 0  byte
[265] 0  byte
[266] 0  byte
[267] 0  byte
[268] 0  byte
[269] 0  byte
[270] 0  byte
[271] 0  byte
[272] 0  byte
[273] 0  byte
[274] 0  byte
[275] 0  byte
[276] 0  byte
[277] 0  byte
[278] 0  byte
[279] 0  byte
[280] 0  byte
[281] 0  byte
[282] 0  byte
[283] 0  byte
[284] 0  byte
[285] 0  byte
[286] 0  byte
[287] 0  byte
[288] 0  byte
[289] 0  byte
[290] 0  byte
[291] 0  byte
[292] 0  byte
[293] 0  byte
[294] 0  byte
[295] 0  byte
[296] 0  byte
[297] 0  byte
[298] 0  byte
[299] 0  byte
[300] 0  byte
[301] 0  byte
[302] 0  byte
[303] 0  byte
[304] 0  byte
[305] 0  byte
[306] 0  byte
[307] 0  byte
[308] 0  byte
[309] 0  byte
[310] 0  byte
[311] 0  byte
[312] 0  byte
[313] 0  byte
[314] 0  byte
[315] 0  byte
[316] 0  byte
[317] 0  byte
[318] 0  byte
[319] 0  byte
[320] 0  byte
[321] 0  byte
[322] 0  byte
[323] 0  byte
[324] 0  byte
[325] 0  byte
[326] 0  byte
[327] 0  byte
[328] 0  byte
[329] 0  byte
[330] 0  byte
[331] 0  byte
[332] 0  byte
[333] 0  byte
[334] 0  byte
[335] 0  byte
[336] 0  byte
[337] 0  byte
[338] 0  byte
[339] 0  byte
[340] 0  byte
[341] 0  byte
[342] 0  byte
[343] 0  byte
[344] 0  byte
[345] 0  byte
[346] 0  byte
[347] 0  byte
[348] 0  byte
[349] 0  byte
[350] 0  byte
[351] 0  byte
[352] 0  byte
[353] 0  byte
[354] 0  byte
[355] 0  byte
[356] 0  byte
[357] 0  byte
[358] 0  byte
[359] 0  byte
[360] 0  byte
[361] 0  byte
[362] 0  byte
[363] 0  byte
[364] 0  byte
[365] 0  byte
[366] 0  byte
[367] 0  byte
[368] 0  byte
[369] 0  byte
[370] 0  byte
[371] 0  byte
[372] 0  byte
[373] 0  byte
[374] 0  byte
[375] 0  byte
[376] 0  byte
[377] 0  byte
[378] 132   byte
[379] 0  byte
[380] 125   byte
[381] 28 byte
[382] 1  byte
[383] 123   byte
[384] 3  byte
[385] 0  byte
[386] 1  byte
[387] 0  byte
[388] 2  byte
[389] 92 byte
[390] 5  byte
[391] 0  byte
[392] 0  byte
[393] 0  byte
[394] 0  byte
[395] 0  byte
[396] 0  byte
[397] 0  byte
[398] 0  byte
[399] 0  byte
[400] 0  byte
[401] 0  byte
[402] 1  byte
[403] 6  byte
 
(All remaining bytes are zero)

Open in new window

0
JimBrandleyCommented:
I just looked at the values in the byte array. If temperature is 231, my drive elected not to return it. The 231 code came from a Seagate Specification, mine are Western Digital. That would say that codes are vendor-specific, or mine doesn't return temperature.

Jim
0
YeavisAuthor Commented:
This is where I'm getting confused I think. The NUM_ATTRIBUTES_STRUCTS variable made me think the data would be stored in 30 different structs (1 for each attribute). Since I'm getting the same data as you are, I'm assuming that I'm at least in the right place. However, I'm totally lost on how to get each attribute name, value, and worst value. I have no idea what the offset should be for each attribute...
0
JimBrandleyCommented:
I have not been able to locate anything that describes the number of bytes assigned to an attribute. Have you?

Jim
0
YeavisAuthor Commented:
Maybe this will help... here's the C++ code I have been porting to C#.
stCIP.cBufferSize=READ_ATTRIBUTE_BUFFER_SIZE;
	stCIP.bDriveNumber =ucDriveIndex;
	stCIP.irDriveRegs.bFeaturesReg=READ_ATTRIBUTES;
	stCIP.irDriveRegs.bSectorCountReg = 1;
	stCIP.irDriveRegs.bSectorNumberReg = 1;
	stCIP.irDriveRegs.bCylLowReg = SMART_CYL_LOW;
	stCIP.irDriveRegs.bCylHighReg = SMART_CYL_HI;
	stCIP.irDriveRegs.bDriveHeadReg = DRIVE_HEAD_REG;
	stCIP.irDriveRegs.bCommandReg = SMART_CMD;
	bRet=DeviceIoControl(hDevice,SMART_RCV_DRIVE_DATA,&stCIP,sizeof(stCIP),szAttributes,sizeof(ST_ATAOUTPARAM) + READ_ATTRIBUTE_BUFFER_SIZE - 1,&dwRet,NULL);
	if(bRet)
	{
		m_stDrivesInfo[ucDriveIndex].m_ucSmartValues=0;
		m_stDrivesInfo[ucDriveIndex].m_ucDriveIndex=ucDriveIndex;
		pT1=(PBYTE)(((ST_ATAOUTPARAM*)szAttributes)->bBuffer);
		for(ucT1=0;ucT1<30;++ucT1)
		{
			pT3=&pT1[2+ucT1*12];
			pT2=(PDWORD)&pT3[INDEX_ATTRIB_RAW];
			pT3[INDEX_ATTRIB_RAW+2]=pT3[INDEX_ATTRIB_RAW+3]=pT3[INDEX_ATTRIB_RAW+4]=pT3[INDEX_ATTRIB_RAW+5]=pT3[INDEX_ATTRIB_RAW+6]=0;
			if(pT3[INDEX_ATTRIB_INDEX]!=0)
			{
				pSmartValues=&m_stDrivesInfo[ucDriveIndex].m_stSmartInfo[m_stDrivesInfo[ucDriveIndex].m_ucSmartValues];
				pSmartValues->m_ucAttribIndex=pT3[INDEX_ATTRIB_INDEX];
				pSmartValues->m_ucValue=pT3[INDEX_ATTRIB_VALUE];
				pSmartValues->m_ucWorst=pT3[INDEX_ATTRIB_WORST];
				pSmartValues->m_dwAttribValue=pT2[0];
				pSmartValues->m_dwThreshold=MAXDWORD;
				m_oSmartInfo[MAKELPARAM(pSmartValues->m_ucAttribIndex,ucDriveIndex)]=pSmartValues;
				m_stDrivesInfo[ucDriveIndex].m_ucSmartValues++;
			}
		}
	}
	else
		dwRet=GetLastError();
 
	stCIP.irDriveRegs.bFeaturesReg=READ_THRESHOLDS;
	stCIP.cBufferSize=READ_THRESHOLD_BUFFER_SIZE; // Is same as attrib size
	bRet=DeviceIoControl(hDevice,SMART_RCV_DRIVE_DATA,&stCIP,sizeof(stCIP),szAttributes,sizeof(ST_ATAOUTPARAM) + READ_ATTRIBUTE_BUFFER_SIZE - 1,&dwRet,NULL);
	if(bRet)
	{
		pT1=(PBYTE)(((ST_ATAOUTPARAM*)szAttributes)->bBuffer);
		for(ucT1=0;ucT1<30;++ucT1)
		{
			pT2=(PDWORD)&pT1[2+ucT1*12+5];
			pT3=&pT1[2+ucT1*12];
			pT3[INDEX_ATTRIB_RAW+2]=pT3[INDEX_ATTRIB_RAW+3]=pT3[INDEX_ATTRIB_RAW+4]=pT3[INDEX_ATTRIB_RAW+5]=pT3[INDEX_ATTRIB_RAW+6]=0;
			if(pT3[0]!=0)
			{
				pSmartValues=GetSMARTValue(ucDriveIndex,pT3[0]);
				if(pSmartValues)
					pSmartValues->m_dwThreshold=pT2[0];
			}
		}
	}
	return bRet;
}

Open in new window

0
JimBrandleyCommented:
I found that code on the web Saturday - was just now looking at it...

Jim
0
JimBrandleyCommented:
I just found two things in SmartReader.h in that project that should help:

This tells us how many elements exist in an attribute:

#define INDEX_ATTRIB_INDEX                         0
#define INDEX_ATTRIB_UNKNOWN1                      1
#define INDEX_ATTRIB_UNKNOWN2                      2
#define INDEX_ATTRIB_VALUE                         3
#define INDEX_ATTRIB_WORST                         4
#define INDEX_ATTRIB_RAW                           5

And this tells us the size:
typedef struct
{
   BYTE m_ucAttribIndex;
   DWORD m_dwAttribValue;
   BYTE m_ucValue;
   BYTE m_ucWorst;
   DWORD m_dwThreshold;
}ST_SMART_INFO;

I would assume unknown1 and unknown2 are bytes until proven otherwise.

Jim
0
YeavisAuthor Commented:
Cool, looks like you're looking at the same C++ code I am. So I have the ST_SMART_INFO struct in my code already, granted it's called DRIVE_INFO but the structure is identical. No, I have not found anything that tells me how many bytes each attribute should be but I have found these links:

VB Smart info
http://www.dbforums.com/t876711.html 

Attributes Definition
http://smartlinux.sourceforge.net/smart/attributes.php

0
JimBrandleyCommented:
I'll have to let you work this one for a while. I have to get to work - My job pays the mortgage. I will take a look again this evening.

Jim
0
YeavisAuthor Commented:
Sounds good. Thanks for your help...
0
YeavisAuthor Commented:
I got this figured out on my own. Every attribute is 12 bytes long, and they start at byte 18. Thanks for putting in the time to attempt to help me. I finally came across a document that told me this information and now it all makes sense and my application is working great! Take care...
0
JimBrandleyCommented:
Glad you got it. I played with it some, had the 12 bytes OK, but could not figure out where it started.

Jim
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
YeavisAuthor Commented:
I'll give you the points because you spent so much time trying to help me. Take care...
0
JimBrandleyCommented:
Thanks. Good luck.

Jim
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C#

From novice to tech pro — start learning today.