diff --git a/include/MUtils/OSSupport.h b/include/MUtils/OSSupport.h index 88e7c3b..c9af7b0 100644 --- a/include/MUtils/OSSupport.h +++ b/include/MUtils/OSSupport.h @@ -122,6 +122,20 @@ namespace MUtils } network_type_t; + /** + * \brief This enumeration specifies drive types + */ + typedef enum + { + DRIVE_TYPE_ERR = 0, ///< The drive type cannot be determined + DRIVE_TYPE_FDD = 1, ///< Floppy Drive, or Flash Card reader + DRIVE_TYPE_HDD = 2, ///< Hard Disk drive or Solid-State Drive + DRIVE_TYPE_NET = 3, ///< Remote/Network drive + DRIVE_TYPE_OPT = 4, ///< Optical disk srive, e.g. CD or DVD + DRIVE_TYPE_RAM = 5 ///< RAM disk + } + drive_type_t; + //System message MUTILS_API void system_message_nfo(const wchar_t *const title, const wchar_t *const text); MUTILS_API void system_message_wrn(const wchar_t *const title, const wchar_t *const text); @@ -189,6 +203,19 @@ namespace MUtils //Free diskspace MUTILS_API bool free_diskspace(const QString &path, quint64 &freeSpace); + /** + * \brief Detect drive type + * + * This function detetcs the type of the drive to which the given path is pointing. + * + * \param path The path to the drive whose type is to be detected. On the Windows platform, only the drive letter is relevant. + * + * \param fast_seeking Pointer to a variable that will be set to TRUE, if the drive supports "fast" seeking (e.g. SSD or similar device), or to FALSE otherwise. This parameter is optional and may be NULL. + * + * \return The function returns the type of the drive as a `OS::drive_type_t` value. In case of error, the value `DRIVE_TYPE_ERR` will be returned. + */ + MUTILS_API drive_type_t get_drive_type(const QString &path, bool *fast_seeking = NULL); + //Shell open MUTILS_API bool shell_open(const QWidget *parent, const QString &url, const bool explore = false); MUTILS_API bool shell_open(const QWidget *parent, const QString &url, const QString ¶meters, const QString &directory, const bool explore = false); diff --git a/include/MUtils/Version.h b/include/MUtils/Version.h index 62e497c..7acf336 100644 --- a/include/MUtils/Version.h +++ b/include/MUtils/Version.h @@ -68,7 +68,9 @@ namespace MUtils #endif #elif defined(_MSC_VER) #if (_MSC_VER == 1911) - #if((_MSC_FULL_VER == 191125508) || (_MSC_FULL_VER == 191125506)) + #if((_MSC_FULL_VER >= 191125542) && (_MSC_FULL_VER <= 191125547)) + "MSVC 2017.4"; + #elif((_MSC_FULL_VER >= 191125506) && (_MSC_FULL_VER <= 191125508)) "MSVC 2017.3"; #else #error Compiler version is not supported yet! diff --git a/src/OSSupport_Win32.cpp b/src/OSSupport_Win32.cpp index fc2a641..c96f74d 100644 --- a/src/OSSupport_Win32.cpp +++ b/src/OSSupport_Win32.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #pragma warning(push) #pragma warning(disable:4091) //for MSVC2015 #include @@ -48,6 +49,7 @@ #include #include #include +#include //Main thread ID static const DWORD g_main_thread_id = GetCurrentThreadId(); @@ -1230,6 +1232,121 @@ bool MUtils::OS::free_diskspace(const QString &path, quint64 &freeSpace) return false; } +/////////////////////////////////////////////////////////////////////////////// +// DRIVE TYPE +/////////////////////////////////////////////////////////////////////////////// + +static wchar_t get_drive_letter(const QString &path) +{ + QString nativePath = QDir::toNativeSeparators(path); + while (nativePath.startsWith("\\\\?\\") || nativePath.startsWith("\\\\.\\")) + { + nativePath = QDir::toNativeSeparators(nativePath.mid(4)); + } + if ((path.length() > 1) && (path[1] == QLatin1Char(':'))) + { + const wchar_t letter = static_cast(path[0].unicode()); + if (((letter >= 'A') && (letter <= 'Z')) || ((letter >= 'a') && (letter <= 'z'))) + { + return towupper(letter); + } + } + return L'\0'; /*invalid path spec*/ +} + +static QSet get_physical_drive_ids(const wchar_t drive_letter) +{ + QSet physical_drives; + wchar_t driveName[8]; + _snwprintf_s(driveName, 8, _TRUNCATE, L"\\\\.\\%c:", drive_letter); + const HANDLE hDrive = CreateFileW(driveName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (hDrive && (hDrive != INVALID_HANDLE_VALUE)) + { + const size_t BUFF_SIZE = sizeof(VOLUME_DISK_EXTENTS) + (32U * sizeof(DISK_EXTENT)); + VOLUME_DISK_EXTENTS *const diskExtents = (VOLUME_DISK_EXTENTS*)_malloca(BUFF_SIZE); + DWORD dwSize; + if (DeviceIoControl(hDrive, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, (LPVOID)diskExtents, (DWORD)BUFF_SIZE, (LPDWORD)&dwSize, NULL)) + { + for (DWORD index = 0U; index < diskExtents->NumberOfDiskExtents; ++index) + { + physical_drives.insert(diskExtents->Extents[index].DiskNumber); + } + } + _freea(diskExtents); + CloseHandle(hDrive); + } + return physical_drives; +} + +static bool incurs_seek_penalty(const DWORD device_id) +{ + wchar_t driveName[24]; + _snwprintf_s(driveName, 24, _TRUNCATE, L"\\\\?\\PhysicalDrive%u", device_id); + const HANDLE hDevice = CreateFileW(driveName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + bool seeking_penalty = true; + if (hDevice && (hDevice != INVALID_HANDLE_VALUE)) + { + STORAGE_PROPERTY_QUERY spq; + DEVICE_SEEK_PENALTY_DESCRIPTOR dspd; + memset(&spq, 0, sizeof(STORAGE_PROPERTY_QUERY)); + spq.PropertyId = (STORAGE_PROPERTY_ID)StorageDeviceSeekPenaltyProperty; + spq.QueryType = PropertyStandardQuery; + DWORD dwSize; + if (DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, (LPVOID)&spq, (DWORD)sizeof(spq), (LPVOID)&dspd, (DWORD)sizeof(dspd), (LPDWORD)&dwSize, NULL)) + { + seeking_penalty = dspd.IncursSeekPenalty; + } + CloseHandle(hDevice); + } + return seeking_penalty; +} + +static bool is_fast_seeking_drive(const wchar_t drive_letter) +{ + bool fast_seeking = false; + const QSet physical_drive_ids = get_physical_drive_ids(drive_letter); + if (!physical_drive_ids.empty()) + { + fast_seeking = true; + for (QSet::const_iterator iter = physical_drive_ids.constBegin(); iter != physical_drive_ids.constEnd(); ++iter) + { + fast_seeking = fast_seeking && (!incurs_seek_penalty(*iter)); + } + } + return fast_seeking; +} + +MUtils::OS::drive_type_t MUtils::OS::get_drive_type(const QString &path, bool *fast_seeking) +{ + drive_type_t driveType = DRIVE_TYPE_ERR; + const wchar_t driveLetter = get_drive_letter(path); + if (driveLetter) + { + wchar_t driveName[8]; + _snwprintf_s(driveName, 8, _TRUNCATE, L"\\\\.\\%c:\\", driveLetter); + switch (GetDriveTypeW(driveName)) + { + case DRIVE_REMOVABLE: driveType = DRIVE_TYPE_FDD; break; + case DRIVE_FIXED: driveType = DRIVE_TYPE_HDD; break; + case DRIVE_REMOTE: driveType = DRIVE_TYPE_NET; break; + case DRIVE_CDROM: driveType = DRIVE_TYPE_OPT; break; + case DRIVE_RAMDISK: driveType = DRIVE_TYPE_RAM; break; + } + } + if (fast_seeking) + { + if (driveType == DRIVE_TYPE_HDD) + { + *fast_seeking = is_fast_seeking_drive(driveLetter); + } + else + { + *fast_seeking = (driveType == DRIVE_TYPE_RAM); + } + } + return driveType; +} + /////////////////////////////////////////////////////////////////////////////// // SHELL OPEN ///////////////////////////////////////////////////////////////////////////////