SetupDi.cs

Gather Information from the Device Setup or Information Class

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
public class SetupDi
{
    /// <summary>
    ///     Class Names (Incomplete??)
    /// </summary>
    public string[] ClassNames =
    {
        "AudioEndpoint",
        "CDROM",
        "Computer",
        "DiskDrive",
        "Display",
        "DriverInterface",
        "HDC",
        "HIDClass",
        "Keyboard",
        "MEDIA",
        "Monitor",
        "Mouse",
        "Net",
        "PrintQueue",
        "Processor",
        "SCSIAdapter",
        "Sensor",
        "SoftwareDevice",
        "System",
        "USB",
        "Volume",
        "VolumeSnapshot",
        "WPD"
    };
    /// <summary>
    ///     Are we running using 64 or 32 bits
    /// </summary>
    public bool Is64Bit => IntPtr.Size == 8;
    /// <summary>
    ///     32 and 64 bit operations require differing offsets within the pointer return values
    /// </summary>
    public uint OffsetSize => Is64Bit ? 8u : 6u;
    /// <summary>
    ///     Get a list of active drives on the PC.
    /// </summary>
    public IEnumerable<string> GetDriveReadyList => from driveInfo in DriveInfo.GetDrives() where driveInfo.IsReady select driveInfo.Name;
    /// <summary>
    ///     Get a list of drive: letters and associated MountPoints for active drives.
    /// </summary>
    public Dictionary<string, string> LogicalDrives
    {
        get
        {
            var _logicalDrives = new Dictionary<string, string>();
            foreach (var drive in GetDriveReadyList)
            {
                var sb = new StringBuilder(1024);
                if (NativeWin32.GetVolumeNameForVolumeMountPoint(drive, sb, sb.Capacity))
                    _logicalDrives.Add(drive.Replace("\\", ""), sb.ToString());
            }
            return _logicalDrives;
        }
    }
    /// <summary>
    ///     Get a list of Drive: -> Disk numbers and associated length and offset information
    /// </summary>
    public Dictionary<string, List<NativeWin32.DISK_EXTENT>> DiskNumbers
    {
        get
        {
            var _diskNumbers = new Dictionary<string, List<NativeWin32.DISK_EXTENT>>();
            foreach (var ld in LogicalDrives)
            {
                var dkexts = new List<NativeWin32.DISK_EXTENT>();
                var hFile  = NativeWin32.CreateFile(@"\\.\" + ld.Key, NativeWin32.GENERIC_READ, NativeWin32.FILE_SHARE_READ | NativeWin32.FILE_SHARE_WRITE, IntPtr.Zero, NativeWin32.OPEN_EXISTING, 0, IntPtr.Zero);
                if (hFile == (IntPtr) NativeWin32.INVALID_HANDLE_VALUE)
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                var size          = 1024;
                var buffer        = Marshal.AllocHGlobal(size);
                var alloced       = buffer;
                var bytesReturned = 0;
                try
                {
                    if (!NativeWin32.DeviceIoControl(hFile, NativeWin32.IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, IntPtr.Zero, 0, buffer, size, out bytesReturned, IntPtr.Zero))
                    {
                    }
                }
                catch (Exception e)
                {
                    var m = e.Message;
                }
                finally
                {
                    NativeWin32.CloseHandle(hFile);
                }
                if (bytesReturned > 0)
                {
                    var vde     = new NativeWin32.VOLUME_DISK_EXTENTS();
                    var dextent = new NativeWin32.DISK_EXTENT();
                    Marshal.PtrToStructure(buffer, vde);
                    buffer = new IntPtr(buffer.ToInt64() + Marshal.SizeOf(vde));
                    for (var i = 0; i < vde.NumberOfDiskExtents; i++)
                    {
                        dextent = (NativeWin32.DISK_EXTENT) Marshal.PtrToStructure(buffer, typeof(NativeWin32.DISK_EXTENT));
                        dkexts.Add(dextent);
                        buffer = new IntPtr(buffer.ToInt64() + Marshal.SizeOf(dextent));
                    }
                }
                Marshal.FreeHGlobal(alloced);
                _diskNumbers.Add(ld.Key, dkexts);
            }
            return _diskNumbers;
        }
    }
    /// <summary>
    ///     Associates a drive letter to its physical disk numbers using SetupDi API functions.
    /// </summary>
    /// <returns></returns>
    public Dictionary<string, VOLUME_INFO> GetVolumePaths()
    {
        var _volumepaths = new Dictionary<string, VOLUME_INFO>();
        var classGuid    = NativeWin32.GUID_DEVINTERFACE.GUID_DEVINTERFACE_VOLUME;
        var hDevInfo     = NativeWin32.SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, NativeWin32.DICFG.DEVICEINTERFACE | NativeWin32.DICFG.PRESENT);
        if (hDevInfo == (IntPtr) NativeWin32.INVALID_HANDLE_VALUE)
            throw new Exception("read hardware information error");
        var devIndex = 0u;
        do
        {
            var dia = new NativeWin32.SP_DEVICE_INTERFACE_DATA();
            dia.cbSize = (uint) Marshal.SizeOf(typeof(NativeWin32.SP_DEVICE_INTERFACE_DATA));
            if (NativeWin32.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, classGuid, devIndex, ref dia))
            {
                var didd = new NativeWin32.SP_DEVICE_INTERFACE_DETAIL_DATA();
                didd.cbSize = OffsetSize;
                var devInfoData = new NativeWin32.SP_DEVINFO_DATA();
                devInfoData.cbSize = (uint) Marshal.SizeOf(typeof(NativeWin32.SP_DEVINFO_DATA));
                uint nRequiredSize = 0;
                uint nBytes        = 1024;
                if (NativeWin32.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref dia, ref didd, nBytes, ref nRequiredSize, ref devInfoData))
                {
                    var sb = new StringBuilder(1024);
                    if (NativeWin32.GetVolumeNameForVolumeMountPoint(didd.devicePath + @"\", sb, sb.Capacity))
                    {
                        var cv = sb.ToString();
                        var di = new VOLUME_INFO();
                        foreach (var V in LogicalDrives)
                            if (V.Value.IndexOf(cv, StringComparison.OrdinalIgnoreCase) != -1)
                            {
                                di.Drive            = V.Key;
                                di.VolumeMountPoint = cv;
                                di.DevicePath       = didd.devicePath;
                                foreach (var de in DiskNumbers[V.Key])
                                {
                                    di.DiskNumbers.Add(de.DiskNumber);
                                    di.ExtentLengths.Add(de.ExtentLength);
                                    di.StartingOffsets.Add(de.StartingOffset);
                                }
                                _volumepaths.Add(di.Drive.Trim(), di);
                                break;
                            }
                    }
                }
                else
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            else
            {
                break;
            }
            devIndex++;
        } while (true);
        NativeWin32.SetupDiDestroyDeviceInfoList(hDevInfo);
        return _volumepaths;
    }
    /// <summary>
    ///     Retrieves a list of All devices installed starting at the base root
    /// </summary>
    public Dictionary<string, DEVICE_INFO> GetAllGUIDs()
    {
        var DevList   = new Dictionary<string, DEVICE_INFO>();
        var classGuid = Guid.Empty;
        var hDevInfo  = NativeWin32.SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, NativeWin32.DICFG.ALLCLASSES | NativeWin32.DICFG.PRESENT);
        if (hDevInfo == (IntPtr) NativeWin32.INVALID_HANDLE_VALUE)
            throw new Exception("read hardware information error");
        var devIndex = 0;
        do
        {
            var devInfoData = new NativeWin32.SP_DEVINFO_DATA();
            devInfoData.cbSize    = (uint) Marshal.SizeOf(typeof(NativeWin32.SP_DEVINFO_DATA));
            devInfoData.classGuid = Guid.Empty;
            devInfoData.devInst   = 0;
            devInfoData.reserved  = UIntPtr.Zero;
            if (!NativeWin32.SetupDiEnumDeviceInfo(hDevInfo, devIndex, ref devInfoData))
                break;
            var di = new DEVICE_INFO();
            di.Guid            = $"{devInfoData.classGuid}";
            di.ClassName       = $"{GetClassNameFromGuid(devInfoData.classGuid)}";
            di.Description     = $"{GetClassDescriptionFromGuid(devInfoData.classGuid)}";
            di.InstanceID      = $"{GetDeviceInstanceId(hDevInfo, devInfoData)}";
            di.DI_Capabilities = (NativeWin32.DeviceCapabilities) GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.Capabilities, 0);
            di.FriendlyName    = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.FriendlyName,   null);
            di.ClassGuid       = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.ClassGuid,      null);
            di.HardwareId      = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.HardwareId,     null);
            di.Mfg             = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.Mfg,            null);
            di.EnumeratorName  = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.EnumeratorName, null);
            if (!DevList.ContainsValue(di))
                DevList.Add($"{devIndex}", di);
            devIndex++;
        } while (true);
        NativeWin32.SetupDiDestroyDeviceInfoList(hDevInfo);
        return DevList;
    }
    /// <summary>
    ///     Get a list of all devices starting a a specified Guid base.
    /// </summary>
    public IEnumerable<DEVICE_INFO> GetGUIDGroup(string ClassName)
    {
        if (ClassNames.All(c => c.IndexOf(ClassName, StringComparison.OrdinalIgnoreCase) == -1))
            throw new Exception("ClassName is Invalid.");
        var dl = GetAllGUIDs();
        return dl.Values.Where(d => d.ClassName.IndexOf(ClassName, StringComparison.OrdinalIgnoreCase) != -1);
    }
    /// <summary>
    ///     property is SPDRP type
    /// </summary>
    private static uint GetProperty(IntPtr deviceInfoSet, NativeWin32.SP_DEVINFO_DATA devInfoData, uint property, uint defaultValue)
    {
        var propertyRegDataType = 0u;
        var requiredSize        = IntPtr.Zero;
        var propertyBufferSize  = (uint) Marshal.SizeOf(typeof(uint));
        var propertyBuffer      = Marshal.AllocHGlobal((int) propertyBufferSize);
        if (!NativeWin32.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfoData, property, propertyRegDataType, propertyBuffer, propertyBufferSize, requiredSize))
        {
            var error = Marshal.GetLastWin32Error();
            if (error != NativeWin32.ERROR_INVALID_DATA)
                throw new Win32Exception(error);
            return defaultValue;
        }
        var value = (uint) Marshal.PtrToStructure(propertyBuffer, typeof(uint));
        Marshal.FreeHGlobal(propertyBuffer);
        return value;
    }
    /// <summary>
    ///     property is SPDRP type
    /// </summary>
    private Guid GetProperty(IntPtr deviceInfoSet, NativeWin32.SP_DEVINFO_DATA devData, uint property, Guid defaultValue)
    {
        var propertyRegDataType = 0u;
        var requiredSize        = IntPtr.Zero;
        var propertyBufferSize  = (uint) Marshal.SizeOf(typeof(Guid));
        var propertyBuffer      = Marshal.AllocHGlobal((int) propertyBufferSize);
        if (!NativeWin32.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devData, property, propertyRegDataType, propertyBuffer, propertyBufferSize, requiredSize))
        {
            Marshal.FreeHGlobal(propertyBuffer);
            var error = Marshal.GetLastWin32Error();
            if (error != NativeWin32.ERROR_INVALID_DATA)
                throw new Win32Exception(error);
            return defaultValue;
        }
        var value = (Guid) Marshal.PtrToStructure(propertyBuffer, typeof(Guid));
        Marshal.FreeHGlobal(propertyBuffer);
        return value;
    }
    /// <summary>
    ///     property is SPDRP type
    /// </summary>
    private string GetProperty(IntPtr deviceInfoSet, NativeWin32.SP_DEVINFO_DATA devInfoData, uint property, string defaultValue)
    {
        var propertyRegDataType = 0u;
        var requiredSize        = IntPtr.Zero;
        var propertyBufferSize  = 1024u;
        var propertyBuffer      = new StringBuilder((int) propertyBufferSize);
        if (!NativeWin32.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfoData, property, propertyRegDataType, propertyBuffer, propertyBufferSize, requiredSize))
        {
            var error = Marshal.GetLastWin32Error();
            if (error != NativeWin32.ERROR_INVALID_DATA)
                throw new Win32Exception(error);
            return defaultValue;
        }
        return propertyBuffer.ToString();
    }
    public string GetClassNameFromGuid(Guid guid)
    {
        var result        = string.Empty;
        var className     = new StringBuilder();
        var iRequiredSize = 0;
        var iSize         = 0;
        var b             = NativeWin32.SetupDiClassNameFromGuid(ref guid, className, iSize, ref iRequiredSize);
        className = new StringBuilder(iRequiredSize);
        iSize     = iRequiredSize;
        b         = NativeWin32.SetupDiClassNameFromGuid(ref guid, className, iSize, ref iRequiredSize);
        if (b)
            result = className.ToString();
        return result;
    }
    public string GetClassDescriptionFromGuid(Guid guid)
    {
        var result        = string.Empty;
        var classDesc     = new StringBuilder(0);
        var iRequiredSize = 0;
        var iSize         = 0;
        var b             = NativeWin32.SetupDiGetClassDescription(ref guid, classDesc, iSize, ref iRequiredSize);
        classDesc = new StringBuilder(iRequiredSize);
        iSize     = iRequiredSize;
        b         = NativeWin32.SetupDiGetClassDescription(ref guid, classDesc, iSize, ref iRequiredSize);
        if (b)
            result = classDesc.ToString();
        return result;
    }
    public string GetDeviceInstanceId(IntPtr DeviceInfoSet, NativeWin32.SP_DEVINFO_DATA DeviceInfoData)
    {
        var result        = string.Empty;
        var id            = new StringBuilder(0);
        var iRequiredSize = 0;
        var iSize         = 0;
        var b             = NativeWin32.SetupDiGetDeviceInstanceId(DeviceInfoSet, ref DeviceInfoData, id, iSize, ref iRequiredSize);
        id    = new StringBuilder(iRequiredSize);
        iSize = iRequiredSize;
        b     = NativeWin32.SetupDiGetDeviceInstanceId(DeviceInfoSet, ref DeviceInfoData, id, iSize, ref iRequiredSize);
        if (b)
            result = id.ToString();
        return result;
    }
    public class VOLUME_INFO
    {
        public string      DevicePath;
        public List<uint>  DiskNumbers = new List<uint>();
        public string      Drive;
        public List<ulong> ExtentLengths   = new List<ulong>();
        public List<ulong> StartingOffsets = new List<ulong>();
        public string      VolumeMountPoint;
    }
    public struct DEVICE_INFO
    {
        public string                         Guid;
        public string                         ClassName;
        public string                         Description;
        public string                         InstanceID;
        public NativeWin32.DeviceCapabilities DI_Capabilities;
        public string                         FriendlyName;
        public string                         ClassGuid;
        public string                         HardwareId;
        public string                         Mfg;
        public string                         EnumeratorName;
        public string                         Drive;
        public string                         VolumePath;
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *