我正在使用
DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &driveInfo, sizeof(driveInfo), &dwResult, NULL)
检查driveInfo.MediaType
是RemovableMedia
还是FixedMedia
,但似乎我的所有卷都被“看作”固定:
\\.\C: NTFS Fixed, this is ok - internal hard drive
\\.\D: NTFS Fixed, this is ok - internal hard drive
\\.\E: NTFS Fixed, this is ok - internal hard drive
\\.\F: NTFS Fixed, this is NOT ok, this is a USB external 2.5" hard drive
因此我的问题是:
是否有可靠的方法来了解音量是否可以移除?
应该有一种方法,因为Windows 区分可移动的(他们在时钟附近有一个“安全地移除硬盘并弹出媒体”的图标)。
答案 0 :(得分:2)
问题在于你提出了错误的问题。当他们使用这个术语时,"可移动"表示介质和介质的驱动器是分开的(如软盘驱动器或CD-ROM)。任何不允许单个驱动器在不同时间保持不同媒体的东西都是“固定的”#34;驱动。
根据您的需求,我相信您希望将SetupDiGetDeviceRegistryProperty与SPDRP_CAPABILITIES
标志一起使用。这将告诉您驱动器是否可以弹出其媒体(几乎相当于"可移动"您已经找到),还有设备本身是否可移动( CM_DEVCAP_REMOVABLE
)。
不幸的是,微软的SetupDi*
函数使用起来很麻烦(就像我所知道的那样好)。他们有一些demo code使用正确的函数并检索相当类似的信息,但代码也有点难看,所以可能需要一些研究和实验来修改它以获得你想要的东西。
答案 1 :(得分:1)
最简单可靠的方法是将IOCTL_STORAGE_QUERY_PROPERTY
与StorageDeviceProperty
一起使用。返回时我们得到了STORAGE_DEVICE_DESCRIPTOR
- 并寻找
RemovableMedia
指示 TRUE 时,设备的媒体(如果有)是可移除的。 如果设备没有媒体,则应忽略此成员。什么时候 FALSE 设备的媒体无法删除。
因此我们需要具有任何访问权限的磁盘句柄(因为IOCTL_STORAGE_QUERY_PROPERTY
定义为CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
(在每个IOCtl编码访问中(读,写,两者或任何),在本例中为FILE_ANY_ACCESS
。使用此句柄查询可以看起来像下一个
ULONG IsRemovable(HANDLE hDisk, BOOLEAN& RemovableMedia)
{
STORAGE_PROPERTY_QUERY spq = { StorageDeviceProperty, PropertyStandardQuery };
STORAGE_DEVICE_DESCRIPTOR sdd;
ULONG rcb;
if (DeviceIoControl(hDisk, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), &sdd, sizeof(sdd), &rcb, 0))
{
RemovableMedia = sdd.RemovableMedia;
return NOERROR;
}
return GetLastError();
}
用于枚举我们可以使用的所有磁盘驱动器,例如下一个代码:
void EnumDisks()
{
ULONG len;
if (!CM_Get_Device_Interface_List_SizeW(&len, const_cast<GUID*>(&GUID_DEVINTERFACE_DISK), 0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
PWSTR buf = (PWSTR)alloca(len << 1);
if (!CM_Get_Device_Interface_ListW(const_cast<GUID*>(&GUID_DEVINTERFACE_DISK), 0, buf, len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
while (*buf)
{
HANDLE hDisk = CreateFile(buf, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hDisk != INVALID_HANDLE_VALUE)
{
BOOLEAN RemovableMedia;
if (!IsRemovable(hDisk, RemovableMedia))
{
DbgPrint("%u %S\n", RemovableMedia, buf);
}
CloseHandle(hDisk);
}
buf += wcslen(buf) + 1;
}
}
}
}
但是对于测试,您可以打开磁盘作为L"\\\\?\\X:"
,例如
答案 2 :(得分:0)
杰里·科芬(Jerry Coffin)几乎是正确的。
但是,您必须改为检查SPDRP_REMOVAL_POLICY
属性。
到达那里很麻烦。这是一个C#实现。它返回PNPDeviceID列表或设备路径。然后可以将其与WMI数据或PowerShell Get-Disk
的结果相关。
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace checkIfDriveIsRemovable
{
class Program
{
// some relevant sources:
// https://www.pinvoke.net/default.aspx/setupapi.setupdigetclassdevs
// https://www.pinvoke.net/default.aspx/setupapi.setupdigetdeviceregistryproperty
// https://stackoverflow.com/questions/15000196/reading-device-managers-property-fields-in-windows-7-8
// https://stackoverflow.com/questions/14621211/determine-if-drive-is-removable-flash-or-hdd-knowing-only-the-drive-letter
private static string GUID_DEVINTERFACE_DISK = "53F56307-B6BF-11D0-94F2-00A0C91EFB8B";
private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr( -1 );
const int BUFFER_SIZE = 1024;
enum RemovalPolicy : uint
{
CM_REMOVAL_POLICY_EXPECT_NO_REMOVAL = 1,
CM_REMOVAL_POLICY_EXPECT_ORDERLY_REMOVAL = 2,
CM_REMOVAL_POLICY_EXPECT_SURPRISE_REMOVAL = 3
}
enum SetupDiGetDeviceRegistryPropertyEnum : uint
{
SPDRP_DEVICEDESC = 0x00000000, // DeviceDesc (R/W)
SPDRP_HARDWAREID = 0x00000001, // HardwareID (R/W)
SPDRP_COMPATIBLEIDS = 0x00000002, // CompatibleIDs (R/W)
SPDRP_UNUSED0 = 0x00000003, // unused
SPDRP_SERVICE = 0x00000004, // Service (R/W)
SPDRP_UNUSED1 = 0x00000005, // unused
SPDRP_UNUSED2 = 0x00000006, // unused
SPDRP_CLASS = 0x00000007, // Class (R--tied to ClassGUID)
SPDRP_CLASSGUID = 0x00000008, // ClassGUID (R/W)
SPDRP_DRIVER = 0x00000009, // Driver (R/W)
SPDRP_CONFIGFLAGS = 0x0000000A, // ConfigFlags (R/W)
SPDRP_MFG = 0x0000000B, // Mfg (R/W)
SPDRP_FRIENDLYNAME = 0x0000000C, // FriendlyName (R/W)
SPDRP_LOCATION_INFORMATION = 0x0000000D, // LocationInformation (R/W)
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = 0x0000000E, // PhysicalDeviceObjectName (R)
SPDRP_CAPABILITIES = 0x0000000F, // Capabilities (R)
SPDRP_UI_NUMBER = 0x00000010, // UiNumber (R)
SPDRP_UPPERFILTERS = 0x00000011, // UpperFilters (R/W)
SPDRP_LOWERFILTERS = 0x00000012, // LowerFilters (R/W)
SPDRP_BUSTYPEGUID = 0x00000013, // BusTypeGUID (R)
SPDRP_LEGACYBUSTYPE = 0x00000014, // LegacyBusType (R)
SPDRP_BUSNUMBER = 0x00000015, // BusNumber (R)
SPDRP_ENUMERATOR_NAME = 0x00000016, // Enumerator Name (R)
SPDRP_SECURITY = 0x00000017, // Security (R/W, binary form)
SPDRP_SECURITY_SDS = 0x00000018, // Security (W, SDS form)
SPDRP_DEVTYPE = 0x00000019, // Device Type (R/W)
SPDRP_EXCLUSIVE = 0x0000001A, // Device is exclusive-access (R/W)
SPDRP_CHARACTERISTICS = 0x0000001B, // Device Characteristics (R/W)
SPDRP_ADDRESS = 0x0000001C, // Device Address (R)
SPDRP_UI_NUMBER_DESC_FORMAT = 0X0000001D, // UiNumberDescFormat (R/W)
SPDRP_DEVICE_POWER_DATA = 0x0000001E, // Device Power Data (R)
SPDRP_REMOVAL_POLICY = 0x0000001F, // Removal Policy (R)
SPDRP_REMOVAL_POLICY_HW_DEFAULT = 0x00000020, // Hardware Removal Policy (R)
SPDRP_REMOVAL_POLICY_OVERRIDE = 0x00000021, // Removal Policy Override (RW)
SPDRP_INSTALL_STATE = 0x00000022, // Device Install State (R)
SPDRP_LOCATION_PATHS = 0x00000023, // Device Location Paths (R)
SPDRP_BASE_CONTAINERID = 0x00000024 // Base ContainerID (R)
}
[Flags]
public enum DiGetClassFlags : uint
{
DIGCF_DEFAULT = 0x00000001, // only valid with DIGCF_DEVICEINTERFACE
DIGCF_PRESENT = 0x00000002,
DIGCF_ALLCLASSES = 0x00000004,
DIGCF_PROFILE = 0x00000008,
DIGCF_DEVICEINTERFACE = 0x00000010,
}
public enum RegType : uint
{
REG_BINARY = 3,
REG_DWORD = 4,
REG_EXPAND_SZ = 2,
REG_MULTI_SZ = 7,
REG_SZ = 1
}
[StructLayout( LayoutKind.Sequential )]
struct SP_DEVICE_INTERFACE_DATA
{
public Int32 cbSize;
public Guid interfaceClassGuid;
public Int32 flags;
private UIntPtr reserved;
}
[StructLayout( LayoutKind.Sequential )]
struct SP_DEVINFO_DATA
{
public UInt32 cbSize;
public Guid ClassGuid;
public UInt32 DevInst;
public IntPtr Reserved;
}
[StructLayout( LayoutKind.Sequential, CharSet = CharSet.Auto )]
struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
public int cbSize;
[MarshalAs( UnmanagedType.ByValArray, SizeConst = BUFFER_SIZE )]
public byte[] DevicePath;
}
[DllImport( "setupapi.dll", CharSet = CharSet.Auto )]
static extern IntPtr SetupDiGetClassDevs(
ref Guid ClassGuid,
IntPtr Enumerator,
IntPtr hwndParent,
uint Flags
);
[DllImport( @"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true )]
static extern Boolean SetupDiEnumDeviceInterfaces( IntPtr hDevInfo, IntPtr devInfo, ref Guid interfaceClassGuid, UInt32 memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData );
[DllImport( @"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true )]
static extern Boolean SetupDiGetDeviceInterfaceDetail( IntPtr hDevInfo, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, UInt32 deviceInterfaceDetailDataSize, ref UInt32 requiredSize, ref SP_DEVINFO_DATA deviceInfoData );
[DllImport( "setupapi.dll", CharSet = CharSet.Auto, SetLastError = true )]
static extern bool SetupDiGetDeviceRegistryProperty( IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, uint property, out UInt32 propertyRegDataType, byte[] propertyBuffer, uint propertyBufferSize, out UInt32 requiredSize );
[DllImport( "setupapi.dll" )]
static extern bool SetupDiGetDeviceInstanceIdA(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, byte[] DeviceInstanceId, Int32 DeviceInstanceIdSize, out UInt32 RequiredSize);
[DllImport( "setupapi.dll" )]
static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
static string getStringProp(IntPtr h, SP_DEVINFO_DATA da, SetupDiGetDeviceRegistryPropertyEnum prop)
{
UInt32 requiredSize;
UInt32 regType;
byte[] ptrBuf = new byte[BUFFER_SIZE];
if( !SetupDiGetDeviceRegistryProperty( h, ref da, ( uint ) prop, out regType, ptrBuf, BUFFER_SIZE, out requiredSize ) )
throw new InvalidOperationException( "Error getting string property" );
if( regType != (uint) RegType.REG_SZ || ( requiredSize & 1 ) != 0 )
throw new InvalidOperationException( "Property is not a REG_SZ" );
if( requiredSize == 0 )
return "";
return Encoding.Unicode.GetString( ptrBuf, 0, (int) requiredSize - 2 );
}
static uint getDWORDProp(IntPtr h, SP_DEVINFO_DATA da, SetupDiGetDeviceRegistryPropertyEnum prop)
{
UInt32 requiredSize;
UInt32 regType;
byte[] ptrBuf = new byte[4];
if( !SetupDiGetDeviceRegistryProperty( h, ref da, ( uint ) prop, out regType, ptrBuf, 4, out requiredSize ) )
throw new InvalidOperationException( "Error getting DWORD property" );
if( regType != ( uint ) RegType.REG_DWORD || requiredSize != 4 )
throw new InvalidOperationException( "Property is not a REG_DWORD" );
return BitConverter.ToUInt32( ptrBuf, 0 );
}
public static string[] getRemovableDisks( bool getPNPDeviceID = false )
{
List<String> result = new List<string>();
Guid DiskGUID = new Guid( GUID_DEVINTERFACE_DISK );
IntPtr h = SetupDiGetClassDevs( ref DiskGUID, IntPtr.Zero, IntPtr.Zero, ( uint ) ( DiGetClassFlags.DIGCF_PRESENT | DiGetClassFlags.DIGCF_DEVICEINTERFACE ) );
if( h == INVALID_HANDLE_VALUE )
return null;
IntPtr x = new IntPtr(); ;
int y = Marshal.SizeOf( x );
try {
for( uint i = 0; ; i++ ) {
SP_DEVICE_INTERFACE_DATA dia = new SP_DEVICE_INTERFACE_DATA();
dia.cbSize = Marshal.SizeOf( dia );
if( !SetupDiEnumDeviceInterfaces( h, IntPtr.Zero, ref DiskGUID, i, ref dia ) )
break;
SP_DEVINFO_DATA da = new SP_DEVINFO_DATA();
da.cbSize = ( uint ) Marshal.SizeOf( da );
// build a Device Interface Detail Data structure
SP_DEVICE_INTERFACE_DETAIL_DATA didd = new SP_DEVICE_INTERFACE_DETAIL_DATA();
// I honestly don't know, why this works. The if-part can be found on the net a few times, the else part is from me
if( IntPtr.Size == 4 )
didd.cbSize = 4 + Marshal.SystemDefaultCharSize; // trust me :)
else
didd.cbSize = 8;
// now we can get some more detailed information
uint nRequiredSize = 0;
uint nBytes = BUFFER_SIZE;
if( SetupDiGetDeviceInterfaceDetail( h, ref dia, ref didd, nBytes, ref nRequiredSize, ref da ) ) {
string devicePath = Encoding.Unicode.GetString( didd.DevicePath, 0, ( int ) nRequiredSize - 6 ); // remove 6 bytes: 2 bytes zero termination and another 4 bytes, because nRequiredSize also counts SP_DEVICE_INTERFACE_DETAIL_DATA.cbSize (as it seems...)
UInt32 RequiredSize;
byte[] ptrBuf = new byte[BUFFER_SIZE];
string PNPDeviceID = "";
if( SetupDiGetDeviceInstanceIdA( h, ref da, ptrBuf, BUFFER_SIZE, out RequiredSize ) ) {
if( RequiredSize >= 1 )
PNPDeviceID = Encoding.ASCII.GetString( ptrBuf, 0, ( int ) RequiredSize - 1 );
}
// you can get the properties, which are shown in "device manager -> properties of the drive -> details"
/*
string desc = getStringProp( h, da, SetupDiGetDeviceRegistryPropertyEnum.SPDRP_PHYSICAL_DEVICE_OBJECT_NAME ); // SPDRP_DEVICEDESC );
string driver = getStringProp( h, da, SetupDiGetDeviceRegistryPropertyEnum.SPDRP_DRIVER );
string friendlyname = getStringProp( h, da, SetupDiGetDeviceRegistryPropertyEnum.SPDRP_FRIENDLYNAME );
// no, the removable flag in the capabalities is of no use! Use removalPolicy!
uint capabilities = getDWORDProp( h, da, SetupDiGetDeviceRegistryPropertyEnum.SPDRP_CAPABILITIES );
uint removalPolicy = getDWORDProp( h, da, SetupDiGetDeviceRegistryPropertyEnum.SPDRP_REMOVAL_POLICY );
uint removalPolicyHW = getDWORDProp( h, da, SetupDiGetDeviceRegistryPropertyEnum.SPDRP_REMOVAL_POLICY_HW_DEFAULT );
//uint removalPolicyOVR = getDWORDProp( h, da, SetupDiGetDeviceRegistryPropertyEnum.SPDRP_REMOVAL_POLICY_OVERRIDE );
Console.WriteLine( "{0,-40} {1,-60} {2,-20} {3,-20} {4,5} {5} {6}", friendlyname, PNPDeviceID, desc, driver, capabilities, removalPolicy, removalPolicyHW );
*/
try {
uint removalPolicy = getDWORDProp( h, da, SetupDiGetDeviceRegistryPropertyEnum.SPDRP_REMOVAL_POLICY );
if( removalPolicy == ( uint ) RemovalPolicy.CM_REMOVAL_POLICY_EXPECT_ORDERLY_REMOVAL || removalPolicy == ( uint ) RemovalPolicy.CM_REMOVAL_POLICY_EXPECT_SURPRISE_REMOVAL )
result.Add( getPNPDeviceID ? PNPDeviceID : devicePath );
} catch( InvalidOperationException ) {
continue;
}
}
}
} finally {
SetupDiDestroyDeviceInfoList( h );
}
return result.ToArray();
}
static void Main(string[] args)
{
string[] removableDisks = getRemovableDisks();
}
}
}