Як перерахувати фізичні диски в Windows? Для того, щоб отримати список "\\\\.\PhysicalDrive0"
доступних.
Відповіді:
wmic - це дуже повний інструмент
wmic diskdrive list
надати (занадто багато) детальний список, наприклад
щоб отримати менше інформації
wmic diskdrive list brief
Себастьян Годелет згадує в коментарях :
У C:
system("wmic diskdrive list");
Як зазначалося, ви також можете викликати WinAPI, але ... як показано в " Як отримати дані з WMI за допомогою програми C? ", Це досить складно (і, як правило, це робиться на C ++, а не на C).
Або з PowerShell:
Get-WmiObject Win32_DiskDrive
system("wmic diskdrive list");
в С
Один із способів зробити це:
Перерахуйте логічні диски за допомогою 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
зберігає достатньо пам’яті для одного екстенту , тож ви можете назвати його так, як пропонує Гродрігес, а потім перевірити, success || ERROR_MORE_DATA == GetLastError()
оскільки ми в будь-якому випадку дбаємо лише про перший екстент .
Це може бути на 5 років запізно :). Але оскільки я поки що не бачу відповіді на це, додаючи це.
Ми можемо використовувати 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;
}
Відповідь набагато простіший, ніж усі вищезазначені відповіді. Список фізичних накопичувачів фактично зберігається в ключі реєстру, який також надає відображення пристрою.
HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \ disk \ Enum
Count - це число PhysicalDrive #, а кожне пронумероване значення реєстру - це відповідний фізичний диск.
Наприклад, значенням реєстру "0" є PhysicalDrive0. Значення - це фактичний пристрій, на який відображається пристрій PhysicalDrive0. Значення, що міститься тут, може бути передане в CM_Locate_DevNode в межах параметра pDeviceID для використання служб підключення та відтворення. Це дозволить вам зібрати велику кількість інформації на пристрої. Такі властивості з Диспетчера пристроїв, як "Дружнє відображуване ім'я", якщо вам потрібно ім'я для накопичувача, серійні номери тощо.
Немає необхідності в послугах WMI, які можуть не працювати в системі чи інших хакерських системах, і ця функціональність присутня в Windows щонайменше з 2000 року і продовжує бути такою в Windows 10.
Я модифікував програму з відкритим кодом "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
Єдиною правильною відповіддю є відповідь @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 Driver Development Kit є досить тривалим процесом, тому я включив декларації, які потрібно використовувати DeviceIoControl
для цього завдання.
DeviceIoControl
неправильно. Ви не можете припустити, що існує лише одна міра. Вам потрібно запитати DeviceIoControl
про розмір необхідного VOLUME_DISK_EXTENTS
буфера.
DeviceIoControl
пам'ять до такого синглу, VOLUME_DISK_EXTENTS
як ви робили, але потім перевіряєте, чи він не вдався, і якщо так, то подивіться, чи повернувся він ERROR_MORE_DATA
в GetLastError
і якщо так, то виділити новий буфер для NumberOfDiskExtents
ряду DISK_EXTENT
структур (що він повернувся) і зателефонувати DeviceIoControl
ще раз і переконайтеся , що це вдасться. Ви, очевидно, не робите всього цього, якщо перший заклик до DeviceIoControl
успіху.
Єдиний надійний спосіб зробити це - зателефонувати CreateFile()
на всі, \\.\Physicaldiskx
де x - від 0 до 15 (16 - максимально дозволена кількість дисків). Перевірте повернуте значення дескриптора. Якщо помилкова перевірка GetLastError()
на наявність ERROR_FILE_NOT_FOUND . Якщо він повертає щось інше, тоді диск існує, але ви не можете отримати до нього доступ з якихось причин.
15
? Продовжуйте підраховувати, поки не провалитесь. Я не впевнений, що якийсь номер пристрою буде пропущений ОС.
GetLogicDrives () перераховує всі змонтовані розділи диска, а не фізичні диски.
Ви можете перерахувати букви накопичувача за допомогою (або без) GetLogicDrives, а потім зателефонувати QueryDosDevice (), щоб дізнатись, на який фізичний диск призначається лист.
Ви також можете декодувати інформацію в реєстрі за адресою HKEY_LOCAL_MACHINE \ SYSTEM \ MountedDevices. Проте кодування двійкових даних там не є очевидним. Якщо у вас є копія книги Русіновича та Соломона Microsoft Windows Internals, цей вулик реєстру обговорюється в розділі 10.
Товста комбінація команд WMIC чудово працює:
wmic volume list brief
Можливо, захочеться включити старі накопичувачі A: та B: так як ви ніколи не знаєте, хто може ними користуватися! Мені набридло, що USB-накопичувачі стикаються з двома накопичувачами SDHC, призначеними лише для Readyboost. Я присвоїв їм високі літери Z: Y: за допомогою утиліти, яка призначатиме літери дисків пристроям за вашим бажанням. Я здивувався .... Чи можу я зробити букву накопичувача Readyboost A:? ТАК! Чи можу я поставити свою другу букву накопичувача SDHC як B:? ТАК!
Я раніше використовував флоппі-диски, ніколи не думав, що A: або B: стане в нагоді для Readyboost.
Моя думка полягає в тому, що не припускайте, що A: & B: ні для кого не буде використовуватися. Ви навіть можете виявити, що використовується стара команда SUBST!
Я щойно натрапив на це у своєму RSS Reader сьогодні. У мене є більш чисте рішення для вас. Цей приклад є в 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;
Якщо ви хочете отримати "фізичний" доступ, ми розробляємо цей API, який врешті-решт дозволить вам спілкуватися з пристроями зберігання даних. Це відкритий код, і ви можете побачити поточний код для деякої інформації. Перевірте ще більше функцій: https://github.com/virtium/vtStor
Ось це нове рішення робить це з виконанням викликів WMI.
Тоді все, що вам потрібно зробити, це просто зателефонувати:
queryAndPrintResult(L"SELECT * FROM Win32_DiskDrive", L"Name");
Якщо вам потрібно лише переглянути існуючі диски, цього буде достатньо:
powershell "get-physicaldisk"
Складіть список усіх літер в американському алфавіті, пропускаючи a & b. "CDEFGHIJKLMNOPQRSTUVWXYZ". Відкрийте кожен із цих накопичувачів, CreateFile
наприклад CreateFile("\\.\C:")
. Якщо він не повертається, INVALID_HANDLE_VALUE
ви отримали "хороший" привід. Далі візьміть цей дескриптор і проведіть його, DeviceIoControl
щоб отримати номер диска. Докладнішу інформацію див. У моїй відповіді .