Enumerazione dei dischi

GUID_DEVINTERFACE_DISK identifica la classe dell'interfaccia del dispositivo del disco. I driver della classe disco forniti dal sistema registrano le istanze di questa classe per i dischi che espongono. L'enumerazione delle istanze abilitate è il modo indipendente dallo stack per individuare i percorsi dei dispositivi del disco: sia lo stack legacy che lo stack NVMe nativo espongono questa classe di interfaccia.

Questo è il motivo per cui è consigliabile enumerare il GUID dell'interfaccia anziché basarsi su un GUID della classe di installazione, ad esempio GUID_DEVCLASS_DISKDRIVE, che non è garantito che sia indipendente dallo stack.

GUID dell'interfaccia vs. GUID della classe di dispositivo

GUID_DEVINTERFACE_DISK e GUID_DEVCLASS_DISKDRIVE rispondono a domande diverse e non sono intercambiabili:

  • GUID_DEVINTERFACE_DISK identifica una classe di interfaccia, non un singolo percorso del dispositivo. L'enumerazione delle istanze abilitate restituisce percorsi di collegamento simbolico che le applicazioni possono passare a CreateFileW. L'handle risultante può essere usato per le operazioni supportate dal disco di destinazione, subordinatamente ai diritti di accesso richiesti e alle autorizzazioni applicabili. Un percorso enumerato non garantisce che l'apertura del disco abbia esito positivo: il dispositivo potrebbe essere rimosso o non essere disponibile prima che la richiesta di apertura sia completata.
  • GUID_DEVCLASS_DISKDRIVEè una classe di installazione del dispositivo usata da PnP/SetupAPI per l'installazione e la classificazione dei dispositivi, ad esempio l'installazione del driver, il raggruppamento Gestione dispositivi, la corrispondenza INF e attività di manutenzione simili. Non è garantito che un'istanza del dispositivo appartenente a questa classe di installazione esponga un'interfaccia che è possibile aprire, ed enumerare in base alla classe di installazione non dice nulla sulle interfacce funzionali, se presenti, attualmente esposte dall'istanza del dispositivo.
  • Lo stack di dischi legacy (disk.sys/classpnp.sys) usa la GUID_DEVCLASS_DISKDRIVE classe di installazione, mentre lo stack NVMe nativo (nvmedisk.sys) usa la propria classe di installazione diversa, {75416e63-5912-4dfa-ae8f-3efaccaffb14}. Enumerare solo GUID_DEVCLASS_DISKDRIVE non rileverebbe quindi tutti i dischi gestiti dallo stack NVMe nativo. GUID_DEVINTERFACE_DISK non presenta questo divario, poiché i suoi due stack registrano la stessa interfaccia per ogni disco che gestiscono.

Ambito di enumerazione

Questo codice di esempio esegue un'enumerazione una tantum delle interfacce del dispositivo disco attualmente abilitate. Non enumera volumi o lettere di unità e l'elenco restituito non è un inventario continuamente aggiornato.

CM_GET_DEVICE_INTERFACE_LIST_PRESENT seleziona le interfacce presenti/abilitate. Non indica se un disco è online in Gestione disco.

Per un'applicazione a esecuzione prolungata che tiene traccia degli arrivi e delle rimozioni, registrare le notifiche dell'interfaccia dispositivo con CM_Register_Notification prima di eseguire l'enumerazione iniziale. Riconciliare le notifiche con l'elenco iniziale e gestire i duplicati, perché un'interfaccia in arrivo può comparire in entrambi. Rilasciare la registrazione della notifica con CM_Unregister_Notification al termine. La rimozione del dispositivo può ancora entrare in race condition con l'apertura di un percorso o l'esecuzione di I/O.

Codice di esempio: enumerare i dischi

Note

La ripetizione di tentativi gestisce le modifiche tra la query di dimensioni e il recupero dell'elenco; non garantisce il successo finale o la disponibilità continua dei dispositivi restituiti. L'esempio riprova CR_BUFFER_SMALL senza un limite di tentativi, quindi gli arrivi ripetuti dell'interfaccia possono teoricamente far sì che continui a riprovare indefinitamente. Altri errori vengono segnalati e causano l'interruzione dell'enumerazione.

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