summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2015-10-02 14:52:54 +0200
committerDaniel Wilhelm <daniel@wili.li>2015-10-02 14:52:54 +0200
commit782e3f33cf07e2b3210e9bee9607f34bf6dfc5df (patch)
treea5b07ff5345140520e7eaf4be4d3bb5f4b8f8c26 /zen
parent6.9 (diff)
downloadFreeFileSync-782e3f33cf07e2b3210e9bee9607f34bf6dfc5df.tar.gz
FreeFileSync-782e3f33cf07e2b3210e9bee9607f34bf6dfc5df.tar.bz2
FreeFileSync-782e3f33cf07e2b3210e9bee9607f34bf6dfc5df.zip
6.10
Diffstat (limited to 'zen')
-rw-r--r--zen/basic_math.h8
-rw-r--r--zen/dll.h121
-rw-r--r--zen/file_error.h4
-rw-r--r--zen/i18n.h4
-rw-r--r--zen/osx_string.h82
-rw-r--r--zen/osx_throw_exception.h56
-rw-r--r--zen/privilege.cpp144
-rw-r--r--zen/privilege.h17
-rw-r--r--zen/symlink_target.h14
-rw-r--r--zen/win_ver.h86
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));
}
diff --git a/zen/i18n.h b/zen/i18n.h
index 5aedc41c..3790e528 100644
--- a/zen/i18n.h
+++ b/zen/i18n.h
@@ -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;
}
}
bgstack15