This is an automated email from the ASF dual-hosted git repository. leginee pushed a commit to branch win10-msvc-trunk in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit 440c451588275ca6019e8f22a62dc9d9ca6d709e Author: Peter Kovacs <[email protected]> AuthorDate: Tue Aug 25 15:50:03 2026 +0200 setup: fail the install when the runtime cannot be installed InstallRuntimes() discarded the result of every runtime it installed and returned true unconditionally. A machine where the redistributable failed got an office that installed cleanly and then would not start, with nothing in the UI pointing at the cause. It now aggregates the per-runtime results and fails, with a message saying what happened. Paired with an explicit Windows version check, so an unsupported OS is refused up front and by name rather than surfacing later as an unexplained runtime failure. The floor is Windows 10 -- see external/vcredist/README; it comes with the runtime the binaries need, it is not an independent policy. That check cannot be an MSI LaunchCondition. Windows Installer's VersionNT and WindowsBuild are both capped at Windows 8.1 values: measured on Windows 11 build 26200, msiexec reports VersionNT=603 and WindowsBuild=9600, and Microsoft's own property table stops at 8.1 and never lists Windows 10. A condition of VersionNT >= 1000, or WindowsBuild >= 10240, would refuse to install on every supported Windows including that one. RtlGetVersion is not subject to the shim and reports 10.0 build 26200 on the same machine, so the check lives in setup.exe, which runs before msiexec anyway. Two new strings, and the three files a new one needs: Resource.h, rctmpl.txt and setup.ulf. Built both architectures; RtlGetVersion, ntdll.dll and both new messages are present in each loader2.exe, which also confirms the ulf -> rctmpl -> .rc path. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VrM7EMKgiuyVcCUe9nSbZR --- main/desktop/win32/source/setup/Resource.h | 2 + main/desktop/win32/source/setup/rctmpl.txt | 2 + main/desktop/win32/source/setup/setup.cpp | 124 +++++++++++++++++++++++-- main/desktop/win32/source/setup/setup.hxx | 1 + main/desktop/win32/source/setup/setup.ulf | 6 ++ main/desktop/win32/source/setup/setup_main.cxx | 5 + 6 files changed, 131 insertions(+), 9 deletions(-) diff --git a/main/desktop/win32/source/setup/Resource.h b/main/desktop/win32/source/setup/Resource.h index 49e635525c..1f008a51fc 100644 --- a/main/desktop/win32/source/setup/Resource.h +++ b/main/desktop/win32/source/setup/Resource.h @@ -38,6 +38,8 @@ #define IDS_UNKNOWN_LANG 23 #define IDS_SETUP_TO_OLD 24 #define IDS_SETUP_NOT_FOUND 25 +#define IDS_OS_TO_OLD 26 +#define IDS_RUNTIME_FAILED 27 #define IDS_LANGUAGE_ENGLISH 50 #define IDS_LANGUAGE_SPAIN 51 #define IDS_LANGUAGE_GERMAN 52 diff --git a/main/desktop/win32/source/setup/rctmpl.txt b/main/desktop/win32/source/setup/rctmpl.txt index d751726d6f..f2f768c3f2 100644 --- a/main/desktop/win32/source/setup/rctmpl.txt +++ b/main/desktop/win32/source/setup/rctmpl.txt @@ -33,6 +33,8 @@ BEGIN IDS_INVALID_PARAM %INVALID_PARAM% IDS_SETUP_TO_OLD %SETUP_TO_OLD% IDS_SETUP_NOT_FOUND %SETUP_NOT_FOUND% + IDS_OS_TO_OLD %OS_TO_OLD% + IDS_RUNTIME_FAILED %RUNTIME_FAILED% IDS_USAGE %USAGE% IDS_ALREADY_RUNNING %ALREADY_RUNNING% IDS_UNKNOWN_ERROR %UNKNOWN_ERROR% diff --git a/main/desktop/win32/source/setup/setup.cpp b/main/desktop/win32/source/setup/setup.cpp index b0036f790f..6e62b818d5 100644 --- a/main/desktop/win32/source/setup/setup.cpp +++ b/main/desktop/win32/source/setup/setup.cpp @@ -64,6 +64,13 @@ #define ERROR_SHOW_USAGE -2 #define ERROR_SETUP_TO_OLD -3 #define ERROR_SETUP_NOT_FOUND -4 +#define ERROR_OS_TO_OLD -5 +#define ERROR_RUNTIME_FAILED -6 + +// Lowest Windows this office can run on. Not an arbitrary policy: the VC v14 +// redistributable that supplies the runtime the binaries need installs only on +// Windows 10/11 and Server 2016 and later, so the floor comes with the toolset. +#define REQUIRED_WINDOWS_MAJOR 10 #define PARAM_SETUP_USED TEXT( " SETUP_USED=1 " ) #define PARAM_PACKAGE TEXT( "/I " ) @@ -1041,6 +1048,12 @@ void SetupApp::DisplayError( UINT nErr ) const nMsgType = MB_OK | MB_ICONINFORMATION; WIN::LoadString( m_hInst, IDS_USAGE, sError, MAX_TEXT_LENGTH ); break; + case ERROR_OS_TO_OLD: // - 5 + WIN::LoadString( m_hInst, IDS_OS_TO_OLD, sError, MAX_TEXT_LENGTH ); + break; + case ERROR_RUNTIME_FAILED: // - 6 + WIN::LoadString( m_hInst, IDS_RUNTIME_FAILED, sError, MAX_TEXT_LENGTH ); + break; default: WIN::LoadString( m_hInst, IDS_UNKNOWN_ERROR, sError, MAX_TEXT_LENGTH ); break; @@ -1838,6 +1851,75 @@ boolean SetupApp::IsPatchInstalled( TCHAR* pBaseDir, TCHAR* pFileName ) else return false; } +//-------------------------------------------------------------------------- +// The real Windows version, not the shimmed one. +// +// GetVersionEx() and the Windows Installer VersionNT / WindowsBuild properties all +// report Windows 8.1 (6.3 / 9600) on Windows 10 and 11 unless the caller carries a +// supportedOS manifest entry. Measured on Windows 11 build 26200, msiexec reports +// VersionNT=603 and WindowsBuild=9600 -- which is why this check cannot be expressed +// as an MSI LaunchCondition, and lives here instead. +// +// RtlGetVersion is not subject to that shim. +static bool GetRealWindowsVersion( DWORD *pMajor, DWORD *pBuild ) +{ + typedef LONG ( WINAPI *pfnRtlGetVersion_t )( OSVERSIONINFOW * ); + + HMODULE hNtdll = ::GetModuleHandle( TEXT( "ntdll.dll" ) ); + if ( hNtdll == NULL ) + return false; + + pfnRtlGetVersion_t pRtlGetVersion = + (pfnRtlGetVersion_t)::GetProcAddress( hNtdll, "RtlGetVersion" ); + if ( pRtlGetVersion == NULL ) + return false; + + OSVERSIONINFOW aInfo; + ZeroMemory( &aInfo, sizeof( aInfo ) ); + aInfo.dwOSVersionInfoSize = sizeof( aInfo ); + + if ( pRtlGetVersion( &aInfo ) != 0 ) + return false; + + *pMajor = aInfo.dwMajorVersion; + *pBuild = aInfo.dwBuildNumber; + return true; +} + +//-------------------------------------------------------------------------- +boolean SetupApp::CheckOSVersion() +{ + DWORD nMajor = 0; + DWORD nBuild = 0; + + if ( !GetRealWindowsVersion( &nMajor, &nBuild ) ) + { + // Could not determine the version. Let the install proceed rather than + // refuse on a machine we simply failed to identify -- if the OS really is too + // old the runtime install will fail next, and say so. + Log( TEXT( "Warning: could not determine the Windows version.\r\n" ) ); + return true; + } + + TCHAR sBuf[ 128 ]; + + StringCchPrintf( sBuf, 128, TEXT( " Windows major version %u, build %u\r\n" ), + nMajor, nBuild ); + Log( sBuf ); + + if ( nMajor < REQUIRED_WINDOWS_MAJOR ) + { + StringCchPrintf( sBuf, 128, + TEXT( "ERROR: Windows %u is older than the required Windows %u.\r\n" ), + nMajor, (DWORD)REQUIRED_WINDOWS_MAJOR ); + Log( sBuf ); + SetError( ERROR_OS_TO_OLD ); + return false; + } + + return true; +} + //-------------------------------------------------------------------------- // Is the VC v14 runtime already usable in THIS process? // @@ -1996,6 +2078,8 @@ boolean SetupApp::InstallRuntimes() OutputDebugStringFormat( TEXT( "found architecture<%d>\r\n" ), siSysInfo.wProcessorArchitecture ); + bool bOk = true; + #if defined( _WIN64 ) // A 64 bit office ships no 32 bit binaries at all, so it needs only the x64 @@ -2004,9 +2088,12 @@ boolean SetupApp::InstallRuntimes() (void)siSysInfo; if ( GetPathToFile( RUNTIME_X64_NAME, &sRuntimePath ) ) - InstallRuntimes( sRuntimePath, true ); + bOk = InstallRuntimes( sRuntimePath, true ) ? true : false; else - Log( TEXT( "ERROR: no installer for x64 runtime libraries found!" ) ); + { + Log( TEXT( "ERROR: no installer for x64 runtime libraries found!\r\n" ) ); + bOk = false; + } if ( sRuntimePath ) delete [] sRuntimePath; @@ -2019,9 +2106,15 @@ boolean SetupApp::InstallRuntimes() if ( siSysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ) { if ( GetPathToFile( RUNTIME_X64_NAME, &sRuntimePath ) ) - InstallRuntimes( sRuntimePath, false ); // cannot probe x64 from a 32 bit process + { + if ( !InstallRuntimes( sRuntimePath, false ) ) // cannot probe x64 from a 32 bit process + bOk = false; + } else - Log( TEXT( "ERROR: no installer for x64 runtime libraries found!" ) ); + { + Log( TEXT( "ERROR: no installer for x64 runtime libraries found!\r\n" ) ); + bOk = false; + } if ( sRuntimePath ) { @@ -2031,18 +2124,31 @@ boolean SetupApp::InstallRuntimes() } if ( GetPathToFile( RUNTIME_X86_NAME, &sRuntimePath ) ) - InstallRuntimes( sRuntimePath, true ); + { + if ( !InstallRuntimes( sRuntimePath, true ) ) + bOk = false; + } else - Log( TEXT( "ERROR: no installer for x86 runtime libraries found!" ) ); + { + Log( TEXT( "ERROR: no installer for x86 runtime libraries found!\r\n" ) ); + bOk = false; + } if ( sRuntimePath ) delete [] sRuntimePath; #endif - // NB still unconditionally true: making a failed runtime install fatal is a separate - // change, so that it can be bisected on its own if it turns out to reject a machine - // we did not expect. + // A failed runtime install used to be swallowed here -- the per-runtime result was + // discarded and this returned true regardless. The office then installed and could + // not start, with nothing to point at. Fail loudly instead. + if ( !bOk ) + { + Log( TEXT( "ERROR: the Visual C++ runtime could not be installed.\r\n" ) ); + SetError( ERROR_RUNTIME_FAILED ); + return false; + } + return true; } diff --git a/main/desktop/win32/source/setup/setup.hxx b/main/desktop/win32/source/setup/setup.hxx index fac796608e..ffe835ee5a 100644 --- a/main/desktop/win32/source/setup/setup.hxx +++ b/main/desktop/win32/source/setup/setup.hxx @@ -109,6 +109,7 @@ public: virtual boolean GetPatches(); virtual boolean ChooseLanguage( long& rLanguage ); virtual boolean CheckVersion(); + virtual boolean CheckOSVersion(); virtual boolean CheckForUpgrade(); virtual boolean InstallRuntimes(); virtual boolean Install( long nLanguage ); diff --git a/main/desktop/win32/source/setup/setup.ulf b/main/desktop/win32/source/setup/setup.ulf index 03a7013409..1950668380 100644 --- a/main/desktop/win32/source/setup/setup.ulf +++ b/main/desktop/win32/source/setup/setup.ulf @@ -49,6 +49,12 @@ en-US = "This package requires at least the version '%s' of the Windows Installe [%SETUP_NOT_FOUND%] en-US = "This package requires the Windows Installer. \nYou need at least Windows Installer '%s' on your system!" +[%OS_TO_OLD%] +en-US = "This version of %PRODUCTNAME% requires Windows 10 or later. \nIt cannot be installed on this version of Windows." + +[%RUNTIME_FAILED%] +en-US = "The Microsoft Visual C++ runtime could not be installed. \n%PRODUCTNAME% cannot run without it, so the installation has been stopped." + [%USAGE%] en-US = "Usage:\n /? : Shows this dialog.\n /a : Performs an administrative installation.\n /j[u|m] : Performs an advertising installation.\n /q[n] : Do not show any user interface (silent mode).\n" diff --git a/main/desktop/win32/source/setup/setup_main.cxx b/main/desktop/win32/source/setup/setup_main.cxx index 0e7b7b797e..168de92810 100644 --- a/main/desktop/win32/source/setup/setup_main.cxx +++ b/main/desktop/win32/source/setup/setup_main.cxx @@ -68,6 +68,11 @@ extern "C" int __stdcall WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, int ) if ( ! pSetup->CheckVersion() ) throw pSetup->GetError(); + // Refuse early and clearly on an unsupported Windows, rather than letting the + // runtime install fail later with a message that does not name the real cause. + if ( ! pSetup->CheckOSVersion() ) + throw pSetup->GetError(); + if ( ! pSetup->IsAdminInstall() ) if ( ! pSetup->GetPatches() ) throw pSetup->GetError();
