diff --git a/DiscImageChef.Devices/Device/List.cs b/DiscImageChef.Devices/Device/List.cs index 4c0332172..39d265806 100644 --- a/DiscImageChef.Devices/Device/List.cs +++ b/DiscImageChef.Devices/Device/List.cs @@ -50,7 +50,7 @@ namespace DiscImageChef.Devices switch(Interop.DetectOS.GetRealPlatformID()) { case Interop.PlatformID.Win32NT: - throw new NotImplementedException(); + return Windows.ListDevices.GetList(); case Interop.PlatformID.Linux: return Linux.ListDevices.GetList(); default: diff --git a/DiscImageChef.Devices/DiscImageChef.Devices.csproj b/DiscImageChef.Devices/DiscImageChef.Devices.csproj index a8b49d8c0..a8d1685c3 100644 --- a/DiscImageChef.Devices/DiscImageChef.Devices.csproj +++ b/DiscImageChef.Devices/DiscImageChef.Devices.csproj @@ -1,4 +1,4 @@ - + Debug @@ -33,6 +33,7 @@ + @@ -41,6 +42,7 @@ + @@ -84,15 +86,7 @@ - - - - - - - - - + diff --git a/DiscImageChef.Devices/Windows/Enums.cs b/DiscImageChef.Devices/Windows/Enums.cs index 60384a46b..ea39d0c62 100644 --- a/DiscImageChef.Devices/Windows/Enums.cs +++ b/DiscImageChef.Devices/Windows/Enums.cs @@ -268,7 +268,8 @@ namespace DiscImageChef.Devices.Windows /// /// ScsiGetAddress /// - IOCTL_SCSI_GET_ADDRESS = 0x41018 + IOCTL_SCSI_GET_ADDRESS = 0x41018, + IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400, } [Flags] @@ -299,4 +300,52 @@ namespace DiscImageChef.Devices.Windows /// NoMultiple = 0x20 } + + enum StoragePropertyId + { + Device = 0, + Adapter = 1, + Id = 2, + UniqueId = 3, + WriteCache = 4, + Miniport = 5, + AccessAlignment = 6, + SeekPenalty = 7, + Trim = 8, + WriteAggregation = 9, + Telemetry = 10, + LBProvisioning = 11, + Power = 12, + Copyoffload = 13, + Resiliency = 14 + } + + enum StorageQueryType + { + Standard = 0, + Exists = 1, + Mask = 2, + Max = 3 + } + + enum StorageBusType + { + Unknown = 0, + SCSI = 1, + ATAPI = 2, + ATA = 3, + FireWire = 4, + SSA = 5, + Fibre = 6, + USB = 7, + RAID = 8, + iSCSI = 9, + SAS = 0xA, + SATA = 0xB, + SecureDigital = 0xC, + MultiMediaCard = 0xD, + Virtual = 0xE, + FileBackedVirtual = 0xF, + NVMe = 0x11, + } } diff --git a/DiscImageChef.Devices/Windows/Extern.cs b/DiscImageChef.Devices/Windows/Extern.cs index d04e44085..f0fba3479 100644 --- a/DiscImageChef.Devices/Windows/Extern.cs +++ b/DiscImageChef.Devices/Windows/Extern.cs @@ -74,6 +74,18 @@ namespace DiscImageChef.Devices.Windows IntPtr Overlapped ); + [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] + internal static extern bool DeviceIoControlStorageQuery( + SafeFileHandle hDevice, + WindowsIoctl IoControlCode, + ref StoragePropertyQuery InBuffer, + uint nInBufferSize, + IntPtr OutBuffer, + uint nOutBufferSize, + ref uint pBytesReturned, + IntPtr Overlapped + ); + [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] internal static extern bool CloseHandle(SafeFileHandle hDevice); } diff --git a/DiscImageChef.Devices/Windows/ListDevices.cs b/DiscImageChef.Devices/Windows/ListDevices.cs new file mode 100644 index 000000000..759650ecc --- /dev/null +++ b/DiscImageChef.Devices/Windows/ListDevices.cs @@ -0,0 +1,176 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : ListDevices.cs +// Author(s) : Natalia Portillo +// +// Component : Component +// +// --[ Description ] ---------------------------------------------------------- +// +// Description +// +// --[ License ] -------------------------------------------------------------- +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2017 Natalia Portillo +// ****************************************************************************/ +using Microsoft.Win32.SafeHandles; +using System; +using System.Collections.Generic; +using System.IO; +using System.Management; +using System.Runtime.InteropServices; +using System.Text; + +namespace DiscImageChef.Devices.Windows +{ + public static class ListDevices + { + public static DeviceInfo[] GetList() + { + List DeviceIDs = new List(); + + try + { + ManagementObjectSearcher mgmtObjSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive"); + ManagementObjectCollection objCol = mgmtObjSearcher.Get(); + + foreach (ManagementObject drive in objCol) + DeviceIDs.Add((string)drive["DeviceID"]); + + mgmtObjSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_TapeDrive"); + objCol = mgmtObjSearcher.Get(); + + foreach (ManagementObject drive in objCol) + DeviceIDs.Add((string)drive["DeviceID"]); + + mgmtObjSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_CDROMDrive"); + objCol = mgmtObjSearcher.Get(); + + foreach (ManagementObject drive in objCol) + DeviceIDs.Add((string)drive["Drive"]); + } + catch(Exception ex) + { +#if DEBUG + throw ex; +#else + return null; +#endif + } + + List dev_list = new List(); + + foreach (string devId in DeviceIDs) + { + string physId = devId; + // TODO: This can be done better + if (devId.Length == 2 && devId[1] == ':') + physId = "\\\\?\\" + devId; + SafeFileHandle fd = Extern.CreateFile(physId, 0, FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.OpenExisting, 0, IntPtr.Zero); + if (fd.IsInvalid) + continue; + + StoragePropertyQuery query = new StoragePropertyQuery(); + query.PropertyId = StoragePropertyId.Device; + query.QueryType = StorageQueryType.Standard; + query.AdditionalParameters = new byte[1]; + + //StorageDeviceDescriptor descriptor = new StorageDeviceDescriptor(); + //descriptor.RawDeviceProperties = new byte[16384]; + + IntPtr descriptorPtr = Marshal.AllocHGlobal(1000); + byte[] descriptor_b = new byte[1000]; + + uint returned = 0; + int error = 0; + + bool hasError = Extern.DeviceIoControlStorageQuery(fd, WindowsIoctl.IOCTL_STORAGE_QUERY_PROPERTY, ref query, (uint)Marshal.SizeOf(query), descriptorPtr, 1000, ref returned, IntPtr.Zero); + + if (hasError) + error = Marshal.GetLastWin32Error(); + + Marshal.Copy(descriptorPtr, descriptor_b, 0, 1000); + + if (hasError && error != 0) + continue; + + StorageDeviceDescriptor descriptor = new StorageDeviceDescriptor(); + descriptor.Version = BitConverter.ToUInt32(descriptor_b, 0); + descriptor.Size = BitConverter.ToUInt32(descriptor_b, 4); + descriptor.DeviceType = descriptor_b[8]; + descriptor.DeviceTypeModifier = descriptor_b[9]; + descriptor.RemovableMedia = BitConverter.ToBoolean(descriptor_b, 10); + descriptor.CommandQueueing= BitConverter.ToBoolean(descriptor_b, 11); + descriptor.VendorIdOffset = BitConverter.ToUInt32(descriptor_b, 12); + descriptor.ProductIdOffset = BitConverter.ToUInt32(descriptor_b, 16); + descriptor.ProductRevisionOffset = BitConverter.ToUInt32(descriptor_b, 20); + descriptor.SerialNumberOffset = BitConverter.ToUInt32(descriptor_b, 24); + descriptor.BusType = (StorageBusType)BitConverter.ToUInt32(descriptor_b, 28); + descriptor.RawPropertiesLength = BitConverter.ToUInt32(descriptor_b, 32); + + DeviceInfo info = new DeviceInfo + { + path = physId, + bus = descriptor.BusType.ToString() + }; + + if (descriptor.VendorIdOffset > 0) + info.vendor = StringHandlers.CToString(descriptor_b, Encoding.ASCII, start: (int)descriptor.VendorIdOffset); + if (descriptor.ProductIdOffset > 0) + info.model = StringHandlers.CToString(descriptor_b, Encoding.ASCII, start: (int)descriptor.ProductIdOffset); + // TODO: Get serial number of SCSI and USB devices, probably also FireWire (untested) + if (descriptor.SerialNumberOffset > 0) + info.serial = StringHandlers.CToString(descriptor_b, Encoding.ASCII, start: (int)descriptor.SerialNumberOffset); + + if (string.IsNullOrEmpty(info.vendor) || info.vendor == "ATA") + { + string[] pieces = info.model.Split(' '); + if (pieces.Length > 1) + { + info.vendor = pieces[0]; + info.model = info.model.Substring(pieces[0].Length + 1); + } + } + + switch (descriptor.BusType) + { + case StorageBusType.SCSI: + case StorageBusType.ATAPI: + case StorageBusType.ATA: + case StorageBusType.FireWire: + case StorageBusType.SSA: + case StorageBusType.Fibre: + case StorageBusType.USB: + case StorageBusType.iSCSI: + case StorageBusType.SAS: + case StorageBusType.SATA: + info.supported = true; + break; + } + + Marshal.FreeHGlobal(descriptorPtr); + dev_list.Add(info); + } + + DeviceInfo[] devices = dev_list.ToArray(); + + return devices; + } + } +} diff --git a/DiscImageChef.Devices/Windows/Structs.cs b/DiscImageChef.Devices/Windows/Structs.cs index e637587a7..471fc6d0f 100644 --- a/DiscImageChef.Devices/Windows/Structs.cs +++ b/DiscImageChef.Devices/Windows/Structs.cs @@ -64,6 +64,7 @@ namespace DiscImageChef.Devices.Windows public byte[] SenseBuf; } + [StructLayout(LayoutKind.Sequential)] struct AtaPassThroughDirect { /// @@ -145,5 +146,43 @@ namespace DiscImageChef.Devices.Windows [FieldOffset(7)] public byte Reserved; } + + [StructLayout(LayoutKind.Sequential)] + struct StoragePropertyQuery + { + [MarshalAs(UnmanagedType.U4)] + public StoragePropertyId PropertyId; + [MarshalAs(UnmanagedType.U4)] + public StorageQueryType QueryType; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] + public byte[] AdditionalParameters; + } + + [StructLayout(LayoutKind.Sequential)] + struct StorageDescriptorHeader + { + public uint Version; + public uint Size; + } + + [StructLayout(LayoutKind.Sequential)] + struct StorageDeviceDescriptor + { + public uint Version; + public uint Size; + public byte DeviceType; + public byte DeviceTypeModifier; + [MarshalAs(UnmanagedType.U1)] + public bool RemovableMedia; + [MarshalAs(UnmanagedType.U1)] + public bool CommandQueueing; + public uint VendorIdOffset; + public uint ProductIdOffset; + public uint ProductRevisionOffset; + public uint SerialNumberOffset; + public StorageBusType BusType; + public uint RawPropertiesLength; + public byte[] RawDeviceProperties; + } }