Перечислите все разделы и проверьте, являются ли они 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).

Это значение (см. здесь ):

PARTITION_ENTRY_UNUSED : 0x00 : неиспользуемый раздел записи.

Это странно, так как, я могу подтвердить, раздел действительно NTFS.

Вопросы:

  • Известно ли, что IOCTL_DISK_GET_PARTITION_INFO не дает стопроцентной надежности для получения типа раздела?

  • Какой способ перечисления всех томов NTFS может быть более надежным?


Примечание. Я также рассматривал использование IOCTL_DISK_GET_PARTITION_INFO_EX вместо IOCTL_DISK_GET_PARTITION_INFO, но затем структура PARTITION_INFORMATION_EX не дает информации о PartitionType, тогда как структура PARTITION_INFORMATION предоставляет доступ к PartitionType.


person Basj    schedule 20.03.2019    source источник
comment
Вы не занимаетесь обработкой ошибок. Вы не проверяете, чтобы убедиться, что CreateFile() и DeviceIoControl() действительно успешны, прежде чем оценивать diskInfo.PartitionType. Другой способ найти разделы NTFS — использовать GetLogicalDrives/Strings() и GetVolumeInformation(), параметр lpFileSystemNameBuffer вернет имя файловой системы на каждом запрашиваемом диске...   -  person Remy Lebeau    schedule 20.03.2019
comment
... По крайней мере, вы все равно должны использовать GetLogicalDrives/Strings(). Это уменьшит количество дисков, которые вы пытаетесь запросить, даже если вы придерживаетесь CreateFile()/DeviceIoControl(). Вместо того, чтобы вслепую перебирать все 26 букв алфавита, вам нужно запрашивать только те, которые действительно назначены диску.   -  person Remy Lebeau    schedule 20.03.2019
comment
@RemyLebeau На самом деле в моем исходном коде я использовал GetLogicalDrives, я не включил его сюда, потому что думал, что он не имеет отношения к проблеме, но вы правы: это важная часть, поэтому я отредактировал вопрос, чтобы включить Это. (Извините, я должен был включить это раньше). С текущим кодом, что бы вы изменили? Если у вас есть возможность, не могли бы вы опубликовать пример кода в ответе? Заранее спасибо.   -  person Basj    schedule 20.03.2019
comment
Используйте wmi запрос для перечисления всех разделы, что-то вроде PowerShell Get-WmiObject -Class "Win32_Volume" -Namespace "ROOT\CIMV2" | Select-Object -Property DriveLetter, Label, FileSystem, DeviceId (начиная с Windows 8, используйте -namespace "ROOT\Microsoft\Windows\Storage" -classname "MSFT_Volume" и -Property DriveLetter, FileSystemLabel, FileSystem, UniqueId).   -  person JosefZ    schedule 20.03.2019
comment
@JosefZ - wmi - очень неэффективный способ, это всего лишь удаленный вызов. для перечисления дисков томам необходимо перечислить интерфейсы через config API   -  person RbMm    schedule 20.03.2019
comment
и вам нужно использовать IOCTL_DISK_GET_PARTITION_INFO_EX для проверки типа раздела   -  person RbMm    schedule 20.03.2019
comment
@RbMm Если возможно, не могли бы вы опубликовать полный пример кода? Потому что я вижу много разных техник: GetLogicalDrives, DeviceIoControl, IOCTL_DISK_GET_PARTITION_INFO против IOCTL_DISK_GET_PARTITION_INFO_EX, Get-WMiObject, GetVolumeInformationW и т. д., так что мы легко можем запутаться. Хороший ответ был бы очень полезен для людей, которые хотят в будущем перечислять тома NTFS.   -  person Basj    schedule 20.03.2019
comment
@RemyLebeau Или, может быть, у вас есть пример кода (см. предыдущий комментарий)?   -  person Basj    schedule 20.03.2019
comment
тут вопрос в постановке задачи. что дают, что получают. связывать ли информацию с чем-либо. и более конкретный вопрос. wmi конечно, совсем не решение.   -  person RbMm    schedule 20.03.2019
comment
@Basj много разных способов решить эту проблему. Так что попробуйте их все и посмотрите, какой из них лучше всего подходит для вашей ситуации и набора навыков.   -  person Remy Lebeau    schedule 20.03.2019
comment
@RemyLebeau Я только что проверил возвращаемые значения. Для ret = DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &diskInfo, sizeof(diskInfo), &lpBytesReturned, NULL); на компьютере, где произошел сбой, я однажды получил 0 0 для lpBytesReturned и ret. После перезагрузки у меня было 2005860068 0. В обоих случаях 0 вместо ret указывает на сбой. Любая идея, как я могу исследовать дальше?   -  person Basj    schedule 20.03.2019
comment
@Basj Вам нужно проверить возвращаемое значение CreateFile() перед вызовом DeviceIoControl(). CreateFile() возвращает INVALID_HANDLE_VALUE в случае ошибки. Кроме того, вам нужно использовать GetLastError(), чтобы узнать, ПОЧЕМУ каждая из этих функций не работает. Например: dev = CreateFile(...); if (dev == INVALID_HANDLE_VALUE) { DWORD err = GetLastError(); ... } else { ret = DeviceIoControl(dev, ...); if (ret == FALSE) { DWORD err = GetLastError(); ... } CloseHandle(dev); }   -  person Remy Lebeau    schedule 20.03.2019
comment
@RemyLebeau Спасибо. Я последовал вашему совету, и исследование показывает, что CreateFile завершается успешно, а DeviceIoControl терпит неудачу (возвращает 0/FALSE). GetLastError() это 1. Я посмотрел здесь, но не нашел причина ошибки.   -  person Basj    schedule 20.03.2019
comment
@RbMm Я также рассматривал возможность использования IOCTL_DISK_GET_PARTITION_INFO_EX вместо IOCTL_DISK_GET_PARTITION_INFO, но тогда структура PARTITION_INFORMATION_EX не дает информации о PartitionType, тогда как структура PARTITION_INFORMATION дает доступ к PartitionType. Что вы думаете?   -  person Basj    schedule 20.03.2019
comment
@RemyLebeau Если у вас есть несколько минут, как вы думаете, можно ли поболтать (не очень долго). Целый день трачу на это, а ошибку найти не могу ;) (см. мое предыдущее сообщение про GetLastError).   -  person Basj    schedule 20.03.2019
comment
@Basj для Коды системных ошибок (0-499), код ошибки 1 — ERROR_INVALID_FUNCTION. Это означает, что IOCTL_DISK_GET_PARTITION_INFO не поддерживается устройством, которое вы передали DeviceIoControl().   -  person Remy Lebeau    schedule 21.03.2019
comment
@Basj PARTITION_INFORMATION_EX, кажется, не дает информации о PartitionType - да, это так. PARTITION_INFORMATION_EX имеет поле PartitionStyle, которое говорит вам, следует ли смотреть на поле Mbr или Gpt для получения дополнительной информации. Обе эти структуры имеют поле PartitionType. Mbr.PartitionType это BYTE, Gpt.PartitionType это GUID.   -  person Remy Lebeau    schedule 21.03.2019
comment
Давайте продолжим обсуждение в чате.   -  person Basj    schedule 21.03.2019
comment
Для downvoter: было бы здорово, если бы вы могли оставить сообщение о том, как улучшить этот вопрос. Я провел обширное исследование в течение нескольких часов, и теперь я опубликовал результаты наблюдений в ответе и попытался обобщить комментарии вместе. Было бы обидно, если бы вопрос/ответ однажды был удален из-за отрицательных голосов, в результате чего были бы потеряны эти отчеты о рабочих и неработающих решениях.   -  person Basj    schedule 21.03.2019


