【发布时间】:2010-09-24 13:42:50
【问题描述】:
如何在 Windows 中列出物理磁盘?
为了获得"\\\\.\PhysicalDrive0"的列表可用。
【问题讨论】:
标签: c windows winapi hard-drive
如何在 Windows 中列出物理磁盘?
为了获得"\\\\.\PhysicalDrive0"的列表可用。
【问题讨论】:
标签: c windows winapi hard-drive
#WMIC wmic是一个非常完善的工具
wmic diskdrive list
提供一个(太多的)详细列表,例如
了解较少
wmic diskdrive list brief
#C Sebastian Godelet 提到in the comments:
在 C 中:
system("wmic diskdrive list");
正如评论,您也可以调用 WinAPI,但是...如“How to obtain data from WMI using a C Application?”所示,这是相当复杂的(通常使用 C++,而不是 C)。
#PowerShell 或者使用 PowerShell:
Get-WmiObject Win32_DiskDrive
2022 年 2 月更新,微软在“Windows 10 features we're no longer developing”中宣布
WMIC 工具在 Windows 10 版本 21H1 和 Windows Server 的 21H1 通用可用性频道版本中已弃用。
此工具已被 Windows PowerShell for WMI 取代。
注意:此弃用仅适用于 command-line management tool。 WMI 本身不受影响。
【讨论】:
system("wmic diskdrive list");
GetLogicalDrives() 枚举所有挂载的磁盘分区,不是物理驱动器。
您可以使用(或不使用)GetLogicalDrives 枚举驱动器盘符,然后调用 QueryDosDevice() 来找出该盘符映射到哪个物理驱动器。
或者,您可以解码注册表中 HKEY_LOCAL_MACHINE\SYSTEM\MountedDevices 中的信息。然而,那里的二进制数据编码并不明显。如果你有一本 Russinovich 和 Solomon 的著作《Microsoft Windows Internals》,第 10 章会讨论这个注册表配置单元。
【讨论】:
我修改了一个名为“dskwipe”的开源程序,以便从中提取磁盘信息。 Dskwipe 是用 C 编写的,您可以从中提取此功能。二进制文件和源代码在这里:dskwipe 0.3 has been released
返回的信息如下所示:
Device Name Size Type Partition Type
------------------------------ --------- --------- --------------------
\\.\PhysicalDrive0 40.0 GB Fixed
\\.\PhysicalDrive1 80.0 GB Fixed
\Device\Harddisk0\Partition0 40.0 GB Fixed
\Device\Harddisk0\Partition1 40.0 GB Fixed NTFS
\Device\Harddisk1\Partition0 80.0 GB Fixed
\Device\Harddisk1\Partition1 80.0 GB Fixed NTFS
\\.\C: 80.0 GB Fixed NTFS
\\.\D: 2.1 GB Fixed FAT32
\\.\E: 40.0 GB Fixed NTFS
【讨论】:
我今天刚刚在我的 RSS 阅读器中遇到了这个问题。我有一个更清洁的解决方案给你。这个例子是在 Delphi 中的,但可以很容易地转换为 C/C++(都是 Win32)。
从以下注册表位置查询所有值名称: HKLM\SYSTEM\MountedDevices
将它们一一传递给下面的函数,您将返回设备名称。相当干净和简单! I found this code on a blog here.
function VolumeNameToDeviceName(const VolName: String): String;
var
s: String;
TargetPath: Array[0..MAX_PATH] of WideChar;
bSucceeded: Boolean;
begin
Result := ”;
// VolumeName has a format like this: \\?\Volume{c4ee0265-bada-11dd-9cd5-806e6f6e6963}\
// We need to strip this to Volume{c4ee0265-bada-11dd-9cd5-806e6f6e6963}
s := Copy(VolName, 5, Length(VolName) - 5);
bSucceeded := QueryDosDeviceW(PWideChar(WideString(s)), TargetPath, MAX_PATH) <> 0;
if bSucceeded then
begin
Result := TargetPath;
end
else begin
// raise exception
end;
end;
【讨论】:
列出美国英语字母表中的所有字母,跳过 a 和 b。 “CDEFGHIJKLMNOPQRSTUVWXYZ”。使用CreateFile 打开每个驱动器,例如CreateFile("\\.\C:")。如果它没有返回INVALID_HANDLE_VALUE,那么你有一个“好”的驱动器。接下来使用该句柄并通过DeviceIoControl 运行它以获取磁盘#。 See my related answer for more details。
【讨论】:
唯一确定的方法是在所有\\.\Physicaldiskx 上调用CreateFile(),其中x 是从0 到15(16 是允许的最大磁盘数)。检查返回的句柄值。如果 ERROR_FILE_NOT_FOUND 无效,请检查 GetLastError()。如果它返回任何其他内容,则该磁盘存在,但由于某种原因您无法访问它。
【讨论】:
15?继续枚举,直到你失败。我不确定操作系统是否会跳过某些设备编号。
一种方法:
使用GetLogicalDrives枚举逻辑驱动器
对于每个逻辑驱动器,打开一个名为 "\\.\X:"(不带引号)的文件,其中 X 是逻辑驱动器号。
调用DeviceIoControl将句柄传递给上一步打开的文件,并将dwIoControlCode参数设置为IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS:
HANDLE hHandle;
VOLUME_DISK_EXTENTS diskExtents;
DWORD dwSize;
[...]
iRes = DeviceIoControl(
hHandle,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
NULL,
0,
(LPVOID) &diskExtents,
(DWORD) sizeof(diskExtents),
(LPDWORD) &dwSize,
NULL);
这会以VOLUME_DISK_EXTENTS 结构的形式返回逻辑卷的物理位置信息。
在卷驻留在单个物理驱动器上的简单情况下,物理驱动器号可在diskExtents.Extents[0].DiskNumber 中获得
【讨论】:
DeviceIoControl(IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS) 调用实现将失败。换句话说,您首先需要向DeviceIoControl 询问VOLUME_DISK_EXTENTS 结构的大小,然后分配那么多内存,然后才使用分配的缓冲区再次调用它。它的工作方式如上所示,因为大多数卷仅驻留在一个磁盘上。
VOLUME_DISK_EXTENTS 为一个区段拥有足够的内存,因此您可以像 Grodriguez 建议的那样调用它,然后检查 success || ERROR_MORE_DATA == GetLastError(),因为无论如何我们只关心第一个区段。
Thic WMIC 命令组合工作正常:
wmic volume list brief
【讨论】:
这可能晚了 5 年 :)。但由于我还没有看到这个问题的答案,所以添加这个。
我们可以使用Setup APIs 来获取磁盘列表,即系统中实现GUID_DEVINTERFACE_DISK 的设备。
一旦我们有了他们的设备路径,我们就可以发出IOCTL_STORAGE_GET_DEVICE_NUMBER 来构造"\\.\PHYSICALDRIVE%d" 和STORAGE_DEVICE_NUMBER.DeviceNumber
另见SetupDiGetClassDevs function
#include <Windows.h>
#include <Setupapi.h>
#include <Ntddstor.h>
#pragma comment( lib, "setupapi.lib" )
#include <iostream>
#include <string>
using namespace std;
#define START_ERROR_CHK() \
DWORD error = ERROR_SUCCESS; \
DWORD failedLine; \
string failedApi;
#define CHK( expr, api ) \
if ( !( expr ) ) { \
error = GetLastError( ); \
failedLine = __LINE__; \
failedApi = ( api ); \
goto Error_Exit; \
}
#define END_ERROR_CHK() \
error = ERROR_SUCCESS; \
Error_Exit: \
if ( ERROR_SUCCESS != error ) { \
cout << failedApi << " failed at " << failedLine << " : Error Code - " << error << endl; \
}
int main( int argc, char **argv ) {
HDEVINFO diskClassDevices;
GUID diskClassDeviceInterfaceGuid = GUID_DEVINTERFACE_DISK;
SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData;
DWORD requiredSize;
DWORD deviceIndex;
HANDLE disk = INVALID_HANDLE_VALUE;
STORAGE_DEVICE_NUMBER diskNumber;
DWORD bytesReturned;
START_ERROR_CHK();
//
// Get the handle to the device information set for installed
// disk class devices. Returns only devices that are currently
// present in the system and have an enabled disk device
// interface.
//
diskClassDevices = SetupDiGetClassDevs( &diskClassDeviceInterfaceGuid,
NULL,
NULL,
DIGCF_PRESENT |
DIGCF_DEVICEINTERFACE );
CHK( INVALID_HANDLE_VALUE != diskClassDevices,
"SetupDiGetClassDevs" );
ZeroMemory( &deviceInterfaceData, sizeof( SP_DEVICE_INTERFACE_DATA ) );
deviceInterfaceData.cbSize = sizeof( SP_DEVICE_INTERFACE_DATA );
deviceIndex = 0;
while ( SetupDiEnumDeviceInterfaces( diskClassDevices,
NULL,
&diskClassDeviceInterfaceGuid,
deviceIndex,
&deviceInterfaceData ) ) {
++deviceIndex;
SetupDiGetDeviceInterfaceDetail( diskClassDevices,
&deviceInterfaceData,
NULL,
0,
&requiredSize,
NULL );
CHK( ERROR_INSUFFICIENT_BUFFER == GetLastError( ),
"SetupDiGetDeviceInterfaceDetail - 1" );
deviceInterfaceDetailData = ( PSP_DEVICE_INTERFACE_DETAIL_DATA ) malloc( requiredSize );
CHK( NULL != deviceInterfaceDetailData,
"malloc" );
ZeroMemory( deviceInterfaceDetailData, requiredSize );
deviceInterfaceDetailData->cbSize = sizeof( SP_DEVICE_INTERFACE_DETAIL_DATA );
CHK( SetupDiGetDeviceInterfaceDetail( diskClassDevices,
&deviceInterfaceData,
deviceInterfaceDetailData,
requiredSize,
NULL,
NULL ),
"SetupDiGetDeviceInterfaceDetail - 2" );
disk = CreateFile( deviceInterfaceDetailData->DevicePath,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
CHK( INVALID_HANDLE_VALUE != disk,
"CreateFile" );
CHK( DeviceIoControl( disk,
IOCTL_STORAGE_GET_DEVICE_NUMBER,
NULL,
0,
&diskNumber,
sizeof( STORAGE_DEVICE_NUMBER ),
&bytesReturned,
NULL ),
"IOCTL_STORAGE_GET_DEVICE_NUMBER" );
CloseHandle( disk );
disk = INVALID_HANDLE_VALUE;
cout << deviceInterfaceDetailData->DevicePath << endl;
cout << "\\\\?\\PhysicalDrive" << diskNumber.DeviceNumber << endl;
cout << endl;
}
CHK( ERROR_NO_MORE_ITEMS == GetLastError( ),
"SetupDiEnumDeviceInterfaces" );
END_ERROR_CHK();
Exit:
if ( INVALID_HANDLE_VALUE != diskClassDevices ) {
SetupDiDestroyDeviceInfoList( diskClassDevices );
}
if ( INVALID_HANDLE_VALUE != disk ) {
CloseHandle( disk );
}
return error;
}
【讨论】:
可能想要包括旧的 A: 和 B: 驱动器,因为您永远不知道谁可能在使用它们! 我厌倦了 USB 驱动器与我的两个仅用于 Readyboost 的 SDHC 驱动器碰撞。 我一直将它们分配给高字母 Z: Y: 使用一个实用程序,它可以根据需要为设备分配驱动器号。我想知道.... 我可以制作一个 Readyboost 驱动器号 A: 吗?是的! 我可以将我的第二个 SDHC 驱动器号设置为 B: 吗?是的!
我以前用过软盘驱动器,从没想过 A: 或 B: 会派上用场 准备好提振。
我的意思是,不要假设 A: & B: 不会被任何人用于任何事情 您甚至可能会发现正在使用旧的 SUBST 命令!
【讨论】:
唯一正确的答案是@Grodriguez 的答案,这是他懒得写的代码:
#include <windows.h>
#include <iostream>
#include <bitset>
#include <vector>
using namespace std;
typedef struct _DISK_EXTENT {
DWORD DiskNumber;
LARGE_INTEGER StartingOffset;
LARGE_INTEGER ExtentLength;
} DISK_EXTENT, *PDISK_EXTENT;
typedef struct _VOLUME_DISK_EXTENTS {
DWORD NumberOfDiskExtents;
DISK_EXTENT Extents[ANYSIZE_ARRAY];
} VOLUME_DISK_EXTENTS, *PVOLUME_DISK_EXTENTS;
#define CTL_CODE(DeviceType, Function, Method, Access) \
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
#define IOCTL_VOLUME_BASE ((DWORD)'V')
#define METHOD_BUFFERED 0
#define FILE_ANY_ACCESS 0x00000000
#define IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS CTL_CODE(IOCTL_VOLUME_BASE, 0, METHOD_BUFFERED, FILE_ANY_ACCESS)
int main() {
bitset<32> drives(GetLogicalDrives());
vector<char> goodDrives;
for (char c = 'A'; c <= 'Z'; ++c) {
if (drives[c - 'A']) {
if (GetDriveType((c + string(":\\")).c_str()) == DRIVE_FIXED) {
goodDrives.push_back(c);
}
}
}
for (auto & drive : goodDrives) {
string s = string("\\\\.\\") + drive + ":";
HANDLE h = CreateFileA(
s.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_RANDOM_ACCESS, NULL
);
if (h == INVALID_HANDLE_VALUE) {
cerr << "Drive " << drive << ":\\ cannot be opened";
continue;
}
DWORD bytesReturned;
VOLUME_DISK_EXTENTS vde;
if (!DeviceIoControl(
h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
NULL, 0, &vde, sizeof(vde), &bytesReturned, NULL
)) {
cerr << "Drive " << drive << ":\\ cannot be mapped into physical drive";
continue;
}
cout << "Drive " << drive << ":\\ is on the following physical drives: ";
for (int i = 0; i < vde.NumberOfDiskExtents; ++i) {
cout << vde.Extents[i].DiskNumber << ' ';
}
cout << endl;
}
}
我认为安装 Windows 驱动程序开发工具包是一个相当漫长的过程,所以我已经包含了需要使用 DeviceIoControl 来完成此任务的声明。
【讨论】:
DeviceIoControl。你不能假设只有一个范围。您需要向DeviceIoControl 询问所需VOLUME_DISK_EXTENTS 缓冲区的大小。
如果您想要“物理”访问,我们正在开发此 API,最终将允许您与存储设备进行通信。它是开源的,您可以查看当前代码以获取一些信息。查看更多功能: https://github.com/virtium/vtStor
【讨论】:
答案远比上述所有答案简单。物理驱动器列表实际上存储在注册表项中,该注册表项还提供了设备映射。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\disk\Enum
Count是PhysicalDrive#的编号,每个编号的Registry Value是对应的物理驱动器。
例如,注册表值“0”是 PhysicalDrive0。该值是 PhysicalDrive0 映射到的实际设备。此处包含的值可以传入参数pDeviceID 内的CM_Locate_DevNode 以使用即插即用服务。这将允许您在设备上收集大量信息。例如设备管理器中的属性,如“友好显示名称”,如果您需要驱动器名称、序列号等。
不需要可能不在系统或其他黑客上运行的 WMI 服务,并且此功能至少自 2000 年以来就已存在于 Windows 中,并且在 Windows 10 中仍然如此。
【讨论】:
Here 是一种新解决方案,通过 WMI 调用来实现。
那么你需要做的就是打电话:
queryAndPrintResult(L"SELECT * FROM Win32_DiskDrive", L"Name");
【讨论】:
如果你只需要查看现有磁盘,这个就足够了:
powershell "get-physicaldisk"
【讨论】: