如何列出物理磁盘?


Answers:


70

WMIC

WMIC是一个非常完整的工具

wmic diskdrive list

提供(太多)详细列表,例如

更少的信息

wmic diskdrive list brief 

C

塞巴斯蒂安·戈德莱特Sebastian Godelet在评论中提到:

在C中:

system("wmic diskdrive list");

如前所述,您也可以调用WinAPI,但是...如“如何使用C应用程序从WMI中获取数据? ”所示,这非常复杂(通常使用C ++而不是C语言完成)。

电源外壳

或使用PowerShell:

Get-WmiObject Win32_DiskDrive

9
-1为不回答这个问题,这是询问如何做到这一点在C
unixman83

11
+1不能回答问题,但这是一条非常有用的信息:-)
Grodriguez 2012年

8
您可以system("wmic diskdrive list");在C中做
Sebastian 2012年

您还可以通过WinApi使用WMI,而不仅仅是调用wmic应用。
Alex P.

启用软件RAID或StorageSpaces时,Win32_DiskDrive不会列出物理磁盘。原始物理磁盘已被过滤掉。易于与PowerShell Get-PhysicalDisk进行比较
Dmitry Gusarov

46

一种方法是:

  1. 使用以下方式枚举逻辑驱动器 GetLogicalDrives

  2. 对于每个逻辑驱动器,打开一个名为"\\.\X:"(不带引号)的文件,其中X是逻辑驱动器号。

  3. 调用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


8
如果有一个没有任何(已装入)卷的空磁盘该怎么办?
j_kubik 2014年

2
请注意,DeviceIoControl(IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS)如果一个卷跨越多个磁盘,则他建议的调用实现将失败。换句话说,您首先需要询问structDeviceIoControl的大小VOLUME_DISK_EXTENTS,然后分配那么多的内存,然后才使用分配的缓冲区再次调用它。因为大多数卷仅驻留在一个磁盘上,所以它以上面显示的方式工作。
ahmd0

抱歉,我无法使用CreateFile((_ T(“ \\。\ C:”),GENERIC_READ,FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,///成功打开“ \\。\ C:” / * FILE_FLAG_WRITE_THROUGH | * / FILE_FLAG_NO_BUFFERING,NULL);您能解决我的问题吗?
Bowman han 2015年

@ ahmd0VOLUME_DISK_EXTENTS可以为一个扩展区保留足够的内存,因此您可以像Grodriguez建议的那样调用它,然后进行检查,success || ERROR_MORE_DATA == GetLastError()因为我们还是只关心第一个扩展区。
米莉·史密斯

1
使用0而不是GENERIC_READ,这将允许即使没有管理员权限也可以打开磁盘,但是仍然可以读取元数据信息,例如磁盘范围。
ivan.ukr

32

这可能为时5年:)。但据我所知,尚无答案,请添加。

我们可以使用Setup API来获取磁盘列表,即系统中实现的设备GUID_DEVINTERFACE_DISK

一旦我们有了自己的设备路径,我们可以发出IOCTL_STORAGE_GET_DEVICE_NUMBER来构建"\\.\PHYSICALDRIVE%d"STORAGE_DEVICE_NUMBER.DeviceNumber

另请参阅SetupDiGetClassDevs功能

#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;
}

添加另一个链接(我没有足够的代表在回答张贴) 安装程序API函数
阿伦

1
听起来不错。比上面我的答案更完整。+1
VonC

请注意,这些SETUPAPI功能将只是列出所有的物理驱动器,但虚拟的,太-其实每一个注册的磁盘驱动器接口将陆续上市,依我看这可能是解决问题的办法,但它也将产生大量的“噪音数据”的,使用SetupAPI比此答案所建议的要复杂得多
specializt

我根据上面列出的答案写了一个名为libwindevblk的小型库,列出了驱动器,在可能的情况下检索了卷名,并提供了一个允许简单地在分区上读写的api
Vincent Rich

