How to list physical disks?

CWindowsWinapiHard Drive

C Problem Overview


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

C Solutions


Solution 1 - C

#WMIC wmic is a very complete tool

wmic diskdrive list

provide a (too much) detailed list, for instance

for less info

wmic diskdrive list brief 

#C Sebastian Godelet mentions in the comments:

> In C:

system("wmic diskdrive list");

As commented, you can also call the WinAPI, but... as shown in "How to obtain data from WMI using a C Application?", this is quite complex (and generally done with C++, not C).


#PowerShell Or with PowerShell:

Get-WmiObject Win32_DiskDrive

Update Feb. 2022, Microsoft announces in "Windows 10 features we're no longer developing"

>The WMIC tool is deprecated in Windows 10, version 21H1 and the 21H1 General Availability Channel release of Windows Server. > > This tool is superseded by Windows PowerShell for WMI. > > Note: This deprecation only applies to the command-line management tool. WMI itself is not affected.

Solution 2 - C

One way to do it:

  1. Enumerate logical drives using GetLogicalDrives

  2. For each logical drive, open a file named "\\.\X:" (without the quotes) where X is the logical drive letter.

  3. Call DeviceIoControl passing the handle to the file opened in the previous step, and the dwIoControlCode parameter set to 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);
    

This returns information of the physical location of a logical volume, as a VOLUME_DISK_EXTENTS structure.

In the simple case where the volume resides on a single physical drive, the physical drive number is available in diskExtents.Extents[0].DiskNumber

Solution 3 - C

This might be 5 years too late :). But as I see no answer for this yet, adding this.

We can use Setup APIs to get the list of disks ie., devices in the system implementing GUID_DEVINTERFACE_DISK.

Once we have their device paths, we can issue IOCTL_STORAGE_GET_DEVICE_NUMBER to construct "\\.\PHYSICALDRIVE%d" with STORAGE_DEVICE_NUMBER.DeviceNumber

See also 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;
}

Solution 4 - C

The answer is far simpler than all the above answers. The physical drive list is actually stored in a Registry key which also gives the device mapping.

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\disk\Enum

Count is the number of PhysicalDrive# and each numbered Registry Value is the corresponding physical drive.

For example, Registry Value "0" is PhysicalDrive0. The value is the actual device PhysicalDrive0 is mapped to. The value contained here can be passed into CM_Locate_DevNode within parameter pDeviceID to use the plug and play services. This will allow you to gather a wealth of information on the device. Such as the properties from Device Manager like "Friendly Display Name" if you need a name for the drive, serial numbers and more.

There is no need for WMI services which may not be running on the system or other hackery and this functionality has been present in Windows since at least 2000 and continues to be the case in Windows 10.

Solution 5 - C

I've modified an open-source program called "dskwipe" in order to pull this disk information out of it. Dskwipe is written in C, and you can pull this function out of it. The binary and source are available here: dskwipe 0.3 has been released

The returned information will look something like this:

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

Solution 6 - C

The only correct answer is the one by @Grodriguez, and here's a code that he was too lazy to write:

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

I think that installation of Windows Driver Development Kit is quite a lengthy process, so I've included the declarations one needs to use DeviceIoControl for this task.

Solution 7 - C

GetLogicalDrives() enumerates all mounted disk partitions, not physical drives.

You can enumerate the drive letters with (or without) GetLogicalDrives, then call QueryDosDevice() to find out which physical drive the letter is mapped to.

Alternatively, you can decode the information in the registry at HKEY_LOCAL_MACHINE\SYSTEM\MountedDevices. The binary data encodings there are not obvious, however. If you have a copy of Russinovich and Solomon's book Microsoft Windows Internals, this registry hive is discussed in Chapter 10.

Solution 8 - C

The only sure shot way to do this is to call CreateFile() on all \\.\Physicaldiskx where x is from 0 to 15 (16 is maximum number of disks allowed). Check the returned handle value. If invalid check GetLastError() for ERROR_FILE_NOT_FOUND. If it returns anything else then the disk exists but you cannot access it for some reason.

Solution 9 - C

Thic WMIC command combination works fine:

wmic volume list brief

Solution 10 - C

If you only need to look at the existing disks, this one will suffice:

powershell "get-physicaldisk"

Solution 11 - C

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!

Solution 12 - C

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");

Solution 13 - C

I just ran across this in my RSS Reader today. I've got a cleaner solution for you. This example is in Delphi, but can very easily be converted to C/C++ (It's all Win32).

Query all value names from the following registry location: HKLM\SYSTEM\MountedDevices

One by one, pass them into the following function and you will be returned the device name. Pretty clean and simple! 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;

Solution 14 - C

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

Solution 15 - C

Make a list of all letters in the US English Alphabet, skipping a & b. "CDEFGHIJKLMNOPQRSTUVWXYZ". Open each of those drives with CreateFile e.g. CreateFile("\\.\C:"). If it does not return INVALID_HANDLE_VALUE then you got a 'good' drive. Next take that handle and run it through DeviceIoControl to get the Disk #. See my related answer for more details.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCiNNView Question on Stackoverflow
Solution 1 - CVonCView Answer on Stackoverflow
Solution 2 - CGrodriguezView Answer on Stackoverflow
Solution 3 - CarunView Answer on Stackoverflow
Solution 4 - CHooliganCoderView Answer on Stackoverflow
Solution 5 - CMickView Answer on Stackoverflow
Solution 6 - Cpolkovnikov.phView Answer on Stackoverflow
Solution 7 - CDie in SenteView Answer on Stackoverflow
Solution 8 - CanniView Answer on Stackoverflow
Solution 9 - Cuser2506992View Answer on Stackoverflow
Solution 10 - Cotto_View Answer on Stackoverflow
Solution 11 - CliverwortView Answer on Stackoverflow
Solution 12 - CJust ShadowView Answer on Stackoverflow
Solution 13 - CMickView Answer on Stackoverflow
Solution 14 - CphandinhlanView Answer on Stackoverflow
Solution 15 - Cunixman83View Answer on Stackoverflow