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 db685841c4f8cffbf3d9a693b2c34b73a842cb4f
Author: Peter Kovacs <[email protected]>
AuthorDate: Tue Aug 25 15:13:44 2026 +0200

    setup: detect the VC runtime by loading it, not by ProductCode
    
    Three things were wrong with how setup.exe handled the runtime prerequisite.
    
    1. Detection.  InstallRuntimes() gated on MsiQueryProductState() against a 
hardcoded
       VC++ 2008 ProductCode.  That cannot work for the v14 runtime: Microsoft 
reissues
       the redistributable with a NEW ProductCode on every servicing revision, 
so an
       exact-GUID test misreports every machine carrying anything but the one 
pinned
       build.  Replaced with a functional check -- LoadLibraryEx of 
vcruntime140.dll,
       msvcp140.dll and (x64 only) vcruntime140_1.dll, with
       LOAD_LIBRARY_SEARCH_SYSTEM32 so a stray copy beside setup.exe cannot 
fool it.
       No registry, no GUID, nothing that goes stale; it tests the thing that 
actually
       matters, which is whether our DLLs will bind.
    
       The probe can only answer for setup.exe's own bitness -- a 64 bit 
process cannot
       load a 32 bit DLL -- so the caller passes whether the runtime being 
considered
       matches.  When it does not we simply run the bundle, which is idempotent.
    
    2. Command line.  PARAM_SILENTINSTALL was " /Q", which the VC++ 2008 
installer
       understood and a Burn bundle does not.  Now " /install /quiet 
/norestart".
       Without /norestart the bundle may reboot the machine mid-install.
    
    3. Exit codes.  Anything non-zero was treated as failure.  Burn also 
returns 1638
       (a newer runtime is already installed) and 3010 (success, reboot 
required), both
       of which are success here -- 1638 is the common case on any developer 
machine.
    
    Also conditioned by architecture.  A 64 bit office ships no 32 bit 
binaries, so it
    needs only the x64 runtime.  The 32 bit office still needs both, because it
    cross-builds 64 bit shell extensions (shlxthdl_x64, ooofilt_x64, 
propertyhdl_x64,
    so_activex_x64) that get loaded by 64 bit Explorer.
    
    InstallRuntimes() still returns true unconditionally.  Making a failed 
runtime
    install fatal is deliberately left as a separate change so it can be 
bisected on its
    own if it turns out to reject a machine we did not expect.
    
    Verified by building both architectures and reading the binaries:
    
        x64 loader2.exe   /install /quiet /norestart, vcruntime140.dll, 
msvcp140.dll,
                          vcruntime140_1.dll present; both 2008 GUIDs and " /Q" 
gone
        x86 loader2.exe   same, except vcruntime140_1.dll correctly absent
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01VrM7EMKgiuyVcCUe9nSbZR
---
 main/desktop/win32/source/setup/setup.cpp | 122 ++++++++++++++++++++++++++----
 main/desktop/win32/source/setup/setup.hxx |   2 +-
 2 files changed, 107 insertions(+), 17 deletions(-)

diff --git a/main/desktop/win32/source/setup/setup.cpp 
b/main/desktop/win32/source/setup/setup.cpp
index d1be11df41..b0036f790f 100644
--- a/main/desktop/win32/source/setup/setup.cpp
+++ b/main/desktop/win32/source/setup/setup.cpp
@@ -74,7 +74,17 @@
 #define PARAM_PATCH         TEXT( " /update " )
 #define PARAM_REG_ALL_MSO_TYPES TEXT( "REGISTER_ALL_MSO_TYPES=1 " )
 #define PARAM_REG_NO_MSO_TYPES  TEXT( "REGISTER_NO_MSO_TYPES=1 " )
-#define PARAM_SILENTINSTALL     TEXT( " /Q" )
+// The VC v14 redistributable is a Burn bundle, not the old MSI-style 
installer the
+// VC++ 2008 package was.  It does not understand /Q; its silent switches are 
these.
+// /norestart matters -- without it the bundle may reboot the machine 
mid-install.
+#define PARAM_SILENTINSTALL     TEXT( " /install /quiet /norestart" )
+
+// Burn exit codes that mean "the runtime is now present".  1638 is
+// ERROR_PRODUCT_VERSION: a NEWER runtime is already installed, which is 
success for
+// our purposes -- it is also the common case on any developer machine.
+#define RUNTIME_INSTALL_OK          0L
+#define RUNTIME_INSTALL_NEWER       1638L
+#define RUNTIME_INSTALL_REBOOT_REQ  3010L
 
 #define PARAM_RUNNING           TEXT( "ignore_running" )
 #define CMDLN_REG_ALL_MSO_TYPES TEXT( "msoreg=1" )
