diff --git a/base/applications/winver/CMakeLists.txt b/base/applications/winver/CMakeLists.txt index ac5b762b83ddd..55c82b76600fe 100644 --- a/base/applications/winver/CMakeLists.txt +++ b/base/applications/winver/CMakeLists.txt @@ -1,5 +1,11 @@ -add_executable(winver winver.c winver.rc) +list(APPEND SOURCE + osinfo.c + winver.c + winver_p.h) + +add_executable(winver ${SOURCE} winver.rc) set_module_type(winver win32gui UNICODE) -add_importlibs(winver shell32 comctl32 msvcrt kernel32) +add_importlibs(winver advapi32 user32 shell32 comctl32 msvcrt kernel32) +add_pch(winver winver_p.h SOURCE) add_cd_file(TARGET winver DESTINATION reactos/system32 FOR all) diff --git a/base/applications/winver/lang/en-US.rc b/base/applications/winver/lang/en-US.rc new file mode 100644 index 0000000000000..1474f0fded2d7 --- /dev/null +++ b/base/applications/winver/lang/en-US.rc @@ -0,0 +1,7 @@ +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +STRINGTABLE +BEGIN + IDS_OSINFO_COMPAT_FORMAT "Reporting NT %lu.%lu (Build %lu%s)" + IDS_OSINFO_SPK_FORMAT ": %s" +END diff --git a/base/applications/winver/osinfo.c b/base/applications/winver/osinfo.c new file mode 100644 index 0000000000000..1d127875a6962 --- /dev/null +++ b/base/applications/winver/osinfo.c @@ -0,0 +1,189 @@ +/* + * PROJECT: ReactOS Version Program + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Retrieve OS name and simple compatibility information + * COPYRIGHT: Copyright 2025 Thamatip Chitpong + */ + +#include "winver_p.h" + +#define OSINFO_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion" +#define MAJOR_VER_MAX_LEN 16 +#define MINOR_VER_MAX_LEN 16 +#define BUILD_NUMBER_MAX_LEN 16 + +static +LPWSTR +Winver_GetRegValueString( + _In_ HKEY hKey, + _In_ LPCWSTR lpValue) +{ + DWORD dwType = REG_NONE; + DWORD dwSize; + LONG lError; + LPWSTR pszValue; + + lError = RegQueryValueExW(hKey, lpValue, NULL, &dwType, NULL, &dwSize); + if (lError != ERROR_SUCCESS || dwType != REG_SZ) + return NULL; + + /* NOTE: Reserved space for a NULL terminator */ + pszValue = HeapAlloc(GetProcessHeap(), 0, dwSize + sizeof(WCHAR)); + if (!pszValue) + return NULL; + + lError = RegQueryValueExW(hKey, lpValue, NULL, &dwType, (LPBYTE)pszValue, &dwSize); + if (lError != ERROR_SUCCESS) + { + HeapFree(GetProcessHeap(), 0, pszValue); + return NULL; + } + + /* Ensure the returned string is NULL terminated */ + pszValue[dwSize / sizeof(WCHAR)] = UNICODE_NULL; + + return pszValue; +} + +static +LPWSTR +Winver_GetOSProductName(VOID) +{ + HKEY hKey; + LONG lError; + LPWSTR pszRet; + + lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, + OSINFO_KEY, + 0, + KEY_QUERY_VALUE, + &hKey); + if (lError != ERROR_SUCCESS) + return NULL; + + pszRet = Winver_GetRegValueString(hKey, L"ProductName"); + + RegCloseKey(hKey); + + return pszRet; +} + +static +VOID +Winver_FormatCompatSpkInfo( + _Inout_ PWINVER_OS_INFO OSInfo, + _In_ LPCWSTR lpSpk) +{ + static const WCHAR szFmtSpecs[] = L"%s"; + WCHAR szFormat[8]; + DWORD dwLength; + + dwLength = LoadStringW(GetModuleHandleW(NULL), + IDS_OSINFO_SPK_FORMAT, + szFormat, + _countof(szFormat)); + if (dwLength < CONST_STR_LEN(szFmtSpecs)) + { + return; + } + dwLength -= CONST_STR_LEN(szFmtSpecs); + + /* NOTE: dwLength excludes format specifiers and includes a NULL terminator */ + dwLength += wcslen(lpSpk) + 1; + OSInfo->pszCompatSpk = HeapAlloc(GetProcessHeap(), 0, dwLength * sizeof(WCHAR)); + if (OSInfo->pszCompatSpk) + { + StringCchPrintfW(OSInfo->pszCompatSpk, dwLength, szFormat, lpSpk); + } +} + +static +VOID +Winver_FormatCompatInfo( + _Inout_ PWINVER_OS_INFO OSInfo) +{ + static const WCHAR szFmtSpecs[] = L"%lu%lu%lu%s"; + WCHAR szFormat[64]; + DWORD dwLength; + OSVERSIONINFOEXW VerInfo; + + dwLength = LoadStringW(GetModuleHandleW(NULL), + IDS_OSINFO_COMPAT_FORMAT, + szFormat, + _countof(szFormat)); + if (dwLength < CONST_STR_LEN(szFmtSpecs)) + { + return; + } + dwLength -= CONST_STR_LEN(szFmtSpecs); + + VerInfo.dwOSVersionInfoSize = sizeof(VerInfo); + if (!GetVersionExW(&VerInfo)) + return; + + /* Service pack info is optional */ + if (*VerInfo.szCSDVersion) + { + Winver_FormatCompatSpkInfo(OSInfo, VerInfo.szCSDVersion); + if (OSInfo->pszCompatSpk) + { + dwLength += wcslen(OSInfo->pszCompatSpk); + } + } + + /* NOTE: dwLength excludes format specifiers and includes a NULL terminator */ + dwLength += MAJOR_VER_MAX_LEN + MINOR_VER_MAX_LEN + BUILD_NUMBER_MAX_LEN + 1; + OSInfo->pszCompatInfo = HeapAlloc(GetProcessHeap(), 0, dwLength * sizeof(WCHAR)); + if (OSInfo->pszCompatInfo) + { + StringCchPrintfW(OSInfo->pszCompatInfo, dwLength, szFormat, + VerInfo.dwMajorVersion, + VerInfo.dwMinorVersion, + VerInfo.dwBuildNumber, + OSInfo->pszCompatSpk ? OSInfo->pszCompatSpk : L""); + } +} + +/** + * @remark + * To enable this feature, the OS product name must be valid. + * The caller can check if the info is available by checking if the returned + * pointer is not NULL. + */ +PWINVER_OS_INFO +Winver_GetOSInfo(VOID) +{ + PWINVER_OS_INFO OSInfo; + + OSInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*OSInfo)); + if (!OSInfo) + return NULL; + + /* OS name */ + OSInfo->pszName = Winver_GetOSProductName(); + if (!OSInfo->pszName) + { + /* This info must be valid */ + HeapFree(GetProcessHeap(), 0, OSInfo); + return NULL; + } + + /* Simple compatibility information */ + Winver_FormatCompatInfo(OSInfo); + + return OSInfo; +} + +VOID +Winver_FreeOSInfo( + _In_ PWINVER_OS_INFO OSInfo) +{ + if (OSInfo) + { + HeapFree(GetProcessHeap(), 0, OSInfo->pszCompatInfo); + HeapFree(GetProcessHeap(), 0, OSInfo->pszCompatSpk); + HeapFree(GetProcessHeap(), 0, OSInfo->pszName); + + HeapFree(GetProcessHeap(), 0, OSInfo); + } +} diff --git a/base/applications/winver/resource.h b/base/applications/winver/resource.h new file mode 100644 index 0000000000000..a336f0d70860c --- /dev/null +++ b/base/applications/winver/resource.h @@ -0,0 +1,4 @@ +#pragma once + +#define IDS_OSINFO_COMPAT_FORMAT 1 +#define IDS_OSINFO_SPK_FORMAT 2 diff --git a/base/applications/winver/winver.c b/base/applications/winver/winver.c index 1e22096c4eef7..73e6a435c1eec 100644 --- a/base/applications/winver/winver.c +++ b/base/applications/winver/winver.c @@ -4,16 +4,13 @@ * FILE: base/applications/winver/winver.c */ -#include -#include -#include -#include -#include -#include +#include "winver_p.h" int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) { INITCOMMONCONTROLSEX iccx; + PWINVER_OS_INFO OSInfo; + int Ret; UNREFERENCED_PARAMETER(hInstance); UNREFERENCED_PARAMETER(hPrevInstance); @@ -25,5 +22,14 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi iccx.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES; InitCommonControlsEx(&iccx); - return ShellAboutW(NULL, L"ReactOS", NULL, NULL); + OSInfo = Winver_GetOSInfo(); + + Ret = ShellAboutW(NULL, + OSInfo ? OSInfo->pszName : L"ReactOS", + OSInfo ? OSInfo->pszCompatInfo : NULL, + NULL); + + Winver_FreeOSInfo(OSInfo); + + return Ret; } diff --git a/base/applications/winver/winver.rc b/base/applications/winver/winver.rc index 050ae69437015..1d9dec6f9d928 100644 --- a/base/applications/winver/winver.rc +++ b/base/applications/winver/winver.rc @@ -2,9 +2,18 @@ #include #include +#include "resource.h" + #define REACTOS_STR_FILE_DESCRIPTION "ReactOS Version Program" #define REACTOS_STR_INTERNAL_NAME "winver" #define REACTOS_STR_ORIGINAL_FILENAME "winver.exe" #include #include + +/* UTF-8 */ +#pragma code_page(65001) + +#ifdef LANGUAGE_EN_US + #include "lang/en-US.rc" +#endif diff --git a/base/applications/winver/winver_p.h b/base/applications/winver/winver_p.h new file mode 100644 index 0000000000000..5c44a2fffb9c1 --- /dev/null +++ b/base/applications/winver/winver_p.h @@ -0,0 +1,38 @@ +/* + * PROJECT: ReactOS Version Program + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Main header + * COPYRIGHT: Copyright 2025 Thamatip Chitpong + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "resource.h" + +#define CONST_STR_LEN(str) (_countof(str) - 1) + +typedef struct _WINVER_OS_INFO +{ + LPWSTR pszName; + LPWSTR pszCompatSpk; + LPWSTR pszCompatInfo; +} WINVER_OS_INFO, *PWINVER_OS_INFO; + +PWINVER_OS_INFO +Winver_GetOSInfo(VOID); + +VOID +Winver_FreeOSInfo( + _In_ PWINVER_OS_INFO OSInfo);