Skip to content

Commit

Permalink
Platform: Rework platform detection
Browse files Browse the repository at this point in the history
The "friendly" name detection (reported in the About dialog Version tab, and the log) has been tweaked to show the non-numerical "DisplayVersion" over the "ReleaseId" from the registry.

Also, added detection of "Windows Server 2022".
  • Loading branch information
brianferguson committed Sep 9, 2022
1 parent 416edc1 commit 111caf9
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 139 deletions.
269 changes: 137 additions & 132 deletions Common/Platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,187 +8,192 @@
#include "StdAfx.h"
#include "Platform.h"

namespace Platform {

namespace {
namespace
{

bool IsWin11()
std::wstring& GetBuildNumberFromRegistry()
{
// Temporary Windows 11 check
static bool s_IsWin11 = []() -> bool
static std::wstring s_BuildNumber = []() -> std::wstring
{
if (IsWindows10OrGreater())
std::wstring buildNumber = L"0";

HKEY hkey = nullptr;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion", 0UL, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS)
{
WCHAR buffer[256] = { 0 };
WCHAR buffer[10] = { 0 };
DWORD size = _countof(buffer);
int buildNumber = 0;

HKEY hKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion", 0UL, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
if (RegQueryValueEx(hkey, L"CurrentBuildNumber", nullptr, nullptr, (LPBYTE)buffer, (LPDWORD)&size) == ERROR_SUCCESS ||
RegQueryValueEx(hkey, L"CurrentBuild", nullptr, nullptr, (LPBYTE)buffer, (LPDWORD)&size) == ERROR_SUCCESS)
{
if (RegQueryValueEx(hKey, L"CurrentBuildNumber", nullptr, nullptr, (LPBYTE)buffer, (LPDWORD)&size) == ERROR_SUCCESS)
{
buildNumber = _wtoi(buffer);
}
RegCloseKey(hKey);
buildNumber = buffer;
}

// |GetTempPath2W| doesn't exist in Windows version prior to Windows 11 (as of yet)
typedef void* (__stdcall* TempPath2)();
TempPath2 tmpPath2 = (TempPath2)GetProcAddress(GetModuleHandle(L"kernel32"), "GetTempPath2W");

return tmpPath2 && buildNumber >= 22000;
RegCloseKey(hkey);
hkey = nullptr;
}
return false;

return buildNumber;
} ();
return s_IsWin11;

return s_BuildNumber;
}

} // namespace
};

LPCWSTR GetPlatformName()
inline bool IsWindows11OrGreater()
{
static std::wstring s_Name = []() -> std::wstring
static bool s_IsWindows11OrGreater = []() -> bool
{
const bool isServer = IsWindowsServer();
std::wstring releaseID = GetPlatformReleaseID();
if (!IsWindows10OrGreater()) return false;

// Note: Place newer versions at the top.
const WCHAR* version =
IsWin11() ? L"11" : // Temporary hack
IsWindows10OrGreater() ? (isServer ? (releaseID == L"1809" ? L"2019" : L"2016") : L"10") :
IsWindows8Point1OrGreater() ? (isServer ? L"2012 R2" : L"8.1") :
IsWindows8OrGreater() ? (isServer ? L"2012" : L"8") :
IsWindows7OrGreater() ? (isServer ? L"2008 R2" : L"7") :
nullptr; // Unknown
if (version)
{
std::wstring name = L"Windows ";
name += isServer ? L"Server " : L"";
name += version;
return name;
}
// |GetTempPath2W| doesn't exist in Windows version prior to Windows 11 (as of yet)
typedef void* (__stdcall* TempPath2)();
HMODULE hmod = GetModuleHandle(L"kernel32");
if (!hmod) return false;

TempPath2 tmpPath2 = (TempPath2)GetProcAddress(hmod, "GetTempPath2W");

return L"Unknown";
int buildNumber = _wtoi(GetBuildNumberFromRegistry().c_str());
return tmpPath2 && (buildNumber >= 22000);
} ();
return s_Name.c_str();

return s_IsWindows11OrGreater;
}

std::wstring GetPlatformReleaseID()
Platform::Platform()
{
static std::wstring s_ID = []()->std::wstring
{
std::wstring id;
Initialize();
}

if (IsWindows10OrGreater())
{
WCHAR buffer[10] = { 0 };
DWORD size = _countof(buffer);
Platform::~Platform()
{
}

HKEY hKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion", 0UL, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
{
if (RegQueryValueEx(hKey, L"ReleaseId", nullptr, nullptr, (LPBYTE)buffer, (LPDWORD)&size) == ERROR_SUCCESS)
{
id = buffer;
}
RegCloseKey(hKey);
}
}
return id;
} ();
return s_ID; // Can be empty!
Platform& Platform::GetInstance()
{
static Platform s_Platform;
return s_Platform;
}

std::wstring GetPlatformFriendlyName()
void Platform::Initialize()
{
std::wstring name;
// Is Windows 64 bit?
m_Is64BitWindows = [&]() -> bool
{
#if _WIN64
return true;
#endif
auto isWow64Process = (decltype(IsWow64Process)*)GetProcAddress(GetModuleHandle(L"kernel32"), "IsWow64Process");
if (isWow64Process)
{
BOOL isWow64 = FALSE;
return isWow64Process(GetCurrentProcess(), &isWow64) && isWow64;
}
return false;
} ();

WCHAR buffer[256] = { 0 };
DWORD size = _countof(buffer);
std::wstring ubrStr;
std::wstring servicePack;

HKEY hKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion", 0UL, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
HKEY hkey = nullptr;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion", 0UL, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS)
{
if (RegQueryValueEx(hKey, L"ProductName", nullptr, nullptr, (LPBYTE)buffer, (LPDWORD)&size) == ERROR_SUCCESS)
{
name = buffer;
if (IsWin11()) // Temporary hack
{
size_t pos = name.find(L"Windows 10");
if (pos != std::wstring::npos)
{
name.replace(pos, 10ULL, L"Windows 11");
}
}
WCHAR buffer[256] = { 0 };
DWORD size = _countof(buffer);

// For Windows 10 (and above?), use the "ReleaseId" as part of the version number.
// (ie. 1507, 1511, 1607, 1703, 1709, 1803, 1809, 1903, 1909, 2004, 2009, ...)
std::wstring id = GetPlatformReleaseID();
if (!id.empty())
// Get Windows 10/11 specific values
if (IsWindows10OrGreater())
{
// Prefer "DisplayVersion" over "ReleaseId"
if ((RegQueryValueEx(hkey, L"DisplayVersion", nullptr, nullptr, (LPBYTE)buffer, (LPDWORD)&size) == ERROR_SUCCESS) ||
(RegQueryValueEx(hkey, L"ReleaseId", nullptr, nullptr, (LPBYTE)buffer, (LPDWORD)&size) == ERROR_SUCCESS))
{
name += L' ';
name += id;
m_DisplayVersion = buffer;
}
}

name += Is64BitWindows() ? L" 64-bit" : L" 32-bit";
size = _countof(buffer);
if (RegQueryValueEx(hkey, L"ProductName", nullptr, nullptr, (LPBYTE)buffer, (LPDWORD)&size) == ERROR_SUCCESS)
{
m_ProductName = buffer;

size = _countof(buffer);
if (RegQueryValueEx(hKey, L"CurrentBuildNumber", nullptr, nullptr, (LPBYTE)buffer, (LPDWORD)&size) == ERROR_SUCCESS ||
RegQueryValueEx(hKey, L"CurrentBuild", nullptr, nullptr, (LPBYTE)buffer, (LPDWORD)&size) == ERROR_SUCCESS)
if (IsWindows11OrGreater() && !m_DisplayVersion.empty())
{
name += L" (build ";
name += buffer;
name += L')';
size_t pos = m_ProductName.find(L"Windows 10");
if (_wcsnicmp(L"Windows 10", m_ProductName.c_str(), 10ULL) == 0)
{
m_ProductName.replace(pos, 10ULL, L"Windows 11");
}
}
}

size = _countof(buffer);
if (RegQueryValueEx(hKey, L"CSDVersion", nullptr, nullptr, (LPBYTE)buffer, (LPDWORD)&size) == ERROR_SUCCESS)
{
name += L' ';
name += buffer;
}
DWORD ubr = 0UL;
size = sizeof(DWORD);
if (RegQueryValueEx(hkey, L"UBR", nullptr, nullptr, (LPBYTE)&ubr, &size) == ERROR_SUCCESS && ubr > 0UL)
{
ubrStr = L'.';
ubrStr += std::to_wstring(ubr);
}

size = _countof(buffer);
if (RegQueryValueEx(hkey, L"CSDVersion", nullptr, nullptr, (LPBYTE)buffer, (LPDWORD)&size) == ERROR_SUCCESS)
{
servicePack = buffer;
}

RegCloseKey(hKey);
RegCloseKey(hkey);
hkey = nullptr;
}
else

// Retrieve build number
m_BuildNumber = GetBuildNumberFromRegistry();

// Set Name
const bool isServer = IsWindowsServer();
m_Name = isServer ? L"Windows Server " : L"Windows ";
m_Name += [&]() -> LPCWSTR
{
return
IsWindows11OrGreater() ? L"11" :
IsWindows10OrGreater() ? (isServer ?
(m_DisplayVersion == L"21H2" ? L"2022" :
(m_DisplayVersion == L"1809" ? L"2019" : L"2016")) : L"10") :
IsWindows8Point1OrGreater() ? (isServer ? L"2012 R2" : L"8.1") :
IsWindows8OrGreater() ? (isServer ? L"2012" : L"8") :
IsWindows7OrGreater() ? (isServer ? L"2008 R2" : L"7") :
L"Unknown";
} ();

// Set "friendly" name
m_FriendlyName = m_ProductName;
if (!m_DisplayVersion.empty())
{
name = L"Windows version unknown";
m_FriendlyName += L' ';
m_FriendlyName += m_DisplayVersion;
}
if (!m_BuildNumber.empty())
{
m_FriendlyName += L" (build ";
m_FriendlyName += m_BuildNumber;
m_FriendlyName += ubrStr;

return name;
}
if (!servicePack.empty())
{
m_FriendlyName += L": ";
m_FriendlyName += servicePack;
}

std::wstring GetPlatformUserLanguage()
{
m_FriendlyName += L')';
}
m_FriendlyName += m_Is64BitWindows ? L" 64-bit" : L" 32-bit";

// Retrieve user language LCID
LANGID id = GetUserDefaultUILanguage();
LCID lcid = MAKELCID(id, SORT_DEFAULT);
WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
if (GetLocaleInfo(lcid, LOCALE_SENGLISHLANGUAGENAME, buffer, _countof(buffer)) == 0)
{
_snwprintf_s(buffer, _TRUNCATE, L"%s", L"<error>");
}
return std::wstring(buffer);
m_UserLanguage = buffer;
}

/*
** Returns |true| if running on 64-bit Windows.
*/
bool Is64BitWindows()
{
#if _WIN64
return true;
#endif

auto isWow64Process = (decltype(IsWow64Process)*)GetProcAddress(GetModuleHandle(L"kernel32"), "IsWow64Process");
if (isWow64Process)
{
BOOL isWow64 = FALSE;
return isWow64Process(GetCurrentProcess(), &isWow64) && isWow64;
}

return false;
}

} // namespace Platform
38 changes: 38 additions & 0 deletions Common/Platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,43 @@

#include <string>

class Platform
{
public:
static Platform& GetInstance();

bool Is64BitWindows() { return m_Is64BitWindows; }

std::wstring GetName() { return m_Name; }
std::wstring GetFriendlyName() { return m_FriendlyName; }
std::wstring GetReleaseID() { return m_DisplayVersion; } // Can be empty
std::wstring GetBuildNumber() { return m_BuildNumber; }
std::wstring GetProductName() { return m_ProductName; }
std::wstring GetUserLanguage() { return m_UserLanguage; }

private:
Platform();
~Platform();

Platform(const Platform& other) = delete;
Platform& operator=(Platform other) = delete;

void Initialize();

bool m_Is64BitWindows;

std::wstring m_Name;
std::wstring m_FriendlyName;
std::wstring m_DisplayVersion;
std::wstring m_BuildNumber;
std::wstring m_ProductName;
std::wstring m_UserLanguage;
};

// Convenience function.
inline Platform& GetPlatform() { return Platform::GetInstance(); }

/*
namespace Platform {
LPCWSTR GetPlatformName();
Expand All @@ -19,5 +56,6 @@ std::wstring GetPlatformUserLanguage();
bool Is64BitWindows();
} // namespace Platform
*/

#endif
8 changes: 4 additions & 4 deletions Library/DialogAbout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1755,8 +1755,8 @@ void DialogAbout::TabVersion::Initialize()
SetWindowText(item, tmpSz);

_snwprintf_s(tmpSz, _TRUNCATE, L"%s - %s (%hu)",
Platform::GetPlatformFriendlyName().c_str(),
Platform::GetPlatformUserLanguage().c_str(),
GetPlatform().GetFriendlyName().c_str(),
GetPlatform().GetUserLanguage().c_str(),
GetUserDefaultUILanguage());
item = GetControl(Id_WinVerLabel);
SetWindowText(item, tmpSz);
Expand Down Expand Up @@ -1831,8 +1831,8 @@ INT_PTR DialogAbout::TabVersion::OnCommand(WPARAM wParam, LPARAM lParam)
tmpSz,
_TRUNCATE,
L"%s - %s (%hu)\n",
Platform::GetPlatformFriendlyName().c_str(),
Platform::GetPlatformUserLanguage().c_str(),
GetPlatform().GetFriendlyName().c_str(),
GetPlatform().GetUserLanguage().c_str(),
GetUserDefaultUILanguage());

text += tmpSz;
Expand Down
Loading

0 comments on commit 111caf9

Please sign in to comment.