19

答案比上述所有答案都简单得多。物理驱动器列表实际上存储在注册表项中,该注册表项还提供了设备映射。

HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \ disk \ Enum

Count是PhysicalDrive#的数量,每个编号的注册表值是对应的物理驱动器。

例如对于注册表值“ 0”是PhysicalDrive0。该值是实际设备PhysicalDrive0映射到的位置。值包含在这里可以传递到CM_Locate_DevNode内参数pDeviceID使用即插即用服务。这将使您可以在设备上收集大量信息。例如,如果需要驱动器名称,序列号等,则来自设备管理器的属性,例如“友好显示名称”。

不需要可能不在系统或其他黑客上运行的WMI服务,并且此功能至少在2000年就已存在于Windows中,并且在Windows 10中一直如此。


有趣的选择,可能比我7岁以上的答案更有意义。+1
VonC

我认为最好的选择是,因为它简单,可靠,并且使用注册表可能是Windows开发人员在定义Windows时想要的。
felknight '16

比我的答案+1好得多。最后一个问题是为什么它必须包含实际信息。是否有文件记录?Windows在哪一刻将数据写入那里?管理控制台是否使用它?
polkovnikov.ph

出色的方法,但是有一个小缺点:它无法列出每个物理驱动器的大小,因为它们没有存储在注册表中(不过WMI服务确实提供了它们)。对于获取每个驱动器的制造商和型号之类的东西,它仍然要好得多,并且占用的资源更少,因此请+1。我需要获取每个驱动器的大小,并且我不会在C语言中使用它,因此我必须采用WMI方式。它与物理内存差不多,它的详细数据也没有存储在注册表中……
Yin Cognyto

“至少在2000年开始出现在Windows中”:无法确认。该注册表项在Windows XP和Windows 7的缺失
托马斯

12

我修改了一个名为“ dskwipe”的开源程序,以从中提取磁盘信息。Dskwipe用C编写,您可以从中提取此功能。二进制文件和源代码在这里可用:dskwipe 0.3已发布

返回的信息将如下所示:

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

1
我以为是这样,但是它会强制搜索驱动器。是否没有可以直接报告设备的api?
CiNN

2
是。Win32中的SetupApi,函数名称以SetupDi开头
Warren P

10

唯一确定的方法是调用x在0到15之间的CreateFile()所有\\.\Physicaldiskx位置(16是允许的最大磁盘数量)。检查返回的句柄值。如果作废的支票GetLastError()ERROR_FILE_NOT_FOUND。如果返回任何其他内容,则表明该磁盘已存在,但由于某种原因您无法访问它。


7
你从哪里得到这个号码?
弱点

1
为什么要限制在15?不断枚举,直到失败为止。我不确定操作系统是否会跳过某些设备号。
Ajay

1
@Ajay我最好的猜测是万一您插入设备A,插入设备B,然后拔出设备A
哈尔,

10

唯一正确的答案是@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用于此任务的声明。


1
还:重新定义Windows宏可能只是有史以来最糟糕的想法-这样的应用程序将很快中断并停止工作。
specializt

2
就像我在对此答案的评论中显示的那样,您打错电话了DeviceIoControl。您不能假设只有一个范围。您需要询问DeviceIoControl所需VOLUME_DISK_EXTENTS缓冲区的大小。
ahmd0

@ ahmd0我很乐意修复它。您能否指向描述这种行为的msdn页面?(尽管创建位于两个盘区中的磁盘的方法也可以,因为我刚刚发现没有办法对其进行测试。)
polkovnikov.ph 2015年

@ polkovnikov.ph:msdn.microsoft.com/en-us/library/windows/desktop/aa365727.aspx你基本上调用DeviceIoControl内存单个VOLUME_DISK_EXTENTS像你这样,但再检查,如果它失败了,如果是这样,看看它是否返回ERROR_MORE_DATAGetLastError如果是的话,分配给新缓冲NumberOfDiskExtents的数量DISK_EXTENT结构(即它返回),并调用DeviceIoControl一次,并确保它成功。如果第一个调用DeviceIoControl成功,显然您不会做所有这一切。
ahmd0

