Search code examples
powershellwindows-10disk

Finding physical drive of unpartitioned disk from drive letter


I need to automate the initialization of a microSD on Windows10 (physical cleanup, special partitioning, format...). The initial information I have is the drive letter the OS assigns tho the drive, and I want to find related disk number and physycal device. This issue is discussed in many articles (e.g. Identify physical drive from drive letter), but all solutions I have tried fail when the disk is not formatted and not partitioned. It is a very special case: the device is registered as physical drive, the letter is assignet to a volume, but I cant't find a way to get link between these namespaces. Usual functions (e.g. powershell get-ciminstance / get-cimassociatedinstance) fail to detect an association between disk and volume. The only tool that seems to know the connection is Windows' disk manager, as it shows the empty disk and marks it with the corresponding drive letter. Is there a way to do the same programmatically (e.g. in powershell)?


Solution

  • Here the solution I eventually found. Raymond Chen's blog is the source of information (https://devblogs.microsoft.com/oldnewthing/20201019-00/?p=104380). I hope it can help. Here a minimal example (error check removed, multibyte only...):

    // 1) Convert a path (F:\) in a volume path
    // NOTE: we could skip this step, as root path
    // and volume path are usually the same.
    
    char volpath[MAX_PATH];
    volpath[0] = '\0';
    GetVolumePathName("f:\\", volpath, sizeof(volpath));
    
    // 2) Get volume name from volume path
    char name[MAX_PATH];
    name[0] = '\0';
    GetVolumeNameForVolumeMountPoint(volpath, name, sizeof(name));
    
    // 3) Remove trailing \ to get volume objct name
    int l = strlen(name);
    if (name[l - 1] == '\\') {
        name[l - 1] = '\0';
    }
    
    // 4) Open volume object
    HANDLE handle = CreateFile(name,
        0,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS,
        NULL);
    
    // 5) Get the list of underlying physical drives
    // NOTE: we are assuming we are in the simplest case:
    // a volume covering a single drive. In general, the
    // service would fill an array of disk descriptors
    // if the volume spanned over multiple drives.
    
    VOLUME_DISK_EXTENTS singleExtent;
    memset(&singleExtent, 0, sizeof(singleExtent));
    
    DWORD bytesWritten = 0;
    DeviceIoControl(handle, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
        NULL, 0,
        &singleExtent, sizeof(singleExtent),
        &bytesWritten,
        NULL);
    
    // 6) Enjoy the result.
    
    printf("ndrive=%u, drive=%u len=%llu off=%llu\n",
        singleExtent.NumberOfDiskExtents,
        singleExtent.Extents[0].DiskNumber,
        singleExtent.Extents[0].ExtentLength,
        singleExtent.Extents[0].StartingOffset);
    printf("Physical device is \\\\.\\PhysicalDrive%u\n",
        singleExtent.Extents[0].DiskNumber);