@@ -85,10 +95,13 @@
 
 #define RUNTIME_X64_NAME    TEXT( "redist\\vcredist_x64.exe" )
 #define RUNTIME_X86_NAME    TEXT( "redist\\vcredist_x86.exe" )
-// Microsoft Visual C++ 2008 Redistributable - x86 9.0.30729.6161
-#define PRODUCTCODE_X86     TEXT( "{9BE518E6-ECC6-35A9-88E4-87755C07200F}" )
-// Microsoft Visual C++ 2008 Redistributable - x64 9.0.30729.6161
-#define PRODUCTCODE_X64     TEXT( "{5FCE6D76-F5DC-37AB-B2B8-22AB8CEDB1D4}" )
+
+// There is deliberately no ProductCode here any more.  The old code gated on
+// MsiQueryProductState() against a hardcoded VC++ 2008 GUID, which cannot 
work for the
+// v14 runtime: Microsoft reissues that redistributable with a NEW ProductCode 
on every
+// servicing revision, so an exact-GUID test misreports every machine that has 
anything
+// other than the one pinned build.  We test for the runtime functionally 
instead --
+// see RuntimeAlreadyPresent().
 
 #define ADVAPI32API_CheckTokenMembership "CheckTokenMembership"
 
@@ -1826,12 +1839,50 @@ boolean SetupApp::IsPatchInstalled( TCHAR* pBaseDir, 
TCHAR* pFileName )
 }
 
 //--------------------------------------------------------------------------