Ответы (2)


Как говорит @RemyLebeau, вы не проверяете возвращаемое значение для каждого вызова.

PARTITION_ENTRY_UNUSED часто означает, что вызов DeviceIoControl() не удался. Это зависит от разрешений вашего пользователя. Вам следует проверить права доступа вашего пользователя, чтобы узнать, есть ли у него разрешение FILE_READ_DATA (включенное в GENERIC_READ) на том C:. В моей тестовой среде, если у вас нет доступа к открытому тому C: с помощью GENERIC_READ, CreateFile() возвращает INVALID_HANDLE_VALUE, а затем DeviceIoControl() также не работает.

РЕДАКТИРОВАТЬ:

Я предлагаю использовать GetVolumeInformation(), например:

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

И вы увидите информацию о типе в буфере fs.

person Drake Wu    schedule 20.03.2019
comment
Спасибо за ваш ответ @DrakeWu-MSFT. Какое решение вы бы предложили для надежного перечисления разделов NTFS? Примечание. Программное обеспечение, над которым я работаю, всегда запускается от имени администратора (поэтому разрешение пользователя не должно быть проблемой). - person Basj; 20.03.2019
comment
Итак, не могли бы вы убедиться, что звонки успешны. Я предлагаю использовать GetVolumeInformation(). Смотрите мою правку. - person Drake Wu; 20.03.2019
comment
он работает с GetVolumeInformationW @DrakeWu-MSFT, я нашел "NTFS" в fs. Надежно ли проверить, равно ли fs NTFS? Или может быть и "ntfs" в некоторых версиях Windows? - person Basj; 20.03.2019
comment
@Basj это действительно имеет значение? Просто используйте сравнение без учета регистра. - person Remy Lebeau; 21.03.2019
comment
@RemyLebeau Я имею в виду в целом, документально ли это всегда "NTFS" или существуют версии Windows, для которых это может быть NTFS-something или IFS, или что-то небольшое изменение, означающее одно и то же? (Вы дали лучшее решение в чате, но этот вопрос интересен самостоятельно). - person Basj; 21.03.2019
comment
@Basj имена официально не задокументированы MS, о которых я знаю, но взгляните на Каковы возможные значения для строки FileSystemName, которую GetVolumeInformation возвращается?. - person Remy Lebeau; 21.03.2019

Я провел дальнейшее расследование благодаря комментариям @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 (см. Коды системных ошибок (0-499)).

Вывод (цитирую комментарий Реми):

Это означает, что IOCTL_DISK_GET_PARTITION_INFO не поддерживается устройством, которое вы передали в 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 не поддерживается на дисках с разделами GPT.

Итак, вот вывод:

  • используйте IOCTL_DISK_GET_PARTITION_INFO_EX вместо IOCTL_DISK_GET_PARTITION_INFO

  • вместо этого, вероятно, проще использовать GetVolumeInformation() и просто сравнить, является ли результат строкой "NTFS", как в другом ответе

  • #P13# <блочная цитата> #P14#
person Basj    schedule 20.03.2019
comment
найдите определение PARTITION_BASIC_DATA_GUID в diskguid.h. этот (EBD0A0A2-B9E5-4433-87C0-68B6B72699C7) не означает раздел ntfs. это не связано с файловой системой - person RbMm; 21.03.2019
comment
@RbMm Хорошо, я удалю эту часть в своем ответе. Не стесняйтесь публиковать лучший ответ, мне все еще интересно. - person Basj; 21.03.2019
comment
и вам не нужно проверять ntfs это или нет перед отправкой FSCTL_ENUM_USN_DATA - person RbMm; 21.03.2019
comment
@RbMm да, это то, что я упомянул в своем последнем абзаце, спасибо за эту точность. - person Basj; 21.03.2019