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();

Reply via email to