diff options
author | Daniel Wilhelm <daniel@wili.li> | 2015-10-02 14:52:54 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2015-10-02 14:52:54 +0200 |
commit | 782e3f33cf07e2b3210e9bee9607f34bf6dfc5df (patch) | |
tree | a5b07ff5345140520e7eaf4be4d3bb5f4b8f8c26 /zen | |
parent | 6.9 (diff) | |
download | FreeFileSync-782e3f33cf07e2b3210e9bee9607f34bf6dfc5df.tar.gz FreeFileSync-782e3f33cf07e2b3210e9bee9607f34bf6dfc5df.tar.bz2 FreeFileSync-782e3f33cf07e2b3210e9bee9607f34bf6dfc5df.zip |
6.10
Diffstat (limited to 'zen')
-rw-r--r-- | zen/basic_math.h | 8 | ||||
-rw-r--r-- | zen/dll.h | 121 | ||||
-rw-r--r-- | zen/file_error.h | 4 | ||||
-rw-r--r-- | zen/i18n.h | 4 | ||||
-rw-r--r-- | zen/osx_string.h | 82 | ||||
-rw-r--r-- | zen/osx_throw_exception.h | 56 | ||||
-rw-r--r-- | zen/privilege.cpp | 144 | ||||
-rw-r--r-- | zen/privilege.h | 17 | ||||
-rw-r--r-- | zen/symlink_target.h | 14 | ||||
-rw-r--r-- | zen/win_ver.h | 86 |
10 files changed, 493 insertions, 43 deletions
diff --git a/zen/basic_math.h b/zen/basic_math.h index 56cfd923..69e861be 100644 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -32,9 +32,9 @@ template <class T> const T& max(const T& a, const T& b, const T& c); template <class T> -void confine(T& val, const T& minVal, const T& maxVal); //make sure minVal <= val && val <= maxVal +void clamp(T& val, const T& minVal, const T& maxVal); //make sure minVal <= val && val <= maxVal template <class T> -T confineCpy(const T& val, const T& minVal, const T& maxVal); +T clampCpy(const T& val, const T& minVal, const T& maxVal); template <class T, class InputIterator> //precondition: range must be sorted! auto nearMatch(const T& val, InputIterator first, InputIterator last) -> typename std::iterator_traits<InputIterator>::value_type; @@ -134,7 +134,7 @@ const T& max(const T& a, const T& b, const T& c) template <class T> inline -T confineCpy(const T& val, const T& minVal, const T& maxVal) +T clampCpy(const T& val, const T& minVal, const T& maxVal) { assert(minVal <= maxVal); if (val < minVal) @@ -145,7 +145,7 @@ T confineCpy(const T& val, const T& minVal, const T& maxVal) } template <class T> inline -void confine(T& val, const T& minVal, const T& maxVal) //name trim, clamp? +void clamp(T& val, const T& minVal, const T& maxVal) { assert(minVal <= maxVal); if (val < minVal) diff --git a/zen/dll.h b/zen/dll.h new file mode 100644 index 00000000..f6422fa7 --- /dev/null +++ b/zen/dll.h @@ -0,0 +1,121 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef DLLLOADER_H_4239582598670968 +#define DLLLOADER_H_4239582598670968 + +#include <memory> +#ifdef ZEN_WIN +#include <string> +#include "scope_guard.h" +#include "win.h" //includes "windows.h" + +#elif defined ZEN_LINUX || defined ZEN_MAC +#include <dlfcn.h> +#endif + +namespace zen +{ +/* +Manage DLL function and library ownership + - thread safety: like built-in type + - full value semantics + + Usage: + typedef BOOL (WINAPI* FunType_IsWow64Process)(HANDLE hProcess, PBOOL Wow64Process); + const zen::SysDllFun<FunType_IsWow64Process> isWow64Process(L"kernel32.dll", "IsWow64Process"); + if (isWow64Process) ... use function ptr ... + + Usage 2: + #define DEF_DLL_FUN(name) DllFun<dll_ns::FunType_##name> name(dll_ns::getDllName(), dll_ns::funName_##name); + DEF_DLL_FUN(funname1); DEF_DLL_FUN(funname2); DEF_DLL_FUN(funname3); +*/ + +template <class Func> +class DllFun +{ +public: + DllFun() : fun(nullptr) {} + +#ifdef ZEN_WIN + DllFun(const wchar_t* libraryName, const char* functionName) : + hLibRef(::LoadLibrary(libraryName), ::FreeLibrary), + fun(hLibRef ? reinterpret_cast<Func>(::GetProcAddress(static_cast<HMODULE>(hLibRef.get()), functionName)) : nullptr) {} +#elif defined ZEN_LINUX || defined ZEN_MAC + DllFun(const char* libraryName, const char* functionName) : + hLibRef(::dlopen(libraryName, RTLD_LAZY), ::dlclose), + fun(hLibRef ? reinterpret_cast<Func>(::dlsym(hLibRef.get(), functionName)) : nullptr) {} +#endif + operator Func() const { return fun; } + +private: + std::shared_ptr<void> hLibRef; //we would prefer decltype(*HMODULE()) if only it would work... + Func fun; +}; + + +#ifdef ZEN_WIN +//if the dll is already part of the process space, e.g. "kernel32.dll" or "shell32.dll", we can use a faster variant: +//NOTE: since the lifetime of the referenced library is *not* controlled, this is safe to use only for permanently loaded libraries like these! +template <class Func> +class SysDllFun +{ +public: + SysDllFun() : fun(nullptr) {} + + SysDllFun(const wchar_t* systemLibrary, const char* functionName) + { + HMODULE mod = ::GetModuleHandle(systemLibrary); + fun = mod ? reinterpret_cast<Func>(::GetProcAddress(mod, functionName)) : nullptr; + } + + operator Func() const { return fun; } + +private: + Func fun; +}; + +/* +extract binary resources from .exe/.dll: + +-- resource.h -- +#define MY_BINARY_RESOURCE 1337 + +-- resource.rc -- +MY_BINARY_RESOURCE RCDATA "filename.dat" +*/ +std::string getResourceStream(const std::wstring& libraryName, size_t resourceId); +#endif + + + + + + + + + + +//--------------- implementation--------------------------------------------------- +#ifdef ZEN_WIN +inline +std::string getResourceStream(const wchar_t* libraryName, size_t resourceId) +{ + if (HMODULE module = ::LoadLibrary(libraryName)) + { + ZEN_ON_SCOPE_EXIT(::FreeLibrary(module)); + + if (HRSRC res = ::FindResource(module, MAKEINTRESOURCE(resourceId), RT_RCDATA)) + if (HGLOBAL resHandle = ::LoadResource(module, res)) + if (const char* stream = static_cast<const char*>(::LockResource(resHandle))) + return std::string(stream, static_cast<size_t>(::SizeofResource(module, res))); //size is 0 on error + } + return std::string(); +} +#endif +} + +#endif //DLLLOADER_H_4239582598670968 diff --git a/zen/file_error.h b/zen/file_error.h index 73cfa17a..9276e8c5 100644 --- a/zen/file_error.h +++ b/zen/file_error.h @@ -38,10 +38,10 @@ DEFINE_NEW_FILE_ERROR(ErrorDifferentVolume); //CAVEAT: evalulate global error code *before* "throw" statement which may overwrite error code //due to a memory allocation before it creates the thrown instance! (e.g. affects MinGW + Win XP!!!) -inline +template <class FE = FileError> inline void throwFileError(const std::wstring& msg, const std::wstring& functionName, const ErrorCode ec) //throw FileError { - throw FileError(msg, formatSystemError(functionName, ec)); + throw FE(msg, formatSystemError(functionName, ec)); } @@ -11,7 +11,7 @@ #include <memory> #include <cstdint> #include "string_tools.h" - +#include "format_unit.h" //minimal layer enabling text translation - without platform/library dependencies! #ifdef __WXMSW__ //we have wxWidgets @@ -75,7 +75,7 @@ std::wstring translate(const std::wstring& singular, const std::wstring& plural, return translation; } else - return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", zen::numberTo<std::wstring>(n)); + return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", toGuiString(n)); } template <class T> inline diff --git a/zen/osx_string.h b/zen/osx_string.h new file mode 100644 index 00000000..ba83ca27 --- /dev/null +++ b/zen/osx_string.h @@ -0,0 +1,82 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef OSX_STRING_1873641732143214324 +#define OSX_STRING_1873641732143214324 + +#include <CoreFoundation/CoreFoundation.h> //CFString +#include "zstring.h" + +namespace osx +{ +Zstring cfStringToZstring(const CFStringRef& cfStr); + +CFStringRef createCFString (const char* utf8Str); //returns nullptr on error +CFMutableStringRef createMutableCFString(const char* utf8Str); //pass ownership! => ZEN_ON_SCOPE_EXIT(::CFRelease(str)); + + + + + + + + + + + + + +//################# implementation ##################### +inline +Zstring cfStringToZstring(const CFStringRef& cfStr) +{ + if (cfStr) + { + //perf: try to get away cheap: + if (const char* utf8Str = ::CFStringGetCStringPtr(cfStr, kCFStringEncodingUTF8)) + return utf8Str; + + CFIndex length = ::CFStringGetLength(cfStr); + if (length > 0) + { + CFIndex bufferSize = ::CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8); + Zstring buffer; + buffer.resize(bufferSize); + + if (::CFStringGetCString(cfStr, &*buffer.begin(), bufferSize, kCFStringEncodingUTF8)) + { + buffer.resize(zen::strLength(buffer.c_str())); //caveat: memory consumption of returned string! + return buffer; + } + } + } + return Zstring(); +} + + +inline +CFStringRef createCFString(const char* utf8Str) +{ + //don't bother with CFStringCreateWithBytes: it's slightly slower, despite passing length info + return ::CFStringCreateWithCString(nullptr, //CFAllocatorRef alloc, + utf8Str, //const char *cStr, + kCFStringEncodingUTF8); //CFStringEncoding encoding +} + + +inline +CFMutableStringRef createMutableCFString(const char* utf8Str) +{ + if (CFMutableStringRef strRef = ::CFStringCreateMutable(NULL, 0)) + { + ::CFStringAppendCString(strRef, utf8Str, kCFStringEncodingUTF8); + return strRef; + } + return nullptr; +} +} + +#endif //OSX_STRING_1873641732143214324 diff --git a/zen/osx_throw_exception.h b/zen/osx_throw_exception.h new file mode 100644 index 00000000..07e3af3e --- /dev/null +++ b/zen/osx_throw_exception.h @@ -0,0 +1,56 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef OSX_EXCEPTION_89274305834255 +#define OSX_EXCEPTION_89274305834255 + +#import <Cocoa/Cocoa.h> +#include "sys_error.h" +#include "utf.h" + +namespace osx +{ +//for use in Objective C implementation files only! +void throwSysError(NSException* e); //throw SysError + +#define ZEN_OSX_ASSERT(obj) ZEN_OSX_ASSERT_IMPL(obj, #obj) //throw SysError +/* +Example: ZEN_OSX_ASSERT(obj); + +Equivalent to: + if (!obj) + throw zen::SysError(L"Assertion failed: \"obj\"."); +*/ + + + + + + +//######################## implmentation ############################ +inline +void throwSysError(NSException* e) //throw SysError +{ + std::string msg; + if (const char* name = [[e name ] cStringUsingEncoding:NSUTF8StringEncoding]) //"const char*" NOT owned by us! + msg += name; + if (const char* descr = [[e reason] cStringUsingEncoding:NSUTF8StringEncoding]) + { + msg += "\n"; + msg += descr; + } + throw zen::SysError(zen::utfCvrtTo<std::wstring>(msg)); + /* + e.g. + NSInvalidArgumentException + *** +[NSString stringWithCString:encoding:]: NULL cString + */ +} +} + +#define ZEN_OSX_ASSERT_IMPL(obj, txt) if (!(obj)) throw zen::SysError(std::wstring(L"Assertion failed: \"") + L ## txt + L"\"."); + +#endif //OSX_EXCEPTION_89274305834255 diff --git a/zen/privilege.cpp b/zen/privilege.cpp new file mode 100644 index 00000000..c2db4701 --- /dev/null +++ b/zen/privilege.cpp @@ -0,0 +1,144 @@ +#include "privilege.h" +#include <map> +//#include <mutex> +#include "win.h" //includes "windows.h" +#include "thread.h" +#include "zstring.h" +#include "scope_guard.h" +#include "win_ver.h" + +using namespace zen; + + +namespace +{ +bool privilegeIsActive(const wchar_t* privilege) //throw FileError +{ + HANDLE hToken = nullptr; + if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle, + TOKEN_QUERY, //__in DWORD DesiredAccess, + &hToken)) //__out PHANDLE TokenHandle + throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"OpenProcessToken", getLastError()); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken)); + + LUID luid = {}; + if (!::LookupPrivilegeValue(nullptr, //__in_opt LPCTSTR lpSystemName, + privilege, //__in LPCTSTR lpName, + &luid )) //__out PLUID lpLuid + throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"LookupPrivilegeValue", getLastError()); + + PRIVILEGE_SET priv = {}; + priv.PrivilegeCount = 1; + priv.Control = PRIVILEGE_SET_ALL_NECESSARY; + priv.Privilege[0].Luid = luid; + priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED; + + BOOL alreadyGranted = FALSE; + if (!::PrivilegeCheck(hToken, //__in HANDLE ClientToken, + &priv, //__inout PPRIVILEGE_SET RequiredPrivileges, + &alreadyGranted)) //__out LPBOOL pfResult + throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"PrivilegeCheck", getLastError()); + + return alreadyGranted != FALSE; +} + + +void setPrivilege(const wchar_t* privilege, bool enable) //throw FileError +{ + HANDLE hToken = nullptr; + if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle, + TOKEN_ADJUST_PRIVILEGES, //__in DWORD DesiredAccess, + &hToken)) //__out PHANDLE TokenHandle + throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"OpenProcessToken", getLastError()); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken)); + + LUID luid = {}; + if (!::LookupPrivilegeValue(nullptr, //__in_opt LPCTSTR lpSystemName, + privilege, //__in LPCTSTR lpName, + &luid )) //__out PLUID lpLuid + throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"LookupPrivilegeValue", getLastError()); + + TOKEN_PRIVILEGES tp = {}; + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0; + + if (!::AdjustTokenPrivileges(hToken, //__in HANDLE TokenHandle, + false, //__in BOOL DisableAllPrivileges, + &tp, //__in_opt PTOKEN_PRIVILEGES NewState, + 0, //__in DWORD BufferLength, + nullptr, //__out_opt PTOKEN_PRIVILEGES PreviousState, + nullptr)) //__out_opt PDWORD ReturnLength + throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"AdjustTokenPrivileges", getLastError()); + + DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! + if (lastError == ERROR_NOT_ALL_ASSIGNED) //check although previous function returned with success! + { +#ifdef __MINGW32__ //Shobjidl.h +#define ERROR_ELEVATION_REQUIRED 740L +#endif + if (vistaOrLater()) //replace this useless error code with what it *really* means! + lastError = ERROR_ELEVATION_REQUIRED; + + throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"AdjustTokenPrivileges", lastError); + } +} + + +class Privileges +{ +public: + static Privileges& getInstance() + { + //meyers singleton: avoid static initialization order problem in global namespace! + static Privileges inst; + return inst; + } + + void ensureActive(const wchar_t* privilege) //throw FileError + { + boost::lock_guard<boost::mutex> dummy(lockPrivileges); + + if (activePrivileges.find(privilege) != activePrivileges.end()) + return; //privilege already active + + if (privilegeIsActive(privilege)) //privilege was already active before starting this tool + activePrivileges.insert(std::make_pair(privilege, false)); + else + { + setPrivilege(privilege, true); + activePrivileges.insert(std::make_pair(privilege, true)); + } + } + +private: + Privileges() {} + Privileges (const Privileges&) = delete; + Privileges& operator=(const Privileges&) = delete; + + ~Privileges() //clean up: deactivate all privileges that have been activated by this application + { + for (const auto& priv : activePrivileges) + if (priv.second) + { + try + { + setPrivilege(priv.first.c_str(), false); //throw FileError + } + catch (FileError&) {} + } + } + + std::map<Zstring, bool> activePrivileges; //bool: enabled by this application +boost::mutex lockPrivileges; +}; + +//caveat: function scope static initialization is not thread-safe in VS 2010! +auto& dummy = Privileges::getInstance(); +} + + +void zen::activatePrivilege(const wchar_t* privilege) //throw FileError +{ + Privileges::getInstance().ensureActive(privilege); +} diff --git a/zen/privilege.h b/zen/privilege.h new file mode 100644 index 00000000..e9b83be9 --- /dev/null +++ b/zen/privilege.h @@ -0,0 +1,17 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef PRIVILEGE_H_INCLUDED +#define PRIVILEGE_H_INCLUDED + +#include "file_error.h" + +namespace zen +{ +void activatePrivilege(const wchar_t* privilege); //throw FileError; thread-safe!!! +} + +#endif // PRIVILEGE_H_INCLUDED diff --git a/zen/symlink_target.h b/zen/symlink_target.h index 21833492..4106ed02 100644 --- a/zen/symlink_target.h +++ b/zen/symlink_target.h @@ -157,6 +157,13 @@ Zstring getResolvedFilePath_impl(const Zstring& linkPath) //throw FileError { using namespace zen; #ifdef ZEN_WIN + //GetFinalPathNameByHandle() is not available before Vista! + typedef DWORD (WINAPI* GetFinalPathNameByHandleWFunc)(HANDLE hFile, LPTSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags); + const SysDllFun<GetFinalPathNameByHandleWFunc> getFinalPathNameByHandle(L"kernel32.dll", "GetFinalPathNameByHandleW"); + if (!getFinalPathNameByHandle) + throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\"")); + + const HANDLE hDir = ::CreateFile(applyLongPathPrefix(linkPath).c_str(), //_In_ LPCTSTR lpFileName, 0, //_In_ DWORD dwDesiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, @@ -169,13 +176,6 @@ Zstring getResolvedFilePath_impl(const Zstring& linkPath) //throw FileError throwFileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), L"CreateFile", getLastError()); ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir)); - //GetFinalPathNameByHandle() is not available before Vista! - typedef DWORD (WINAPI* GetFinalPathNameByHandleWFunc)(HANDLE hFile, LPTSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags); - const SysDllFun<GetFinalPathNameByHandleWFunc> getFinalPathNameByHandle(L"kernel32.dll", "GetFinalPathNameByHandleW"); - - if (!getFinalPathNameByHandle) - throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\"")); - const DWORD bufferSize = getFinalPathNameByHandle(hDir, nullptr, 0, 0); if (bufferSize == 0) throwFileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), L"GetFinalPathNameByHandle", getLastError()); diff --git a/zen/win_ver.h b/zen/win_ver.h index e123737d..0d3f8d70 100644 --- a/zen/win_ver.h +++ b/zen/win_ver.h @@ -7,30 +7,48 @@ #ifndef WINDOWS_VERSION_HEADER_238470348254325 #define WINDOWS_VERSION_HEADER_238470348254325 -#include <cstdint> +#include <utility> #include "win.h" //includes "windows.h" namespace zen { -std::uint64_t getOsVersion(); -std::uint64_t toBigOsNumber(DWORD high, DWORD low); + struct OsVersion + { + OsVersion() : major(), minor() {} + OsVersion(DWORD high, DWORD low) : major(high), minor(low) {} + + DWORD major; + DWORD minor; + }; + inline bool operator< (const OsVersion& lhs, const OsVersion& rhs) { return lhs.major != rhs.major ? lhs.major < rhs.major : lhs.minor < rhs.minor; } + inline bool operator==(const OsVersion& lhs, const OsVersion& rhs) { return lhs.major == rhs.major && lhs.minor == rhs.minor; } + //version overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx -const std::uint64_t osVersionWin81 = toBigOsNumber(6, 3); -const std::uint64_t osVersionWin8 = toBigOsNumber(6, 2); -const std::uint64_t osVersionWin7 = toBigOsNumber(6, 1); -const std::uint64_t osVersionWinVista = toBigOsNumber(6, 0); -const std::uint64_t osVersionWinServer2003 = toBigOsNumber(5, 2); -const std::uint64_t osVersionWinXp = toBigOsNumber(5, 1); +const OsVersion osVersionWin81 (6, 3); +const OsVersion osVersionWin8 (6, 2); +const OsVersion osVersionWin7 (6, 1); +const OsVersion osVersionWinVista (6, 0); +const OsVersion osVersionWinServer2003(5, 2); +const OsVersion osVersionWinXp (5, 1); -inline bool win81OrLater () { return getOsVersion() >= osVersionWin81; } -inline bool win8OrLater () { return getOsVersion() >= osVersionWin8; } -inline bool win7OrLater () { return getOsVersion() >= osVersionWin7; } -inline bool vistaOrLater () { return getOsVersion() >= osVersionWinVista; } -inline bool winServer2003orLater() { return getOsVersion() >= osVersionWinServer2003; } -inline bool winXpOrLater () { return getOsVersion() >= osVersionWinXp; } +/* + NOTE: there are two basic APIs to check Windows version: (empiric study following) + GetVersionEx -> reports version considering compatibility mode (and compatibility setting in app manifest since Windows 8.1) + VerifyVersionInfo -> always reports *real* Windows Version +*/ +//GetVersionEx()-based APIs: +OsVersion getOsVersion(); +inline bool win81OrLater () { using namespace std::rel_ops; return getOsVersion() >= osVersionWin81; } +inline bool win8OrLater () { using namespace std::rel_ops; return getOsVersion() >= osVersionWin8; } +inline bool win7OrLater () { using namespace std::rel_ops; return getOsVersion() >= osVersionWin7; } +inline bool vistaOrLater () { using namespace std::rel_ops; return getOsVersion() >= osVersionWinVista; } +inline bool winServer2003orLater() { using namespace std::rel_ops; return getOsVersion() >= osVersionWinServer2003; } +inline bool winXpOrLater () { using namespace std::rel_ops; return getOsVersion() >= osVersionWinXp; } +//VerifyVersionInfo()-based APIs: +bool isRealOsVersion(const OsVersion& ver); @@ -39,25 +57,37 @@ inline bool winXpOrLater () { return getOsVersion() >= osVersionWinXp; //######################### implementation ######################### inline -std::uint64_t toBigOsNumber(DWORD high, DWORD low) +OsVersion getOsVersion() { - ULARGE_INTEGER tmp = {}; - tmp.HighPart = high; - tmp.LowPart = low; - - static_assert(sizeof(tmp) == sizeof(std::uint64_t), ""); - return tmp.QuadPart; + OSVERSIONINFO osvi = {}; + osvi.dwOSVersionInfoSize = sizeof(osvi); + if (!::GetVersionEx(&osvi)) //38 ns per call! (yes, that's nano!) -> we do NOT miss C++11 thread-safe statics right now... + { + assert(false); + return OsVersion(); + } + return OsVersion(osvi.dwMajorVersion, osvi.dwMinorVersion); } inline -std::uint64_t getOsVersion() +bool isRealOsVersion(const OsVersion& ver) { - OSVERSIONINFO osvi = {}; - osvi.dwOSVersionInfoSize = sizeof(osvi); - if (!::GetVersionEx(&osvi)) //38 ns per call! (yes, that's nano!) -> we do NOT miss C++11 thread safe statics right now... - return 0; - return toBigOsNumber(osvi.dwMajorVersion, osvi.dwMinorVersion); + OSVERSIONINFOEX verInfo = {}; + verInfo.dwOSVersionInfoSize = sizeof(verInfo); + verInfo.dwMajorVersion = ver.major; + verInfo.dwMinorVersion = ver.minor; + + //Syntax: http://msdn.microsoft.com/en-us/library/windows/desktop/ms725491%28v=vs.85%29.aspx + DWORDLONG conditionMask = 0; + VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_EQUAL); + VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_EQUAL); + + const bool rv = ::VerifyVersionInfo(&verInfo, VER_MAJORVERSION | VER_MINORVERSION, conditionMask) + == TRUE; //silence VC "performance warnings" + assert(rv || GetLastError() == ERROR_OLD_WIN_VERSION); + + return rv; } } |