枚举所有分区并测试它们是否为 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
获取分区类型不是100%靠谱?
枚举所有 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_READ
、CreateFile()
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
失败,GetLastError
为 1
即 ERROR_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.PartitionStyle
是 PARTITION_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
如果diskInfo.PartitionStyle
为0(PARTITION_STYLE_MBR)则diskInfo.Mbr.PartitionType可以测试。 If it's 0x07, it's NTFS.
如果diskInfo.PartitionStyle
为1(PARTITION_STYLE_GPT)则可以测试diskInfo.Gpt.PartitionType,看这里:https://docs.microsoft.com/en-us/windows/desktop/api/winioctl/ns-winioctl-_partition_information_gpt. Even if the NTFS Wikipedia page提到GUID EBD0A0A2-B9E5-4433-87C0-68B6B72699C7
对于 GPT 情况下的 NTFS,此 GUID 实际上与文件系统无关(请参阅下面关于此的评论)。
使用 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."
我正在使用:
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
获取分区类型不是100%靠谱?枚举所有 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_READ
、CreateFile()
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
失败,GetLastError
为 1
即 ERROR_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.PartitionStyle
是 PARTITION_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
如果
diskInfo.PartitionStyle
为0(PARTITION_STYLE_MBR)则diskInfo.Mbr.PartitionType可以测试。 If it's 0x07, it's NTFS.如果
diskInfo.PartitionStyle
为1(PARTITION_STYLE_GPT)则可以测试diskInfo.Gpt.PartitionType,看这里:https://docs.microsoft.com/en-us/windows/desktop/api/winioctl/ns-winioctl-_partition_information_gpt. Even if the NTFS Wikipedia page提到GUIDEBD0A0A2-B9E5-4433-87C0-68B6B72699C7
对于 GPT 情况下的 NTFS,此 GUID 实际上与文件系统无关(请参阅下面关于此的评论)。
使用
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."