Added support for "list-devices" command on Windows.

This commit is contained in:
2017-08-22 03:40:43 +01:00
parent dbdcad4ddd
commit 3e02dd82ea
6 changed files with 282 additions and 12 deletions

View File

@@ -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:

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -33,6 +33,7 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Management" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
@@ -41,6 +42,7 @@
<Compile Include="Linux\Enums.cs" />
<Compile Include="Enums.cs" />
<Compile Include="Windows\Extern.cs" />
<Compile Include="Windows\ListDevices.cs" />
<Compile Include="Windows\Structs.cs" />
<Compile Include="Windows\Enums.cs" />
<Compile Include="Windows\Command.cs" />
@@ -84,15 +86,7 @@
<Compile Include="Linux\ListDevices.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<Folder Include="Linux\" />
<Folder Include="Windows\" />
<Folder Include="Device\" />
<Folder Include="Device\ScsiCommands\" />
<Folder Include="Device\AtaCommands\" />
<Folder Include="FreeBSD\" />
<Folder Include="Device\MmcCommands\" />
</ItemGroup>
<ItemGroup />
<ProjectExtensions>
<MonoDevelop>
<Properties>

View File

@@ -268,7 +268,8 @@ namespace DiscImageChef.Devices.Windows
/// <summary>
/// ScsiGetAddress
/// </summary>
IOCTL_SCSI_GET_ADDRESS = 0x41018
IOCTL_SCSI_GET_ADDRESS = 0x41018,
IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400,
}
[Flags]
@@ -299,4 +300,52 @@ namespace DiscImageChef.Devices.Windows
/// </summary>
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,
}
}

View File

@@ -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);
}

View File

@@ -0,0 +1,176 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : ListDevices.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// 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 <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// 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<string> DeviceIDs = new List<string>();
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<DeviceInfo> dev_list = new List<DeviceInfo>();
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;
}
}
}

View File

@@ -64,6 +64,7 @@ namespace DiscImageChef.Devices.Windows
public byte[] SenseBuf;
}
[StructLayout(LayoutKind.Sequential)]
struct AtaPassThroughDirect
{
/// <summary>
@@ -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;
}
}