English 中文(简体)
如何列出物理磁盘?
原标题:
  • 时间:2008-11-29 17:18:12
  •  标签:

How to list physical disks in Windows? In order to obtain a list of "\\.PhysicalDrive0" available.

问题回答

#WMIC wmic is a very complete tool

wmic diskdrive list

提供(太多)详细的清单,例如

有关信息较少

wmic diskdrive list brief 

#C Sebastian Godelet mentions in the comments:

在 C 中:

system("wmic diskdrive list");

正如评论中提到的,您也可以调用WinAPI,但是...正如"如何使用C应用程序从WMI获取数据?"所示,这相当复杂(通常使用C++而不是C完成)。


#PowerShell Or with PowerShell:

Get-WmiObject Win32_DiskDrive

2022年2月,微软宣布在“Windows 10我们不再开发的功能”中更新。

WMIC工具在Windows 10版本21H1和Windows Server的21H1正式发布渠道中已被弃用。

此工具已被 Windows PowerShell for WMI 取代。

注意:这种废弃仅适用于命令行管理工具。WMI本身不受影响。

一个方法是:

  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 中获取。

这可能晚了五年 :). 但是我还没有看到答案,所以补充一下。

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

一旦我们拥有他们的设备路径,就可以发出< a href="http://msdn.microsoft.com/en-us/library/windows/hardware/ff560551(v=vs.85).aspx">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;
}

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

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\disk\Enum

计数是PhysicalDrive#的数量,每个编号的注册项是相应的物理驱动器。” 的中文翻译是:“计数是PhysicalDrive#的数量,每个编号的注册值是相应的物理驱动器。”

例如,注册表键值“0”是PhysicalDrive0。该键值是实际物理驱动器PhysicalDrive0映射到的设备。这里包含的键值可以传递到CM_Locate_DevNode参数pDeviceID中,以使用即插即用服务。这将允许您获取有关设备的大量信息,例如设备管理器中的属性,如“友好显示名称”(如果需要驱动器名称)、序列号等等。

没有必要使用可能在系统上未运行或其他黑客技巧的WMI服务,而且此功能自至少2000年以来就存在于Windows中,Windows 10也是如此。

我已经修改了一个开源程序叫做“dskwipe”,以便从中提取这个磁盘信息。Dskwipe是用C编写的,你可以从中提取这个函数。二进制和源代码在这里可以找到:dskwipe 0.3已发布

回传的信息会类似于这样:

Device Name                         Size Type      Partition Type
------------------------------ --------- --------- --------------------
\.PhysicalDrive0               40.0 GB Fixed
\.PhysicalDrive1               80.0 GB Fixed
DeviceHarddisk0Partition0     40.0 GB Fixed
DeviceHarddisk0Partition1     40.0 GB Fixed     NTFS
DeviceHarddisk1Partition0     80.0 GB Fixed
DeviceHarddisk1Partition1     80.0 GB Fixed     NTFS
\.C:                           80.0 GB Fixed     NTFS
\.D:                            2.1 GB Fixed     FAT32
\.E:                           40.0 GB Fixed     NTFS

唯一可靠的方法是对所有\.Physicaldiskx,其中x范围从0到15(16为允许的最大磁盘数)调用CreateFile()。检查返回的句柄值。如果无效,请检查GetLastError(),查看ERROR_FILE_NOT_FOUND。如果返回其他任何内容,则表示磁盘存在,但由于某些原因您不能访问它。

唯一正确的答案是@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所需的声明包括在内,以完成此任务。

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

你可以使用 GetLogicalDrives 枚举驱动器字母,然后调用 QueryDosDevice() 函数查找该字母映射到哪个物理驱动器。

或者,您可以在注册表中解码HKEY_LOCAL_MACHINE SYSTEM MountedDevices下的信息。 那里的二进制数据编码并不明显。如果您有Russinovich和Solomon的书Microsoft Windows Internals的副本,则第10章中讨论了此注册表区域。

这个WMIC命令组合运行良好:

wmic volume list brief

如果您只需要查看现有的磁盘,这个就够了:

powershell "get-physicaldisk"

Might want to include the old A: and B: drives as you never know who might be using them! I got tired of USB drives bumping my two SDHC drives that are just for Readyboost. I had been assigning them to High letters Z: Y: with a utility that will assign drive letters to devices as you wish. I wondered.... Can I make a Readyboost drive letter A: ? YES! Can I put my second SDHC drive letter as B: ? YES!

I ve used Floppy Drives back in the day, never thought that A: or B: would come in handy for Readyboost.

My point is, don t assume A: & B: will not be used by anyone for anything You might even find the old SUBST command being used!

Here is a new solution of doing it with doing WMI calls.
Then all you need to do is just to call :

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

我今天在我的RSS阅读器中发现了这个。我有一个更干净的解决方案给你。这个例子是用Delphi编写的,但可以非常容易地转换为C/C++(它都是Win32)。

Query all value names from the following registry location: HKLMSYSTEMMountedDevices

一个接一个地传递它们到下面的函数中,你将得到设备名称。非常干净和简单!我在这里的博客上找到了这段代码。

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;

If you want "physical" access, we re developing this API that will eventually allows you to communicate with storage devices. It s open source and you can see the current code for some information. Check back for more features: https://github.com/virtium/vtStor





相关问题
热门标签