diff --git a/MUtilities_VS2013.vcxproj b/MUtilities_VS2013.vcxproj index 5109d15..d832154 100644 --- a/MUtilities_VS2013.vcxproj +++ b/MUtilities_VS2013.vcxproj @@ -109,6 +109,7 @@ MultiThreadedDebugDLL NoExtensions $(ProjectDir)\include;$(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;%(AdditionalIncludeDirectories) + true Windows @@ -135,6 +136,7 @@ Fast false $(ProjectDir)\include;$(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;%(AdditionalIncludeDirectories) + true Windows @@ -163,6 +165,7 @@ Fast false $(ProjectDir)\include;$(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;%(AdditionalIncludeDirectories) + true Windows diff --git a/include/MUtils/OSSupport.h b/include/MUtils/OSSupport.h index 1a0f9f0..5b674e1 100644 --- a/include/MUtils/OSSupport.h +++ b/include/MUtils/OSSupport.h @@ -34,6 +34,48 @@ namespace MUtils { namespace OS { + namespace Version + { + //Supported OS types + typedef enum + { + OS_UNKNOWN = 0, + OS_WINDOWS = 1 + } + os_type_t; + + //OS version struct + typedef struct _os_info_t + { + unsigned int type; + unsigned int versionMajor; + unsigned int versionMinor; + bool overrideFlag; + + //comparision operators + inline bool operator== (const _os_info_t &rhs) const { return (type == rhs.type) && (versionMajor == rhs.versionMajor) && ((versionMinor == rhs.versionMinor)); } + inline bool operator!= (const _os_info_t &rhs) const { return (type != rhs.type) || (versionMajor != rhs.versionMajor) || ((versionMinor != rhs.versionMinor)); } + inline bool operator> (const _os_info_t &rhs) const { return (type == rhs.type) && ((versionMajor > rhs.versionMajor) || ((versionMajor == rhs.versionMajor) && (versionMinor > rhs.versionMinor))); } + inline bool operator>= (const _os_info_t &rhs) const { return (type == rhs.type) && ((versionMajor > rhs.versionMajor) || ((versionMajor == rhs.versionMajor) && (versionMinor >= rhs.versionMinor))); } + inline bool operator< (const _os_info_t &rhs) const { return (type == rhs.type) && ((versionMajor < rhs.versionMajor) || ((versionMajor == rhs.versionMajor) && (versionMinor < rhs.versionMinor))); } + inline bool operator<= (const _os_info_t &rhs) const { return (type == rhs.type) && ((versionMajor < rhs.versionMajor) || ((versionMajor == rhs.versionMajor) && (versionMinor <= rhs.versionMinor))); } + } + os_version_t; + + //Known Windows NT versions + static const os_version_t WINDOWS_WIN2K = { OS_WINDOWS, 5, 0 }; // 2000 + static const os_version_t WINDOWS_WINXP = { OS_WINDOWS, 5, 1 }; // XP + static const os_version_t WINDOWS_XPX64 = { OS_WINDOWS, 5, 2 }; // XP_x64 + static const os_version_t WINDOWS_VISTA = { OS_WINDOWS, 6, 0 }; // Vista + static const os_version_t WINDOWS_WIN70 = { OS_WINDOWS, 6, 1 }; // 7 + static const os_version_t WINDOWS_WIN80 = { OS_WINDOWS, 6, 2 }; // 8 + static const os_version_t WINDOWS_WIN81 = { OS_WINDOWS, 6, 3 }; // 8.1 + static const os_version_t WINDOWS_WN100 = { OS_WINDOWS, 6, 4 }; // 10 + + //Unknown OS + static const os_version_t UNKNOWN_OPSYS = { OS_UNKNOWN, 0, 0 }; // N/A + } + //Known Folders IDs typedef enum { @@ -52,12 +94,16 @@ namespace MUtils NETWORK_TYPE_YES = 2 /*connected*/ } network_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); MUTILS_API void system_message_err(const wchar_t *const title, const wchar_t *const text); + //Get the OS version + MUTILS_API const Version::os_version_t &os_version(void); + MUTILS_API const char *os_friendly_name(const MUtils::OS::Version::os_version_t &os_version); + //Get known Folder MUTILS_API const QString &known_folder(known_folder_t folder_id); diff --git a/src/OSSupport_Win32.cpp b/src/OSSupport_Win32.cpp index 1b9698d..48976e7 100644 --- a/src/OSSupport_Win32.cpp +++ b/src/OSSupport_Win32.cpp @@ -61,6 +61,174 @@ void MUtils::OS::system_message_err(const wchar_t *const title, const wchar_t *c MessageBoxW(NULL, text, title, MB_TOPMOST | MB_TASKMODAL | MB_ICONERROR); } +/////////////////////////////////////////////////////////////////////////////// +// OS VERSION DETECTION +/////////////////////////////////////////////////////////////////////////////// + +static bool g_os_version_initialized = false; +static MUtils::OS::Version::os_version_t g_os_version_info = MUtils::OS::Version::UNKNOWN_OPSYS; +static QReadWriteLock g_os_version_lock; + +//Maps marketing names to the actual Windows NT versions +static const struct +{ + MUtils::OS::Version::os_version_t version; + const char friendlyName[64]; +} +g_os_version_lut[] = +{ + { MUtils::OS::Version::WINDOWS_WIN2K, "Windows 2000" }, //2000 + { MUtils::OS::Version::WINDOWS_WINXP, "Windows XP or Windows XP Media Center Edition" }, //XP + { MUtils::OS::Version::WINDOWS_XPX64, "Windows Server 2003 or Windows XP x64" }, //XP_x64 + { MUtils::OS::Version::WINDOWS_VISTA, "Windows Vista or Windows Server 2008" }, //Vista + { MUtils::OS::Version::WINDOWS_WIN70, "Windows 7 or Windows Server 2008 R2" }, //7 + { MUtils::OS::Version::WINDOWS_WIN80, "Windows 8 or Windows Server 2012" }, //8 + { MUtils::OS::Version::WINDOWS_WIN81, "Windows 8.1 or Windows Server 2012 R2" }, //8.1 + { MUtils::OS::Version::WINDOWS_WN100, "Windows 10 or Windows Server 2014 (Preview)" }, //10 + { MUtils::OS::Version::UNKNOWN_OPSYS, "N/A" } +}; + +static bool verify_os_version(const DWORD major, const DWORD minor) +{ + OSVERSIONINFOEXW osvi; + DWORDLONG dwlConditionMask = 0; + + //Initialize the OSVERSIONINFOEX structure + memset(&osvi, 0, sizeof(OSVERSIONINFOEXW)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + osvi.dwMajorVersion = major; + osvi.dwMinorVersion = minor; + osvi.dwPlatformId = VER_PLATFORM_WIN32_NT; + + //Initialize the condition mask + VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, VER_PLATFORMID, VER_EQUAL); + + // Perform the test + const BOOL ret = VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID, dwlConditionMask); + + //Error checking + if(!ret) + { + if(GetLastError() != ERROR_OLD_WIN_VERSION) + { + qWarning("VerifyVersionInfo() system call has failed!"); + } + } + + return (ret != FALSE); +} + +static bool get_real_os_version(unsigned int *major, unsigned int *minor, bool *pbOverride) +{ + *major = *minor = 0; + *pbOverride = false; + + //Initialize local variables + OSVERSIONINFOEXW osvi; + memset(&osvi, 0, sizeof(OSVERSIONINFOEXW)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + + //Try GetVersionEx() first + if(GetVersionExW((LPOSVERSIONINFOW)&osvi) == FALSE) + { + qWarning("GetVersionEx() has failed, cannot detect Windows version!"); + return false; + } + + //Make sure we are running on NT + if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) + { + *major = osvi.dwMajorVersion; + *minor = osvi.dwMinorVersion; + } + else + { + qWarning("Not running on Windows NT, unsupported operating system!"); + return false; + } + + //Determine the real *major* version first + forever + { + const DWORD nextMajor = (*major) + 1; + if(verify_os_version(nextMajor, 0)) + { + *pbOverride = true; + *major = nextMajor; + *minor = 0; + continue; + } + break; + } + + //Now also determine the real *minor* version + forever + { + const DWORD nextMinor = (*minor) + 1; + if(verify_os_version((*major), nextMinor)) + { + *pbOverride = true; + *minor = nextMinor; + continue; + } + break; + } + + return true; +} + +const MUtils::OS::Version::os_version_t &MUtils::OS::os_version(void) +{ + QReadLocker readLock(&g_os_version_lock); + + //Already initialized? + if(g_os_version_initialized) + { + return g_os_version_info; + } + + readLock.unlock(); + QWriteLocker writeLock(&g_os_version_lock); + + //Initialized now? + if(g_os_version_initialized) + { + return g_os_version_info; + } + + //Detect OS version + unsigned int major, minor; bool overrideFlg; + if(get_real_os_version(&major, &minor, &overrideFlg)) + { + g_os_version_info.type = Version::OS_WINDOWS; + g_os_version_info.versionMajor = major; + g_os_version_info.versionMinor = minor; + g_os_version_info.overrideFlag = overrideFlg; + } + else + { + qWarning("Failed to determin the operating system version!"); + } + + g_os_version_initialized = true; + return g_os_version_info; +} + +const char *MUtils::OS::os_friendly_name(const MUtils::OS::Version::os_version_t &os_version) +{ + for(size_t i = 0; g_os_version_lut[i].version != MUtils::OS::Version::UNKNOWN_OPSYS; i++) + { + if(os_version == g_os_version_lut[i].version) + { + return g_os_version_lut[i].friendlyName; + } + } + + return NULL; +} + /////////////////////////////////////////////////////////////////////////////// // KNWON FOLDERS ///////////////////////////////////////////////////////////////////////////////