枚举所有分区并测试它们是否为 NTFS

Enumerate all partitions and test if they are NTFS

我正在使用:

DWORD d = GetLogicalDrives();
for (int i = 0; i < 26; i++)
{
    if ((1 << i) & d) // drive letter 'A' + i present on computer
    {
        wstring s = std::wstring(L"\\.\") + wchar_t('A' + i) + L":";

        PARTITION_INFORMATION diskInfo;
        DWORD dwResult;
        HANDLE dev = CreateFile(LPWSTR(s.c_str()), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
        DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &diskInfo, sizeof(diskInfo), &dwResult, NULL);
        CloseHandle(dev);
        if (diskInfo.PartitionType == PARTITION_IFS) 
        {
            ...
        }
    }
}

枚举计算机的所有 NTFS 分区。

它可以在我的 Windows 7、我试过的 Windows 8.1 和 Windows 10 计算机上运行。

但它在另一台 Windows 10 计算机上失败:在这台计算机上,卷 C:diskInfo.PartitionType 值等于 0x00,而不是 0x07 (PARTITION_IFS).

这个值为(见doc here):

PARTITION_ENTRY_UNUSED : 0x00 : An unused entry partition.

这很奇怪,因为我可以确认,该分区确实是 NTFS。

问题:


注意:我还查看了使用 IOCTL_DISK_GET_PARTITION_INFO_EX instead of IOCTL_DISK_GET_PARTITION_INFO but then the structure PARTITION_INFORMATION_EX does not seem to give informations about PartitionType, whereas the structure PARTITION_INFORMATION does give access to PartitionType

正如@RemyLebeau 所说,您没有检查每个调用的 return 值。

PARTITION_ENTRY_UNUSED 通常表示 DeviceIoControl() 调用失败。这取决于您的用户的权限。您应该检查您的用户的访问权限,看它是否对卷 C: 具有 FILE_READ_DATA 权限(包含在 GENERIC_READ 中)。在我的测试环境中,如果您无法使用 GENERIC_READCreateFile() returns INVALID_HANDLE_VALUE 打开卷 C:,然后 DeviceIoControl() 失败以及。

编辑:

我建议使用GetVolumeInformation(),例如:

wchar_t fs[MAX_PATH + 1] = { 0 };
GetVolumeInformationW(L"C:\", NULL, 0, NULL, NULL, NULL, fs, MAX_PATH + 1);

您将在 fs 缓冲区中看到类型信息。

感谢@RemyLebeau 的评论,我做了进一步的调查:

HANDLE dev = CreateFile(..., GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);

if (dev == INVALID_HANDLE_VALUE) 
{ 
    DWORD err = GetLastError();  // then MessageBox       
} 
else
{ 
    BOOL ret = DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &diskInfo, sizeof(diskInfo), &dwResult, NULL);

    if (ret == FALSE) 
    { 
        DWORD err = GetLastError();  // then MessageBox
    } 
    CloseHandle(dev); 
} 

在失败的计算机上(Windows 10 的计算机)。我发现 CreateFile 成功但随后 DeviceIoControl 失败,GetLastError1ERROR_INVALID_FUNCTION(参见 System Error Codes (0-499))。

结论(我引用雷米的评论):

That means IOCTL_DISK_GET_PARTITION_INFO is not supported by the device you passed to DeviceIoControl().

然后我尝试 IOCTL_DISK_GET_PARTITION_INFO_EX:

PARTITION_INFORMATION_EX diskInfo;
BOOL ret = DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &diskInfo, sizeof(diskInfo), &lpBytesReturned, NULL);

然后就成功了。我可以看到 diskInfo.PartitionStylePARTITION_STYLE_GPT (=1),这就是 IOCTL_DISK_GET_PARTITION_INFO 失败的原因。我再次引用雷米的评论:

IOCTL_DISK_GET_PARTITION_INFO is not supported on GPT partitioned drives.

所以这是结论:

  • 使用IOCTL_DISK_GET_PARTITION_INFO_EX代替IOCTL_DISK_GET_PARTITION_INFO

  • 使用 GetVolumeInformation() 可能更容易,只需比较结果是否为 "NTFS" 字符串,如

  • 在我的特殊情况下,我最初想在 尝试使用 DeviceIoControl(hVol, FSCTL_ENUM_USN_DATA, ...) 进行索引之前 测试卷是否为 NTFS,因为我认为此类 MFT 查询仅限于 NTFS 卷。事实上,一个更简单的解决方案是不测试它是否是 NTFS,而只执行 FSCTL_ENUM_USN_DATA。根据文档,可能发生的最坏情况是 FSCTL_ENUM_USN_DATA 失败并出现 ERROR_INVALID_FUNCTION 错误:

    "ERROR_INVALID_FUNCTION The file system on the specified volume does not support this control code."