1
并非每个物理驱动器都将与一个逻辑驱动器相关联,即使那样,也不会为每个逻辑驱动器分配一个驱动器号。
安娜(Anni)2016年

9

GetLogicalDrives()枚举所有已安装的磁盘分区,而不是物理驱动器。

您可以使用(或不使用)GetLogicalDrives枚举驱动器号,然后调用QueryDosDevice()来找出该驱动器号映射到的物理驱动器。

或者,您可以在注册表中的HKEY_LOCAL_MACHINE \ SYSTEM \ MountedDevices中解码信息。但是,那里的二进制数据编码并不明显。如果您拥有Russinovich和Solomon的书Microsoft Windows Internals,则将在第10章中讨论此注册表配置单元。


1
QueryDosDevice获取分区,而不是磁盘本身。将单个磁盘拆分为C:和D:,Win7 x64。因此:c =>“ \ Device \ HarddiskVolume2”; d =>“ \\ Device \ HarddiskVolume3'”
Arioch'The

4

Thic WMIC命令组合可以正常工作:

wmic volume list brief

卷!=物理磁盘。该命令将不会列出包含零卷的物理磁盘,例如未初始化的磁盘。(此外,尽管这不是上一个致命的问题,但此命令的输出将需要进一步处理,以对包含多个卷的物理磁盘的ID进行重复数据删除。)
Keith Russell

2

可能想包括旧的A:和B:驱动器,因为您永远不知道谁在使用它们!我已经厌倦了USB驱动器撞击我的两个仅用于Readyboost的SDHC驱动器。我曾经使用实用程序将它们分配给高字母Z:Y:可以根据需要将驱动器字母分配给设备。我想知道....我可以写一个Readyboost驱动器字母A:吗?是!我可以将第二个SDHC驱动器盘符记为B:吗?是!

那天我曾经使用过软盘驱动器,从没想过A:或B:对于Readyboost会派上用场。

我的观点是,不要假设A:&B:不会被任何人使用,您甚至可能会发现正在使用旧的SUBST命令!


1

今天我在RSS阅读器中遇到了这个问题。我为您提供了更清洁的解决方案。该示例在Delphi中,但是可以很容易地转换为C / C ++(全都是Win32)。

从以下注册表位置查询所有值名称: HKLM \ SYSTEM \ MountedDevices

一步一步地将它们传递给以下函数,您将获得设备名称。很干净简单!我在这里的博客上找到了此代码。

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;

2
我想拥有一个物理名称,以便我可以使用未分配的空间,因此我猜想这个未分配的空间将不会有已安装的卷GUID ...
CiNN

1
弗雷德,这不是我们想要的,类似于@Alnitak的答案。
马特·乔纳

1
您应该在Windows XP和更高版本中使用SetupApi,并且不再使用注册表,这是在Win98中执行此操作的方法,但现在不再使用。
沃伦·P

1

如果您要“物理”访问,我们正在开发此API,该API最终将允许您与存储设备进行通信。它是开源的,您可以查看当前代码以获取一些信息。返回查看更多功能:https : //github.com/virtium/vtStor


1

是通过执行WMI调用来实现的解决方案。
然后,您只需要致电:

queryAndPrintResult(L"SELECT * FROM Win32_DiskDrive", L"Name");

-3

列出美国英语字母中的所有字母,并跳过a和b。“ CDEFGHIJKLMNOPQRSTUVWXYZ”。用CreateFile例如打开每个驱动器CreateFile("\\.\C:")。如果它没有返回,INVALID_HANDLE_VALUE则说明您的驱动器状态良好。接下来,使用该句柄并运行直到DeviceIoControl获得磁盘号。请参阅我的相关答案以获取更多详细信息

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.