Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
GUID_DEVINTERFACE_DISK идентифицирует класс интерфейса дискового устройства. Системные драйверы классов дисков регистрируют экземпляры этого класса для дисков, которые они представляют системе. Перечисление активных экземпляров — это не зависящий от стека способ обнаружения путей к устройству диска: как устаревший стек, так и стек собственного NVMe предоставляют этот класс интерфейса.
Именно поэтому следует перечислить GUID интерфейса, а не ориентироваться на GUID класса установки, например GUID_DEVCLASS_DISKDRIVE, который не гарантированно является независимым от стека.
GUID интерфейса в сравнении с GUID класса устройства
GUID_DEVINTERFACE_DISK и GUID_DEVCLASS_DISKDRIVE отвечают на различные вопросы и не взаимозаменяемы:
-
GUID_DEVINTERFACE_DISKопределяет класс интерфейса, а не отдельный путь устройства. Перечисление включенных экземпляров возвращает пути символьных ссылок, которые приложения могут передавать в качестве аргумента функции CreateFileW. Результирующий дескриптор можно использовать для операций, поддерживаемых целевым диском, при условии запрошенных прав доступа и применимых разрешений. Обнаруженный системой путь к устройству не гарантирует успешное открытие диска: устройство может быть удалено или стать недоступным до завершения запроса на открытие. -
GUID_DEVCLASS_DISKDRIVE— это класс установки устройства, используемый PnP/SetupAPI для установки и классификации устройств, таких как установка драйверов, группировка в диспетчер устройств, сопоставление INF и аналогичные задачи обслуживания. Экземпляр устройства, принадлежащий этому классу установки, не гарантирует, что он предоставляет интерфейс, который можно открыть, и перечисление по классу установки ничего не говорит о том, какие функциональные интерфейсы, если таковые имеются, этот экземпляр устройства в настоящее время предоставляет. - Устаревший стек дисков (
disk.sys/classpnp.sys) использует класс установкиGUID_DEVCLASS_DISKDRIVE, в то время как нативный стек NVMe (nvmedisk.sys) использует собственный, другой класс установки{75416e63-5912-4dfa-ae8f-3efaccaffb14}. Следовательно, перечисление толькоGUID_DEVCLASS_DISKDRIVEприведёт к пропуску всех дисков, управляемых нативным стеком NVMe.GUID_DEVINTERFACE_DISKне имеет этого разрыва, так как оба стека регистрируют один и тот же интерфейс для каждого диска, которым они управляют.
Область перечисления
В этом примере выполняется однократное перечисление интерфейсов дисковых устройств, в настоящее время включённых. Он не перечисляет тома или буквы дисков, а возвращаемый список не является постоянно обновляемым списком.
CM_GET_DEVICE_INTERFACE_LIST_PRESENT выбирает имеющиеся/доступные и включённые интерфейсы. Он не указывает, находится ли диск в состоянии «В сети» в службе управления дисками.
Для приложения, работающего длительное время и отслеживающего подключения и отключения, зарегистрируйте уведомления интерфейса устройства с помощью CM_Register_Notification перед выполнением первоначального перечисления. Сопоставьте уведомления с начальным списком и обработайте дубликаты, так как входящий интерфейс может отображаться в обоих списках. По завершении отмените регистрацию уведомлений с помощью CM_Unregister_Notification. Удаление устройств по-прежнему может конфликтовать с открытием пути или выполнением ввода-вывода.
Пример кода: перечисление дисков
Note
Механизм повторных попыток обрабатывает изменения между запросом размера и извлечением списка; он не гарантирует конечного успеха или непрерывной доступности возвращенных устройств. Пример выполняет повторные попытки CR_BUFFER_SMALL без ограничения числа попыток, поэтому повторные вызовы интерфейса теоретически могут приводить к повторным попыткам бесконечно. О других ошибках сообщается, и они прерывают перечисление.
#define INITGUID
#include <windows.h>
#include <ntddstor.h> // GUID_DEVINTERFACE_DISK
#include <cfgmgr32.h>
#include <stdio.h>
#include <stdlib.h> // malloc, free
#include <wchar.h> // wcslen
#pragma comment(lib, "cfgmgr32.lib")
//
// Opens and closes the disk using its device interface path.
// Zero desired access is intentional: this sample does not read or write data.
// When extending it, request the access required by the intended operation:
// GENERIC_READ for reads, GENERIC_WRITE for writes, and the access required
// by the specific IOCTL. Applicable device permissions also apply.
//
static void
ProcessDisk(
const wchar_t* DevicePath
)
{
HANDLE handle;
handle = CreateFileW(
DevicePath,
0, // No data read/write access requested.
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (handle == INVALID_HANDLE_VALUE)
{
wprintf(L" open failed (%lu): %s\n", GetLastError(), DevicePath);
return;
}
// TODO: perform a supported query whose access requirements are satisfied
// by this handle. Adjust the CreateFileW access argument before adding
// operations that require read or write access.
CloseHandle(handle);
}
int
wmain(
void
)
{
wchar_t* list;
CONFIGRET cr;
ULONG len;
DWORD index;
len = 0;
RetryEnumeration:
//
// Select currently enabled disk device interfaces.
// This does not indicate the disk's online/offline state.
//
cr = CM_Get_Device_Interface_List_SizeW(
&len,
(LPGUID)&GUID_DEVINTERFACE_DISK,
NULL,
CM_GET_DEVICE_INTERFACE_LIST_PRESENT
);
if (cr != CR_SUCCESS || len == 0)
{
wprintf(L"CM_Get_Device_Interface_List_SizeW failed: 0x%lx\n", cr);
return 1;
}
//
// The result is a REG_MULTI_SZ style buffer: a contiguous run of
// NUL-terminated device paths, terminated by an extra empty string.
// 'len' is expressed in characters (including all NULs), so size the
// allocation in wchar_t units, not bytes.
//
list = (wchar_t*)malloc((size_t)len * sizeof(wchar_t));
if (list == NULL)
{
wprintf(L"Out of memory allocating %lu characters\n", len);
return 1;
}
cr = CM_Get_Device_Interface_ListW(
(LPGUID)&GUID_DEVINTERFACE_DISK,
NULL,
list,
len,
CM_GET_DEVICE_INTERFACE_LIST_PRESENT
);
if (cr == CR_BUFFER_SMALL)
{
// An interface may have arrived after the size query.
free(list);
goto RetryEnumeration;
}
if (cr != CR_SUCCESS)
{
wprintf(L"CM_Get_Device_Interface_ListW failed: 0x%lx\n", cr);
free(list);
return 1;
}
//
// Walk the multi-string buffer, one NUL-terminated device path per
// iteration, stopping at the empty string that terminates the list.
// 'list' is guaranteed NUL-terminated by the API contract, so this
// cannot run past the allocation.
//
index = 0;
for (wchar_t* current = list; *current != L'\0'; current += wcslen(current) + 1)
{
wprintf(L"[%lu] %s\n", index, current);
ProcessDisk(current);
index++;
}
free(list);
return 0;
}
Связанные материалы
- Перечисление установленных интерфейсов устройств
- CM_Get_Device_Interface_ListW
- CM_Get_Device_Interface_List_SizeW
- IOCTL_STORAGE_GET_DEVICE_NUMBER
- IOCTL_STORAGE_QUERY_PROPERTY
- GUID_DEVINTERFACE_DISK
- IoGetDeviceInterfaces (эквивалент режима ядра)
- CreateFileW: запрошенные права доступа
- Определение кодов управления ввода-вывода: руководство по настройке битов Access
- CM_Register_Notification: порядок регистрации и начальный перебор