-boolean SetupApp::InstallRuntimes( TCHAR *sProductCode, TCHAR *sRuntimePath )
+// Is the VC v14 runtime already usable in THIS process?
+//
+// Deliberately functional rather than a registry or ProductCode lookup: it 
needs no
+// GUID that goes stale with every servicing update, no registry view 
juggling, and it
+// tests the thing that actually matters -- whether our DLLs will be able to 
bind.
+//
+// LOAD_LIBRARY_SEARCH_SYSTEM32 so we probe the machine-wide runtime and 
cannot be
+// fooled by a stray copy sitting next to setup.exe.
+//
+// This can only answer for setup.exe's own bitness -- a 64 bit process cannot 
load a
+// 32 bit DLL and vice versa.  That is fine: the caller only uses it to skip 
the
+// matching redistributable.  For the other architecture we just run the 
bundle, which
+// is idempotent and returns 1638 quickly when a newer runtime is already 
there.
+static bool RuntimeAlreadyPresent()
 {
-    INSTALLSTATE  nRet = aoo_MsiQueryProductState( sProductCode );
-    OutputDebugStringFormat( TEXT( "MsiQueryProductState returned <%d>\r\n" ), 
nRet );
-    if ( nRet == INSTALLSTATE_DEFAULT )
+    const TCHAR *pModules[] = {
+        TEXT( "vcruntime140.dll" ),
+        TEXT( "msvcp140.dll" ),
+#if defined( _WIN64 )
+        // x64 only -- the separate EH runtime introduced with VS2017.
+        TEXT( "vcruntime140_1.dll" ),
+#endif
+    };
+
+    for ( size_t i = 0; i < sizeof( pModules ) / sizeof( pModules[0] ); ++i )
+    {
+        HMODULE hMod = ::LoadLibraryEx( pModules[i], NULL,
+                                        LOAD_LIBRARY_SEARCH_SYSTEM32 );
+        if ( hMod == NULL )
+            return false;
+        ::FreeLibrary( hMod );
+    }
+
+    return true;
+}
+
+//--------------------------------------------------------------------------
+boolean SetupApp::InstallRuntimes( TCHAR *sRuntimePath, bool 
bMatchesOwnArchitecture )
+{
+    if ( bMatchesOwnArchitecture && RuntimeAlreadyPresent() )
+    {
+        Log( TEXT( " Runtime already present, skipping <%s>\r\n" ), 
sRuntimePath );
         return true;
+    }
 
     Log( TEXT( " Will install runtime <%s>\r\n" ), sRuntimePath );
     OutputDebugStringFormat( TEXT( " Will install runtime <%s>\r\n" ), 
sRuntimePath );
@@ -1879,16 +1930,30 @@ boolean SetupApp::InstallRuntimes( TCHAR *sProductCode, 
TCHAR *sRuntimePath )
     else
     {
         GetExitCodeProcess( aPI.hProcess, &nResult );
-        SetError( nResult );
 
-        if ( nResult != ERROR_SUCCESS )
+        // Burn reports more than one flavour of success.  Treating anything 
non-zero as
+        // a failure would flag every machine that already carries a newer 
runtime.
+        if ( nResult == RUNTIME_INSTALL_OK )
+        {
+            Log( TEXT( " Installation of runtime completed successfully.\r\n" 
) );
+        }
+        else if ( nResult == RUNTIME_INSTALL_NEWER )
+        {
+            Log( TEXT( " A newer runtime is already installed, nothing to 
do.\r\n" ) );
+        }
+        else if ( nResult == RUNTIME_INSTALL_REBOOT_REQ )
+        {
+            Log( TEXT( " Installation of runtime completed, a reboot is 
required.\r\n" ) );
+        }
+        else
         {
             TCHAR sBuf[80];
-            StringCchPrintf( sBuf, 80, TEXT("Warning: install runtime returned 
%u.\r\n"), nResult );
+            StringCchPrintf( sBuf, 80,
+                             TEXT("ERROR: install runtime returned %u.\r\n"), 
nResult );
             Log( sBuf );
+            SetError( nResult );
+            bRet = false;
         }
-        else
-            Log( TEXT( " Installation of runtime completed successfully.\r\n" 
) );
     }
 
     CloseHandle( aPI.hProcess );
@@ -1931,10 +1996,30 @@ boolean SetupApp::InstallRuntimes()
 
     OutputDebugStringFormat( TEXT( "found architecture<%d>\r\n" ), 
siSysInfo.wProcessorArchitecture );
 
+#if defined( _WIN64 )
+
+    // A 64 bit office ships no 32 bit binaries at all, so it needs only the 
x64
+    // runtime.  (The 32 bit office is the other way round: it cross-builds 64 
bit
+    // shell extensions, which is why the branch below installs both.)
+    (void)siSysInfo;
+
+    if ( GetPathToFile( RUNTIME_X64_NAME, &sRuntimePath ) )
+        InstallRuntimes( sRuntimePath, true );
+    else
+        Log( TEXT( "ERROR: no installer for x64 runtime libraries found!" ) );
+
+    if ( sRuntimePath )
+        delete [] sRuntimePath;
+
+#else
+
+    // 32 bit office.  On 64 bit Windows it additionally needs the x64 
runtime, because
+    // its shell extensions (shlxthdl_x64, ooofilt_x64, propertyhdl_x64, 
so_activex_x64)
+    // are 64 bit and get loaded by 64 bit Explorer and friends.
     if ( siSysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 )
     {
         if ( GetPathToFile( RUNTIME_X64_NAME, &sRuntimePath ) )
-            InstallRuntimes( PRODUCTCODE_X64, sRuntimePath );
+            InstallRuntimes( sRuntimePath, false );   // cannot probe x64 from 
a 32 bit process
         else
             Log( TEXT( "ERROR: no installer for x64 runtime libraries found!" 
) );
 
@@ -1946,13 +2031,18 @@ boolean SetupApp::InstallRuntimes()
     }
 
     if ( GetPathToFile( RUNTIME_X86_NAME, &sRuntimePath ) )
-        InstallRuntimes( PRODUCTCODE_X86, sRuntimePath );
+        InstallRuntimes( sRuntimePath, true );
     else
         Log( TEXT( "ERROR: no installer for x86 runtime libraries found!" ) );
 
     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.
     return true;
 }
 
diff --git a/main/desktop/win32/source/setup/setup.hxx 
b/main/desktop/win32/source/setup/setup.hxx
index 2f82cb6b07..fac796608e 100644
--- a/main/desktop/win32/source/setup/setup.hxx
+++ b/main/desktop/win32/source/setup/setup.hxx
@@ -96,7 +96,7 @@ private:
     boolean     IsTerminalServerInstalled() const;
     void        AddFileToPatchList( TCHAR* pPath, TCHAR* pFile );
     boolean     IsPatchInstalled( TCHAR* pBaseDir, TCHAR* pFileName );
-    boolean     InstallRuntimes( TCHAR* pProductCode, TCHAR* pFileName );
+    boolean     InstallRuntimes( TCHAR* pFileName, bool 
bMatchesOwnArchitecture );
 
 public:
     UINT            m_uiRet;

Reply via email to