1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 // MARKER(update_precomp.py): autogen include statement, do not remove 23 #include "precompiled_desktop.hxx" 24 25 #define UNICODE 1 26 #define _UNICODE 1 27 28 #define WIN // scope W32 API 29 30 #if defined _MSC_VER 31 #pragma warning(push, 1) 32 #endif 33 #include <windows.h> 34 #if defined _MSC_VER 35 #pragma warning(pop) 36 #endif 37 #include <tchar.h> 38 #include <assert.h> 39 #include <shlwapi.h> 40 #include <new> 41 #include <time.h> 42 #include <mbctype.h> 43 #include <locale.h> 44 #include <Msiquery.h> 45 #include <MsiDefs.h> 46 #include "strsafe.h" 47 48 #include "setup.hxx" 49 #include "aoo_msi.hxx" 50 51 #include "resource.h" 52 53 //-------------------------------------------------------------------------- 54 55 #define MAX_STR_LENGTH 32000 56 #define MAX_TEXT_LENGTH 1024 57 #define MAX_LANGUAGE_LEN 80 58 #define MAX_STR_CAPTION 256 59 #define VERSION_SIZE 80 60 #define SECTION_SETUP TEXT( "Setup" ) 61 #define SECTION_LANGUAGE TEXT( "Languages" ) 62 #define PRODUCT_NAME_VAR TEXT( "%PRODUCTNAME" ) 63 #define PRODUCT_VERSION TEXT( "ProductVersion" ) 64 #define ERROR_SHOW_USAGE -2 65 #define ERROR_SETUP_TO_OLD -3 66 #define ERROR_SETUP_NOT_FOUND -4 67 #define ERROR_OS_TO_OLD -5 68 #define ERROR_RUNTIME_FAILED -6 69 70 // Lowest Windows this office can run on. Not an arbitrary policy: the VC v14 71 // redistributable that supplies the runtime the binaries need installs only on 72 // Windows 10/11 and Server 2016 and later, so the floor comes with the toolset. 73 #define REQUIRED_WINDOWS_MAJOR 10 74 75 #define PARAM_SETUP_USED TEXT( " SETUP_USED=1 " ) 76 #define PARAM_PACKAGE TEXT( "/I " ) 77 #define PARAM_MINOR_UPGRADE TEXT( "/FVOMUS " ) 78 #define PARAM_ADMIN TEXT( "/A " ) 79 #define PARAM_TRANSFORM TEXT( " TRANSFORMS=" ) 80 #define PARAM_REBOOT TEXT( " REBOOT=Force" ) 81 #define PARAM_PATCH TEXT( " /update " ) 82 #define PARAM_REG_ALL_MSO_TYPES TEXT( "REGISTER_ALL_MSO_TYPES=1 " ) 83 #define PARAM_REG_NO_MSO_TYPES TEXT( "REGISTER_NO_MSO_TYPES=1 " ) 84 // The VC v14 redistributable is a Burn bundle, not the old MSI-style installer the 85 // VC++ 2008 package was. It does not understand /Q; its silent switches are these. 86 // /norestart matters -- without it the bundle may reboot the machine mid-install. 87 #define PARAM_SILENTINSTALL TEXT( " /install /quiet /norestart" ) 88 89 // Burn exit codes that mean "the runtime is now present". 1638 is 90 // ERROR_PRODUCT_VERSION: a NEWER runtime is already installed, which is success for 91 // our purposes -- it is also the common case on any developer machine. 92 #define RUNTIME_INSTALL_OK 0L 93 #define RUNTIME_INSTALL_NEWER 1638L 94 #define RUNTIME_INSTALL_REBOOT_REQ 3010L 95 96 #define PARAM_RUNNING TEXT( "ignore_running" ) 97 #define CMDLN_REG_ALL_MSO_TYPES TEXT( "msoreg=1" ) 98 #define CMDLN_REG_NO_MSO_TYPES TEXT( "msoreg=0" ) 99 100 #define ADVAPI32_DLL TEXT( "advapi32.dll" ) 101 #define PROFILE_NAME TEXT( "setup.ini" ) 102 103 #define RUNTIME_X64_NAME TEXT( "redist\\vcredist_x64.exe" ) 104 #define RUNTIME_X86_NAME TEXT( "redist\\vcredist_x86.exe" ) 105 106 // There is deliberately no ProductCode here any more. The old code gated on 107 // MsiQueryProductState() against a hardcoded VC++ 2008 GUID, which cannot work for the 108 // v14 runtime: Microsoft reissues that redistributable with a NEW ProductCode on every 109 // servicing revision, so an exact-GUID test misreports every machine that has anything 110 // other than the one pinned build. We test for the runtime functionally instead -- 111 // see RuntimeAlreadyPresent(). 112 113 #define ADVAPI32API_CheckTokenMembership "CheckTokenMembership" 114 115 typedef BOOL (WINAPI* PFnCheckTokenMembership)(HANDLE TokenHandle, PSID SidToCheck, PBOOL IsMember); 116 117 #ifdef DEBUG 118 inline void OutputDebugStringFormat( LPCTSTR pFormat, ... ) 119 { 120 TCHAR buffer[1024]; 121 va_list args; 122 123 va_start( args, pFormat ); 124 StringCchVPrintf( buffer, sizeof(buffer), pFormat, args ); 125 OutputDebugString( buffer ); 126 } 127 #else 128 static inline void OutputDebugStringFormat( LPCTSTR, ... ) 129 { 130 } 131 #endif 132 133 //-------------------------------------------------------------------------- 134 135 const TCHAR sMsiExe[] = TEXT( "\\msiexec.exe" ); 136 const TCHAR sDelayReboot[] = TEXT( " /c:\"msiinst /delayreboot\"" ); 137 const TCHAR sMsiQuiet[] = TEXT( " /q" ); 138 const TCHAR sMemMapName[] = TEXT( "Global\\MsiErrorObject" ); 139 140 //-------------------------------------------------------------------------- 141 SetupApp::SetupApp() 142 { 143 m_uiRet = ERROR_SUCCESS; 144 145 // Get OS version 146 OSVERSIONINFO sInfoOS; 147 148 ZeroMemory( &sInfoOS, sizeof(OSVERSIONINFO) ); 149 sInfoOS.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); 150 151 GetVersionEx( &sInfoOS ); 152 153 m_nOSVersion = sInfoOS.dwMajorVersion; 154 m_nMinorVersion = sInfoOS.dwMinorVersion; 155 m_bIsWin9x = ( VER_PLATFORM_WIN32_NT != sInfoOS.dwPlatformId ); 156 m_bNeedReboot = false; 157 m_bAdministrative = false; 158 159 m_hInst = NULL; 160 m_hMapFile = NULL; 161 m_pAppTitle = NULL; 162 m_pCmdLine = NULL; 163 164 m_pDatabase = NULL; 165 m_pReqVersion = NULL; 166 m_pProductName = NULL; 167 m_pAdvertise = NULL; 168 m_pTmpName = NULL; 169 m_pLogFile = NULL; 170 m_pModuleFile = NULL; 171 m_pPatchFiles = NULL; 172 m_pMSIErrorCode = NULL; 173 m_pUpgradeKey = NULL; 174 m_pProductVersion = NULL; 175 176 m_pErrorText = new TCHAR[ MAX_TEXT_LENGTH ]; 177 m_pErrorText[0] = '\0'; 178 179 m_nLanguageID = 0; 180 m_nLanguageCount = 0; 181 m_ppLanguageList = NULL; 182 183 m_bQuiet = false; 184 m_bRegNoMsoTypes = false; 185 m_bRegAllMsoTypes = false; 186 m_bIsMinorUpgrade = false; 187 m_bSupportsPatch = false; 188 189 m_bIgnoreAlreadyRunning = false; 190 } 191 192 //-------------------------------------------------------------------------- 193 SetupApp::~SetupApp() 194 { 195 if ( m_ppLanguageList ) 196 { 197 for ( int i = 0; i < m_nLanguageCount; i++ ) 198 if ( m_ppLanguageList[i] ) 199 delete m_ppLanguageList[ i ]; 200 delete [] m_ppLanguageList; 201 } 202 203 time_t aTime; 204 time( &aTime ); 205 tm *pTime = localtime( &aTime ); // Convert time to struct tm form 206 207 Log( TEXT( "End: %s\n\r\n\r\n" ), _tasctime( pTime ) ); 208 209 if ( m_pLogFile ) fclose( m_pLogFile ); 210 211 if ( m_pTmpName ) 212 { 213 _tremove( m_pTmpName ); 214 free( m_pTmpName ); 215 } 216 217 if ( m_pMSIErrorCode ) UnmapViewOfFile( m_pMSIErrorCode ); 218 if ( m_hMapFile ) CloseHandle( m_hMapFile ); 219 220 if ( m_pAppTitle ) delete [] m_pAppTitle; 221 if ( m_pDatabase ) delete [] m_pDatabase; 222 if ( m_pReqVersion ) delete [] m_pReqVersion; 223 if ( m_pProductName ) delete [] m_pProductName; 224 if ( m_pAdvertise ) delete [] m_pAdvertise; 225 if ( m_pLogFile ) delete [] m_pLogFile; 226 if ( m_pErrorText ) delete [] m_pErrorText; 227 if ( m_pModuleFile ) delete [] m_pModuleFile; 228 if ( m_pPatchFiles ) delete [] m_pPatchFiles; 229 if ( m_pUpgradeKey ) delete [] m_pUpgradeKey; 230 if ( m_pProductVersion ) delete [] m_pProductVersion; 231 } 232 233 //-------------------------------------------------------------------------- 234 boolean SetupApp::Initialize( HINSTANCE hInst ) 235 { 236 m_pCmdLine = WIN::GetCommandLine(); 237 m_hInst = hInst; 238 239 // Load our AppTitle (caption) 240 m_pAppTitle = new TCHAR[ MAX_STR_CAPTION ]; 241 m_pAppTitle[0] = '\0'; 242 WIN::LoadString( hInst, IDS_APP_TITLE, m_pAppTitle, MAX_STR_CAPTION ); 243 244 // Obtain path we are running from 245 m_pModuleFile = new TCHAR[ MAX_PATH ]; 246 m_pModuleFile[ 0 ] = '\0'; 247 248 if ( 0 == WIN::GetModuleFileName( hInst, m_pModuleFile, MAX_PATH ) ) 249 { 250 SetError( WIN::GetLastError() ); 251 return false; 252 } 253 254 if ( ! GetCmdLineParameters( &m_pCmdLine ) ) 255 return false; 256 257 m_hMapFile = CreateFileMapping( 258 INVALID_HANDLE_VALUE, // use paging file 259 NULL, // default security 260 PAGE_READWRITE, // read/write access 261 0, // max. object size 262 sizeof( int ), // buffer size 263 sMemMapName ); 264 if ( m_hMapFile ) 265 { 266 m_pMSIErrorCode = (int*) MapViewOfFile( m_hMapFile, // handle to map object 267 FILE_MAP_ALL_ACCESS, // read/write permission 268 0, 269 0, 270 sizeof( int ) ); 271 if ( m_pMSIErrorCode ) 272 *m_pMSIErrorCode = 0; 273 else 274 OutputDebugStringFormat( TEXT("Could not map view of file (%d).\n"), GetLastError() ); 275 } 276 else 277 OutputDebugStringFormat( TEXT("Could not create file mapping object (%d).\n"), GetLastError() ); 278 279 Log( TEXT("Starting: %s\r\n"), m_pModuleFile ); 280 Log( TEXT(" CommandLine=<%s>\r\n"), m_pCmdLine ); 281 282 if ( m_bQuiet ) 283 Log( TEXT(" Using quiet install mode\r\n") ); 284 285 time_t aTime; 286 time( &aTime ); 287 tm* pTime = localtime( &aTime ); 288 Log( TEXT(" Begin: %s\n"), _tasctime( pTime ) ); 289 290 return true; 291 } 292 293 //-------------------------------------------------------------------------- 294 boolean SetupApp::GetProfileSection( LPCTSTR pFileName, LPCTSTR pSection, 295 DWORD& rSize, LPTSTR *pRetBuf ) 296 { 297 if ( !rSize || !*pRetBuf ) 298 { 299 rSize = 512; 300 *pRetBuf = new TCHAR[ rSize ]; 301 } 302 303 DWORD nRet = GetPrivateProfileSection( pSection, *pRetBuf, rSize, pFileName ); 304 305 if ( nRet && ( nRet + 2 > rSize ) ) // buffer was too small, retry with bigger one 306 { 307 if ( nRet < 32767 - 2 ) 308 { 309 delete [] (*pRetBuf); 310 rSize = nRet + 2; 311 *pRetBuf = new TCHAR[ rSize ]; 312 313 nRet = GetPrivateProfileSection( pSection, *pRetBuf, rSize, pFileName ); 314 } 315 } 316 317 if ( !nRet ) 318 { 319 SetError( WIN::GetLastError() ); 320 321 TCHAR sBuf[80]; 322 StringCchPrintf( sBuf, 80, TEXT("ERROR: GetPrivateProfileSection(): GetLastError returned %u\r\n"), GetError() ); 323 Log( sBuf ); 324 return false; 325 } 326 else if ( nRet + 2 > rSize ) 327 { 328 SetError( ERROR_OUTOFMEMORY ); 329 Log( TEXT( "ERROR: GetPrivateProfileSection() out of memory\r\n" ) ); 330 return false; 331 } 332 333 Log( TEXT( " GetProfileSection read %s\r\n" ), pSection ); 334 335 return true; 336 } 337 338 //-------------------------------------------------------------------------- 339 boolean SetupApp::ReadProfile() 340 { 341 boolean bRet = false; 342 TCHAR *sProfilePath = 0; 343 344 if ( GetPathToFile( PROFILE_NAME, &sProfilePath ) ) 345 { 346 DWORD nSize = 0; 347 LPTSTR pRetBuf = NULL; 348 349 Log( TEXT( " Open ini file: <%s>\r\n" ), sProfilePath ); 350 351 bRet = GetProfileSection( sProfilePath, SECTION_SETUP, nSize, &pRetBuf ); 352 353 if ( !bRet ) 354 { 355 LPTSTR pTmpFile = CopyIniFile( sProfilePath ); 356 delete [] sProfilePath; 357 sProfilePath = pTmpFile; 358 359 if ( sProfilePath ) 360 { 361 SetError( ERROR_SUCCESS ); 362 363 Log( TEXT( " Could not open inifile, copied ini file to: <%s>\r\n" ), sProfilePath ); 364 bRet = GetProfileSection( sProfilePath, SECTION_SETUP, nSize, &pRetBuf ); 365 } 366 } 367 368 if ( bRet ) 369 { 370 LPTSTR pCurLine = pRetBuf; 371 while ( *pCurLine ) 372 { 373 LPTSTR pName = 0; 374 LPTSTR pValue = 0; 375 376 pCurLine += GetNameValue( pCurLine, &pName, &pValue ); 377 378 if ( lstrcmpi( TEXT( "database" ), pName ) == 0 ) 379 { 380 m_pDatabase = pValue; 381 Log( TEXT( " Database = %s\r\n" ), pValue ); 382 } 383 else if ( lstrcmpi( TEXT( "msiversion" ), pName ) == 0 ) 384 { 385 m_pReqVersion = pValue; 386 Log( TEXT( " msiversion = %s\r\n" ), pValue ); 387 } 388 else if ( lstrcmpi( TEXT( "productname" ), pName ) == 0 ) 389 { 390 m_pProductName = pValue; 391 Log( TEXT( " productname = %s\r\n" ), pValue ); 392 m_pAppTitle = SetProdToAppTitle( m_pProductName ); 393 } 394 else if ( lstrcmpi( TEXT( "upgradekey" ), pName ) == 0 ) 395 { 396 m_pUpgradeKey = pValue; 397 Log( TEXT( " upgradekey = %s\r\n" ), pValue ); 398 } 399 else if ( lstrcmpi( TEXT( "productversion" ), pName ) == 0 ) 400 { 401 m_pProductVersion = pValue; 402 Log( TEXT( " productversion = %s\r\n" ), pValue ); 403 } 404 else if ( lstrcmpi( TEXT( "productcode" ), pName ) == 0 ) 405 { 406 delete [] pValue; 407 } 408 else 409 { 410 Log( TEXT( "Warning: unknown entry in profile <%s>\r\n" ), pName ); 411 delete [] pValue; 412 } 413 } 414 } 415 416 if ( bRet && ( !m_pDatabase || !m_pReqVersion || !m_pProductName ) ) 417 { 418 Log( TEXT( "ERROR: incomplete 'Setup' section in profile\r\n" ) ); 419 SetError( ERROR_INVALID_DATA ); 420 bRet = false; 421 } 422 423 if ( bRet ) 424 bRet = GetProfileSection( sProfilePath, SECTION_LANGUAGE, nSize, &pRetBuf ); 425 426 if ( bRet ) 427 { 428 LPTSTR pName = 0; 429 LPTSTR pValue = 0; 430 LPTSTR pCurLine = pRetBuf; 431 LPTSTR pLastChar; 432 int nNext = 0; 433 434 // first line in this section should be the language count 435 nNext = GetNameValue( pCurLine, &pName, &pValue ); 436 if ( lstrcmpi( TEXT( "count" ), pName ) == 0 ) 437 { 438 Log( TEXT( " Languages = %s\r\n" ), pValue ); 439 m_nLanguageCount = _tcstol( pValue, &pLastChar, 10 ); 440 pCurLine += nNext; 441 delete [] pValue; 442 } 443 444 m_ppLanguageList = new LanguageData*[ m_nLanguageCount ]; 445 446 for ( int i=0; i < m_nLanguageCount; i++ ) 447 { 448 if ( !*pCurLine ) 449 { 450 m_nLanguageCount = i; 451 break; 452 } 453 454 pCurLine += GetNameValue( pCurLine, &pName, &pValue ); 455 m_ppLanguageList[ i ] = new LanguageData( pValue ); 456 Log( TEXT( " Language = %s\r\n" ), pValue ); 457 458 if ( m_ppLanguageList[ i ]->m_pTransform ) 459 Log( TEXT( " Transform = %s\r\n" ), m_ppLanguageList[ i ]->m_pTransform ); 460 461 delete [] pValue; 462 } 463 } 464 465 if ( pRetBuf ) 466 delete [] pRetBuf; 467 } 468 469 if ( sProfilePath && ! m_pTmpName ) 470 delete [] sProfilePath; 471 472 return bRet; 473 } 474 475 //-------------------------------------------------------------------------- 476 void SetupApp::AddFileToPatchList( TCHAR* pPath, TCHAR* pFile ) 477 { 478 if ( m_pPatchFiles == NULL ) 479 { 480 m_pPatchFiles = new TCHAR[ MAX_STR_LENGTH ]; 481 StringCchCopy( m_pPatchFiles, MAX_STR_LENGTH, TEXT("\"") ); 482 } 483 else 484 StringCchCat( m_pPatchFiles, MAX_STR_LENGTH, TEXT(";") ); 485 486 StringCchCat( m_pPatchFiles, MAX_STR_LENGTH, pPath ); 487 StringCchCat( m_pPatchFiles, MAX_STR_LENGTH, pFile ); 488 } 489 490 //-------------------------------------------------------------------------- 491 boolean SetupApp::GetPatches() 492 { 493 boolean bRet = true; 494 495 int nPatternLen = lstrlen( m_pModuleFile ) + 7; // 1 for null terminator, 1 for back slash, 5 for extensions 496 TCHAR* pPattern = new TCHAR[ nPatternLen ]; 497 TCHAR* pBaseDir = new TCHAR[ nPatternLen ]; 498 499 // find 'setup.exe' in the path so we can remove it 500 TCHAR *pFilePart = 0; 501 if ( 0 == GetFullPathName( m_pModuleFile, nPatternLen, pPattern, &pFilePart ) ) 502 { 503 SetError( WIN::GetLastError() ); 504 bRet = false; 505 } 506 else 507 { 508 if ( pFilePart ) 509 *pFilePart = '\0'; 510 StringCchCopy( pBaseDir, nPatternLen, pPattern ); 511 StringCchCat( pPattern, nPatternLen, TEXT("*.msp") ); 512 513 WIN32_FIND_DATA aFindFileData; 514 515 HANDLE hFindPatches = FindFirstFile( pPattern, &aFindFileData ); 516 517 if ( hFindPatches != INVALID_HANDLE_VALUE ) 518 { 519 if ( ! IsPatchInstalled( pBaseDir, aFindFileData.cFileName ) ) 520 AddFileToPatchList( pBaseDir, aFindFileData.cFileName ); 521 522 while ( FindNextFile( hFindPatches, &aFindFileData ) ) 523 { 524 if ( ! IsPatchInstalled( pBaseDir, aFindFileData.cFileName ) ) 525 AddFileToPatchList( pBaseDir, aFindFileData.cFileName ); 526 } 527 528 if ( m_pPatchFiles != NULL ) 529 StringCchCat( m_pPatchFiles, MAX_STR_LENGTH, TEXT("\"") ); 530 531 FindClose( hFindPatches ); 532 } 533 } 534 535 delete [] pPattern; 536 delete [] pBaseDir; 537 538 return bRet; 539 } 540 541 //-------------------------------------------------------------------------- 542 boolean SetupApp::GetPathToFile( TCHAR* pFileName, TCHAR** pPath ) 543 { 544 // generate the path to the file = szModuleFile + FileName 545 // note: FileName is a relative path 546 547 boolean bRet = true; 548 549 int nTempPath = lstrlen( m_pModuleFile ) + lstrlen( pFileName ) + 2; // 1 for null terminator, 1 for back slash 550 TCHAR* pTempPath = new TCHAR[ nTempPath ]; 551 552 // find 'setup.exe' in the path so we can remove it 553 TCHAR *pFilePart = 0; 554 if ( 0 == GetFullPathName( m_pModuleFile, nTempPath, pTempPath, &pFilePart ) ) 555 { 556 SetError( WIN::GetLastError() ); 557 bRet = false; 558 } 559 else 560 { 561 if ( pFilePart ) 562 *pFilePart = '\0'; 563 564 StringCchCat( pTempPath, nTempPath, pFileName ); 565 566 int nPath = 2 * nTempPath; 567 *pPath = new TCHAR[ nPath ]; 568 569 // normalize the path 570 int nReturn = GetFullPathName( pTempPath, nPath, *pPath, &pFilePart ); 571 572 if ( nReturn > nPath ) 573 { 574 // try again, with larger buffer 575 delete [] (*pPath); 576 nPath = nReturn; 577 *pPath = new TCHAR[ nPath ]; 578 579 nReturn = GetFullPathName( pTempPath, nPath, *pPath, &pFilePart ); 580 } 581 582 if ( 0 == nReturn ) 583 { 584 // error -- invalid path 585 SetError( WIN::GetLastError() ); 586 bRet = false; 587 } 588 } 589 590 if ( bRet ) // check for the file's existence 591 { 592 DWORD dwFileAttrib = GetFileAttributes( *pPath ); 593 594 if (0xFFFFFFFF == dwFileAttrib) 595 { 596 StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pFileName ); 597 SetError( ERROR_FILE_NOT_FOUND ); 598 bRet = false; 599 } 600 } 601 602 delete [] pTempPath; 603 return bRet; 604 } 605 606 //-------------------------------------------------------------------------- 607 int SetupApp::GetNameValue( TCHAR* pLine, TCHAR** pName, TCHAR** pValue ) 608 { 609 int nRet = lstrlen( pLine ) + 1; 610 *pValue = 0; 611 612 if ( nRet == 1 ) 613 return nRet; 614 615 LPTSTR pChar = pLine; 616 LPTSTR pLast = NULL; 617 618 // Skip leading spaces. 619 while (' ' == *pChar || '\t' == *pChar) 620 pChar = CharNext( pChar ); 621 622 *pName = pChar; 623 624 // look for the end of the name 625 while( *pChar && (' ' != *pChar) && 626 ( '\t' != *pChar ) && ( '=' != *pChar ) ) 627 pChar = CharNext( pChar ); 628 629 if ( ! *pChar ) 630 return nRet; 631 632 pLast = pChar; 633 pChar = CharNext( pChar ); 634 *pLast = '\0'; 635 636 // look for the start of the value 637 while( ( ' ' == *pChar ) || ( '\t' == *pChar ) || 638 ( '=' == *pChar ) ) 639 pChar = CharNext( pChar ); 640 641 int nValueLen = lstrlen( pChar ) + 1; 642 *pValue = new TCHAR[ nValueLen ]; 643 644 if ( *pValue ) 645 StringCchCopy( *pValue, nValueLen, pChar ); 646 647 return nRet; 648 } 649 650 //-------------------------------------------------------------------------- 651 boolean SetupApp::ChooseLanguage( long& rLanguage ) 652 { 653 rLanguage = 0; 654 655 if ( m_bQuiet ) 656 return true; 657 658 // When there are none or only one language, there is nothing 659 // to do here 660 if ( m_nLanguageCount > 1 ) 661 { 662 TCHAR *sString = new TCHAR[ MAX_LANGUAGE_LEN ]; 663 664 LANGID nUserDefLang = GetUserDefaultLangID(); 665 LANGID nSysDefLang = GetSystemDefaultLangID(); 666 667 int nUserPrimary = PRIMARYLANGID( nUserDefLang ); 668 int nSysPrimary = PRIMARYLANGID( nSysDefLang ); 669 670 long nUserIndex = -1; 671 long nUserPrimIndex = -1; 672 long nSystemIndex = -1; 673 long nSystemPrimIndex = -1; 674 long nParamIndex = -1; 675 676 for ( long i=0; i<GetLanguageCount(); i++ ) 677 { 678 long nLanguage = GetLanguageID( i ); 679 int nPrimary = PRIMARYLANGID( nLanguage ); 680 GetLanguageName( nLanguage, sString ); 681 Log( TEXT( " Info: found Language: %s\r\n" ), sString ); 682 683 if ( nLanguage == nUserDefLang ) 684 nUserIndex = i; 685 if ( nPrimary == nUserPrimary ) 686 nUserPrimIndex = i; 687 if ( nLanguage == nSysDefLang ) 688 nSystemIndex = i; 689 if ( nPrimary == nSysPrimary ) 690 nSystemPrimIndex = i; 691 if ( m_nLanguageID && ( nLanguage == m_nLanguageID ) ) 692 nParamIndex = i; 693 } 694 695 if ( m_nLanguageID && ( nParamIndex == -1 ) ) 696 { 697 Log( TEXT( "Warning: Language chosen with parameter -lang not found.\r\n" ) ); 698 } 699 700 if ( nParamIndex != -1 ) 701 { 702 Log( TEXT( "Info: Found language chosen with parameter -lang.\r\n" ) ); 703 rLanguage = GetLanguageID( nParamIndex ); 704 } 705 else if ( nUserIndex != -1 ) 706 { 707 Log( TEXT( "Info: Found user default language.\r\n" ) ); 708 rLanguage = GetLanguageID( nUserIndex ); 709 } 710 else if ( nUserPrimIndex != -1 ) 711 { 712 Log( TEXT( "Info: Found user default primary language.\r\n" ) ); 713 rLanguage = GetLanguageID( nUserPrimIndex ); 714 } 715 else if ( nSystemIndex != -1 ) 716 { 717 Log( TEXT( "Info: Found system default language.\r\n" ) ); 718 rLanguage = GetLanguageID( nSystemIndex ); 719 } 720 else if ( nSystemPrimIndex != -1 ) 721 { 722 Log( TEXT( "Info: Found system default primary language.\r\n" ) ); 723 rLanguage = GetLanguageID( nSystemPrimIndex ); 724 } 725 else 726 { 727 Log( TEXT( "Info: Use default language from ini file.\r\n" ) ); 728 rLanguage = GetLanguageID( 0 ); 729 } 730 delete [] sString; 731 } 732 733 return true; 734 } 735 736 737 //-------------------------------------------------------------------------- 738 LPCTSTR SetupApp::GetPathToMSI() 739 { 740 LPTSTR sMsiPath = NULL; 741 HKEY hInstKey = NULL; 742 TCHAR *sMsiFolder = getInstallerLocation(); 743 DWORD nMsiFolderSize = MAX_PATH + 1; 744 745 if ( sMsiFolder[0] == '\0' ) // use the default location 746 { 747 Log( TEXT( " Could not find path to msiexec.exe in registry" ) ); 748 749 DWORD nRet = WIN::GetSystemDirectory( sMsiFolder, nMsiFolderSize ); 750 if ( nRet > nMsiFolderSize ) 751 { 752 delete [] sMsiFolder; 753 sMsiFolder = new TCHAR[ nRet ]; 754 nMsiFolderSize = nRet; 755 756 nRet = WIN::GetSystemDirectory( sMsiFolder, nMsiFolderSize ); 757 } 758 if ( 0 == nRet ) 759 { 760 sMsiFolder[0] = '\0'; 761 SetError( WIN::GetLastError() ); 762 } 763 nMsiFolderSize = nRet; 764 } 765 766 if ( sMsiFolder[0] != '\0' ) 767 { 768 int nLength = lstrlen( sMsiExe ) + lstrlen( sMsiFolder ) + 1; 769 sMsiPath = new TCHAR[ nLength ]; 770 771 if ( FAILED( StringCchCopy( sMsiPath, nLength, sMsiFolder ) ) || 772 FAILED( StringCchCat( sMsiPath, nLength, sMsiExe ) ) ) 773 { 774 delete [] sMsiPath; 775 sMsiPath = NULL; 776 } 777 } 778 779 if ( ! sMsiPath ) 780 Log( TEXT( "ERROR: Can't build path to msiexec.exe!" ) ); 781 782 return sMsiPath; 783 } 784 785 //-------------------------------------------------------------------------- 786 boolean SetupApp::LaunchInstaller( LPCTSTR pParam ) 787 { 788 LPCTSTR sMsiPath = GetPathToMSI(); 789 790 if ( !sMsiPath ) 791 { 792 Log( TEXT( "ERROR: msiexec not found!" ) ); 793 SetError( ERROR_FILE_NOT_FOUND ); 794 return false; 795 } 796 797 STARTUPINFO aSUI; 798 PROCESS_INFORMATION aPI; 799 800 Log( TEXT( " Will install using <%s>\r\n" ), sMsiPath ); 801 Log( TEXT( " Parameters are: %s\r\n" ), pParam ); 802 803 OutputDebugStringFormat( TEXT( " Will install using <%s>\r\n" ), sMsiPath ); 804 OutputDebugStringFormat( TEXT( " Parameters are: %s\r\n" ), pParam ); 805 806 ZeroMemory( (void*)&aPI, sizeof( PROCESS_INFORMATION ) ); 807 ZeroMemory( (void*)&aSUI, sizeof( STARTUPINFO ) ); 808 809 aSUI.cb = sizeof(STARTUPINFO); 810 aSUI.dwFlags = STARTF_USESHOWWINDOW; 811 aSUI.wShowWindow = SW_SHOW; 812 813 DWORD nCmdLineLength = lstrlen( sMsiPath ) + lstrlen( pParam ) + 2; 814 TCHAR *sCmdLine = new TCHAR[ nCmdLineLength ]; 815 816 if ( FAILED( StringCchCopy( sCmdLine, nCmdLineLength, sMsiPath ) ) || 817 FAILED( StringCchCat( sCmdLine, nCmdLineLength, TEXT( " " ) ) ) || 818 FAILED( StringCchCat( sCmdLine, nCmdLineLength, pParam ) ) ) 819 { 820 delete [] sCmdLine; 821 SetError( ERROR_INSTALL_FAILURE ); 822 return false; 823 } 824 825 if ( !WIN::CreateProcess( NULL, sCmdLine, NULL, NULL, FALSE, 826 CREATE_DEFAULT_ERROR_MODE, NULL, NULL, 827 &aSUI, &aPI ) ) 828 { 829 Log( TEXT( "ERROR: Could not create process %s.\r\n" ), sCmdLine ); 830 SetError( WIN::GetLastError() ); 831 delete [] sCmdLine; 832 return false; 833 } 834 835 DWORD nResult = WaitForProcess( aPI.hProcess ); 836 bool bRet = true; 837 838 if( ERROR_SUCCESS != nResult ) 839 { 840 Log( TEXT( "ERROR: While waiting for %s.\r\n" ), sCmdLine ); 841 SetError( nResult ); 842 bRet = false; 843 } 844 else 845 { 846 GetExitCodeProcess( aPI.hProcess, &nResult ); 847 SetError( nResult ); 848 849 if ( nResult != ERROR_SUCCESS ) 850 { 851 TCHAR sBuf[80]; 852 StringCchPrintf( sBuf, 80, TEXT("Warning: msiexec returned %u.\r\n"), nResult ); 853 Log( sBuf ); 854 } 855 else 856 Log( TEXT( " Installation completed successfully.\r\n" ) ); 857 } 858 859 CloseHandle( aPI.hProcess ); 860 861 delete [] sCmdLine; 862 863 return bRet; 864 } 865 866 //-------------------------------------------------------------------------- 867 boolean SetupApp::Install( long nLanguage ) 868 { 869 LPTSTR pTransform = NULL; 870 871 if ( nLanguage ) // look for transformation 872 { 873 for ( int i = 0; i < m_nLanguageCount; i++ ) 874 { 875 if ( m_ppLanguageList[i]->m_nLanguageID == nLanguage ) 876 { 877 if ( m_ppLanguageList[i]->m_pTransform ) 878 { 879 if ( !GetPathToFile( m_ppLanguageList[i]->m_pTransform, 880 &pTransform ) ) 881 { 882 Log( TEXT( "ERROR: Could not find transform <%s\r\n" ), m_ppLanguageList[i]->m_pTransform ); 883 return false; 884 } 885 } 886 break; 887 } 888 } 889 } 890 891 TCHAR *pDataBasePath = NULL; 892 893 if ( ! GetPathToFile( m_pDatabase, &pDataBasePath ) ) 894 { 895 Log( TEXT( "ERROR: Could not find database <%s\r\n" ), m_pDatabase ); 896 SetError( ERROR_INSTALL_SOURCE_ABSENT ); 897 return false; 898 } 899 900 // we will always use the parameter setup used 901 int nParLen = lstrlen( PARAM_SETUP_USED ); 902 903 if ( m_bRegNoMsoTypes ) 904 nParLen += lstrlen( PARAM_REG_NO_MSO_TYPES ); 905 else if ( m_bRegAllMsoTypes ) 906 nParLen += lstrlen( PARAM_REG_ALL_MSO_TYPES ); 907 908 if ( m_pAdvertise ) 909 nParLen += lstrlen( m_pAdvertise ) + 1; // one for the space 910 else if ( m_bIsMinorUpgrade ) 911 nParLen += lstrlen( PARAM_MINOR_UPGRADE ); 912 else 913 nParLen += lstrlen( PARAM_PACKAGE ); 914 915 nParLen += lstrlen( pDataBasePath ) + 3; // two quotes, one null 916 917 if ( NeedReboot() ) 918 nParLen += lstrlen( PARAM_REBOOT ); 919 920 if ( m_pPatchFiles ) 921 { 922 nParLen += lstrlen( PARAM_PATCH ); 923 nParLen += lstrlen( m_pPatchFiles ); 924 } 925 926 if ( pTransform ) 927 { 928 nParLen += lstrlen( PARAM_TRANSFORM ); 929 nParLen += lstrlen( pTransform ) + 2; // two quotes 930 } 931 932 if ( m_pCmdLine ) 933 nParLen += lstrlen( m_pCmdLine ) + 1; // one for the space; 934 935 TCHAR *pParams = new TCHAR[ nParLen ]; 936 937 StringCchCopy( pParams, nParLen, PARAM_SETUP_USED ); 938 939 if ( m_bRegNoMsoTypes ) 940 StringCchCat( pParams, nParLen, PARAM_REG_NO_MSO_TYPES ); 941 else if ( m_bRegAllMsoTypes ) 942 StringCchCat( pParams, nParLen, PARAM_REG_ALL_MSO_TYPES ); 943 944 if ( m_pAdvertise ) 945 StringCchCat( pParams, nParLen, m_pAdvertise ); 946 else if ( IsAdminInstall() ) 947 StringCchCat( pParams, nParLen, PARAM_ADMIN ); 948 else if ( m_bIsMinorUpgrade ) 949 StringCchCat( pParams, nParLen, PARAM_MINOR_UPGRADE ); 950 else 951 StringCchCat( pParams, nParLen, PARAM_PACKAGE ); 952 953 StringCchCat( pParams, nParLen, TEXT( "\"" ) ); 954 StringCchCat( pParams, nParLen, pDataBasePath ); 955 StringCchCat( pParams, nParLen, TEXT( "\"" ) ); 956 957 if ( NeedReboot() ) 958 StringCchCat( pParams, nParLen, PARAM_REBOOT ); 959 960 if ( m_pPatchFiles ) 961 { 962 StringCchCat( pParams, nParLen, PARAM_PATCH ); 963 StringCchCat( pParams, nParLen, m_pPatchFiles ); 964 } 965 966 if ( pTransform ) 967 { 968 StringCchCat( pParams, nParLen, PARAM_TRANSFORM ); 969 StringCchCat( pParams, nParLen, TEXT( "\"" ) ); 970 StringCchCat( pParams, nParLen, pTransform ); 971 StringCchCat( pParams, nParLen, TEXT( "\"" ) ); 972 } 973 974 if ( m_pCmdLine ) 975 { 976 StringCchCat( pParams, nParLen, TEXT( " " ) ); 977 StringCchCat( pParams, nParLen, m_pCmdLine ); 978 } 979 980 return LaunchInstaller( pParams ); 981 } 982 983 //-------------------------------------------------------------------------- 984 UINT SetupApp::GetError() const 985 { 986 UINT nErr = 0; 987 988 if ( m_pMSIErrorCode ) 989 nErr = (UINT) *m_pMSIErrorCode; 990 991 if ( nErr == 0 ) 992 nErr = m_uiRet; 993 994 if ( nErr != 0 ) 995 OutputDebugStringFormat( TEXT("Setup will return error (%d).\n"), nErr ); 996 return nErr; 997 } 998 999 //-------------------------------------------------------------------------- 1000 void SetupApp::DisplayError( UINT nErr ) const 1001 { 1002 TCHAR sError[ MAX_TEXT_LENGTH ] = {0}; 1003 TCHAR sTmp[ MAX_TEXT_LENGTH ] = {0}; 1004 1005 UINT nMsgType = MB_OK | MB_ICONERROR; 1006 1007 switch ( nErr ) 1008 { 1009 case ERROR_SUCCESS: break; // 0 1010 1011 case ERROR_FILE_NOT_FOUND: // 2 1012 WIN::LoadString( m_hInst, IDS_FILE_NOT_FOUND, sTmp, MAX_TEXT_LENGTH ); 1013 StringCchPrintf( sError, MAX_TEXT_LENGTH, sTmp, m_pErrorText ); 1014 break; 1015 case ERROR_INVALID_DATA: // 13 1016 WIN::LoadString( m_hInst, IDS_INVALID_PROFILE, sError, MAX_TEXT_LENGTH ); 1017 break; 1018 case ERROR_OUTOFMEMORY: WIN::LoadString( m_hInst, IDS_OUTOFMEM, sError, MAX_TEXT_LENGTH ); 1019 break; 1020 case ERROR_INSTALL_USEREXIT: 1021 WIN::LoadString( m_hInst, IDS_USER_CANCELED, sError, MAX_TEXT_LENGTH ); 1022 break; 1023 case ERROR_INSTALL_ALREADY_RUNNING: // 1618 1024 WIN::LoadString( m_hInst, IDS_ALREADY_RUNNING, sError, MAX_TEXT_LENGTH ); 1025 break; 1026 case ERROR_INSTALL_SOURCE_ABSENT: 1027 WIN::LoadString( m_hInst, IDS_NOMSI, sError, MAX_TEXT_LENGTH ); 1028 break; 1029 case ERROR_DS_INSUFF_ACCESS_RIGHTS: // 8344 1030 WIN::LoadString( m_hInst, IDS_REQUIRES_ADMIN_PRIV, sError, MAX_TEXT_LENGTH ); 1031 break; 1032 case E_ABORT: WIN::LoadString( m_hInst, IDS_UNKNOWN_ERROR, sError, MAX_TEXT_LENGTH ); 1033 break; 1034 case ERROR_INVALID_PARAMETER: // 87 1035 WIN::LoadString( m_hInst, IDS_INVALID_PARAM, sTmp, MAX_TEXT_LENGTH ); 1036 StringCchPrintf( sError, MAX_TEXT_LENGTH, sTmp, m_pErrorText ); 1037 break; 1038 1039 case ERROR_SETUP_TO_OLD: // - 3 1040 WIN::LoadString( m_hInst, IDS_SETUP_TO_OLD, sTmp, MAX_TEXT_LENGTH ); 1041 StringCchPrintf( sError, MAX_TEXT_LENGTH, sTmp, m_pReqVersion, m_pErrorText ); 1042 break; 1043 case ERROR_SETUP_NOT_FOUND: // - 4 1044 WIN::LoadString( m_hInst, IDS_SETUP_NOT_FOUND, sTmp, MAX_TEXT_LENGTH ); 1045 StringCchPrintf( sError, MAX_TEXT_LENGTH, sTmp, m_pReqVersion ); 1046 break; 1047 case ERROR_SHOW_USAGE: // - 2 1048 nMsgType = MB_OK | MB_ICONINFORMATION; 1049 WIN::LoadString( m_hInst, IDS_USAGE, sError, MAX_TEXT_LENGTH ); 1050 break; 1051 case ERROR_OS_TO_OLD: // - 5 1052 WIN::LoadString( m_hInst, IDS_OS_TO_OLD, sError, MAX_TEXT_LENGTH ); 1053 break; 1054 case ERROR_RUNTIME_FAILED: // - 6 1055 WIN::LoadString( m_hInst, IDS_RUNTIME_FAILED, sError, MAX_TEXT_LENGTH ); 1056 break; 1057 1058 default: WIN::LoadString( m_hInst, IDS_UNKNOWN_ERROR, sError, MAX_TEXT_LENGTH ); 1059 break; 1060 } 1061 1062 if ( sError[0] ) 1063 { 1064 if ( !m_bQuiet ) 1065 { 1066 ConvertNewline( sError ); 1067 WIN::MessageBox( NULL, sError, m_pAppTitle, nMsgType ); 1068 } 1069 1070 Log( TEXT( "ERROR: %s\r\n" ), sError ); 1071 } 1072 } 1073 1074 //-------------------------------------------------------------------------- 1075 long SetupApp::GetLanguageID( long nIndex ) const 1076 { 1077 if ( nIndex >=0 && nIndex < m_nLanguageCount ) 1078 return m_ppLanguageList[ nIndex ]->m_nLanguageID; 1079 else 1080 return 0; 1081 } 1082 1083 //-------------------------------------------------------------------------- 1084 void SetupApp::GetLanguageName( long nLanguage, LPTSTR sName ) const 1085 { 1086 switch ( nLanguage ) 1087 { 1088 case 1028: WIN::LoadString( m_hInst, IDS_LANGUAGE_ZH_TW, sName, MAX_LANGUAGE_LEN ); break; 1089 case 1029: WIN::LoadString( m_hInst, IDS_LANGUAGE_CS, sName, MAX_LANGUAGE_LEN ); break; 1090 case 1030: WIN::LoadString( m_hInst, IDS_LANGUAGE_DA, sName, MAX_LANGUAGE_LEN ); break; 1091 case 1031: WIN::LoadString( m_hInst, IDS_LANGUAGE_DE_DE, sName, MAX_LANGUAGE_LEN ); break; 1092 case 1032: WIN::LoadString( m_hInst, IDS_LANGUAGE_EL, sName, MAX_LANGUAGE_LEN ); break; 1093 case 1033: WIN::LoadString( m_hInst, IDS_LANGUAGE_EN_US, sName, MAX_LANGUAGE_LEN ); break; 1094 case 1034: WIN::LoadString( m_hInst, IDS_LANGUAGE_ES, sName, MAX_LANGUAGE_LEN ); break; 1095 case 1035: WIN::LoadString( m_hInst, IDS_LANGUAGE_FI, sName, MAX_LANGUAGE_LEN ); break; 1096 case 1036: WIN::LoadString( m_hInst, IDS_LANGUAGE_FR_FR, sName, MAX_LANGUAGE_LEN ); break; 1097 case 1037: WIN::LoadString( m_hInst, IDS_LANGUAGE_HE, sName, MAX_LANGUAGE_LEN ); break; 1098 case 1038: WIN::LoadString( m_hInst, IDS_LANGUAGE_HU, sName, MAX_LANGUAGE_LEN ); break; 1099 case 1040: WIN::LoadString( m_hInst, IDS_LANGUAGE_IT_IT, sName, MAX_LANGUAGE_LEN ); break; 1100 case 1041: WIN::LoadString( m_hInst, IDS_LANGUAGE_JA, sName, MAX_LANGUAGE_LEN ); break; 1101 case 1042: WIN::LoadString( m_hInst, IDS_LANGUAGE_KO, sName, MAX_LANGUAGE_LEN ); break; 1102 case 1043: WIN::LoadString( m_hInst, IDS_LANGUAGE_NL_NL, sName, MAX_LANGUAGE_LEN ); break; 1103 case 1044: WIN::LoadString( m_hInst, IDS_LANGUAGE_NO_NO, sName, MAX_LANGUAGE_LEN ); break; 1104 case 1045: WIN::LoadString( m_hInst, IDS_LANGUAGE_PL, sName, MAX_LANGUAGE_LEN ); break; 1105 case 1046: WIN::LoadString( m_hInst, IDS_LANGUAGE_PT_BR, sName, MAX_LANGUAGE_LEN ); break; 1106 case 1049: WIN::LoadString( m_hInst, IDS_LANGUAGE_RU, sName, MAX_LANGUAGE_LEN ); break; 1107 case 1051: WIN::LoadString( m_hInst, IDS_LANGUAGE_SK, sName, MAX_LANGUAGE_LEN ); break; 1108 case 1053: WIN::LoadString( m_hInst, IDS_LANGUAGE_SV_SE, sName, MAX_LANGUAGE_LEN ); break; 1109 case 1054: WIN::LoadString( m_hInst, IDS_LANGUAGE_TH, sName, MAX_LANGUAGE_LEN ); break; 1110 case 1055: WIN::LoadString( m_hInst, IDS_LANGUAGE_TR, sName, MAX_LANGUAGE_LEN ); break; 1111 case 1061: WIN::LoadString( m_hInst, IDS_LANGUAGE_ET, sName, MAX_LANGUAGE_LEN ); break; 1112 case 2052: WIN::LoadString( m_hInst, IDS_LANGUAGE_ZH_CN, sName, MAX_LANGUAGE_LEN ); break; 1113 case 2070: WIN::LoadString( m_hInst, IDS_LANGUAGE_PT_PT, sName, MAX_LANGUAGE_LEN ); break; 1114 1115 default: 1116 { 1117 TCHAR sTmp[ MAX_LANGUAGE_LEN ] = {0}; 1118 1119 WIN::LoadString( m_hInst, IDS_UNKNOWN_LANG, sTmp, MAX_LANGUAGE_LEN ); 1120 StringCchPrintf( sName, MAX_LANGUAGE_LEN, sTmp, nLanguage ); 1121 } 1122 } 1123 } 1124 1125 //-------------------------------------------------------------------------- 1126 boolean SetupApp::CheckVersion() 1127 { 1128 boolean bRet = false; 1129 1130 Log( TEXT( " Looking for installed MSI with version >= %s\r\n" ), m_pReqVersion ); 1131 1132 DLLVERSIONINFO aInfo; 1133 1134 aInfo.cbSize = sizeof( DLLVERSIONINFO ); 1135 if ( NOERROR == aoo_MsiDllGetVersion( &aInfo ) ) 1136 { 1137 TCHAR pMsiVersion[ VERSION_SIZE ]; 1138 StringCchPrintf( pMsiVersion, VERSION_SIZE, TEXT("%d.%d.%4d"), 1139 aInfo.dwMajorVersion, 1140 aInfo.dwMinorVersion, 1141 aInfo.dwBuildNumber ); 1142 if ( _tcsncmp( pMsiVersion, m_pReqVersion, _tcslen( pMsiVersion ) ) < 0 ) 1143 { 1144 StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pMsiVersion ); 1145 SetError( (UINT) ERROR_SETUP_TO_OLD ); 1146 Log( TEXT( "Warning: Old MSI version found <%s>, update needed!\r\n" ), pMsiVersion ); 1147 } 1148 else 1149 { 1150 Log( TEXT( " Found MSI version <%s>, no update needed\r\n" ), pMsiVersion ); 1151 bRet = true; 1152 } 1153 if ( aInfo.dwMajorVersion >= 3 ) 1154 m_bSupportsPatch = true; 1155 else 1156 Log( TEXT("Warning: Patching not supported! MSI-Version <%s>\r\n"), pMsiVersion ); 1157 } 1158 1159 return bRet; 1160 } 1161 1162 //-------------------------------------------------------------------------- 1163 boolean SetupApp::CheckForUpgrade() 1164 { 1165 // When we have patch files we will never try an Minor upgrade 1166 if ( m_pPatchFiles ) return true; 1167 1168 if ( !m_pUpgradeKey || ( _tcslen( m_pUpgradeKey ) == 0 ) ) 1169 { 1170 Log( TEXT( " No Upgrade Key Found -> continue with standard installation!\r\n" ) ); 1171 return true; 1172 } 1173 1174 HKEY hInstKey = NULL; 1175 1176 if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, m_pUpgradeKey, 0, KEY_READ, &hInstKey ) ) 1177 { 1178 Log( TEXT( " Found Upgrade Key in Registry (HKLM) -> will try minor upgrade!\r\n" ) ); 1179 m_bIsMinorUpgrade = true; 1180 } 1181 else if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_CURRENT_USER, m_pUpgradeKey, 0, KEY_READ, &hInstKey ) ) 1182 { 1183 Log( TEXT( " Found Upgrade Key in Registry (HKCU) -> will try minor upgrade!\r\n" ) ); 1184 m_bIsMinorUpgrade = true; 1185 } 1186 else 1187 { 1188 Log( TEXT( " Didn't Find Upgrade Key in Registry -> continue with standard installation!\r\n" ) ); 1189 return true; 1190 } 1191 1192 if ( m_pProductVersion && ( _tcslen( m_pProductVersion ) > 0 ) ) 1193 { 1194 TCHAR *sProductVersion = new TCHAR[ MAX_PATH + 1 ]; 1195 DWORD nSize = MAX_PATH + 1; 1196 1197 sProductVersion[0] = '\0'; 1198 1199 // get product version 1200 if ( ERROR_SUCCESS == RegQueryValueEx( hInstKey, PRODUCT_VERSION, NULL, NULL, (LPBYTE)sProductVersion, &nSize ) ) 1201 { 1202 if ( lstrcmpi( sProductVersion, m_pProductVersion ) == 0 ) 1203 { 1204 Log( TEXT( " Same Product Version already installed, no minor upgrade!\r\n" ) ); 1205 m_bIsMinorUpgrade = false; 1206 } 1207 } 1208 1209 delete [] sProductVersion; 1210 } 1211 1212 return true; 1213 } 1214 1215 //-------------------------------------------------------------------------- 1216 boolean SetupApp::IsTerminalServerInstalled() const 1217 { 1218 boolean bIsTerminalServer = false; 1219 1220 const TCHAR sSearchStr[] = TEXT("Terminal Server"); 1221 const TCHAR sKey[] = TEXT("System\\CurrentControlSet\\Control\\ProductOptions"); 1222 const TCHAR sValue[] = TEXT("ProductSuite"); 1223 1224 DWORD dwSize = 0; 1225 HKEY hKey = 0; 1226 DWORD dwType = 0; 1227 1228 if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, sKey, 0, KEY_READ, &hKey ) && 1229 ERROR_SUCCESS == RegQueryValueEx( hKey, sValue, NULL, &dwType, NULL, &dwSize ) && 1230 dwSize > 0 && 1231 REG_MULTI_SZ == dwType ) 1232 { 1233 TCHAR* sSuiteList = new TCHAR[ (dwSize*sizeof(byte)/sizeof(TCHAR)) + 1 ]; 1234 1235 ZeroMemory(sSuiteList, dwSize); 1236 1237 if ( ERROR_SUCCESS == RegQueryValueEx( hKey, sValue, NULL, &dwType, (LPBYTE)sSuiteList, &dwSize) ) 1238 { 1239 DWORD nMulti = 0; 1240 DWORD nSrch = lstrlen( sSearchStr ); 1241 const TCHAR *sSubString = sSuiteList; 1242 1243 while (*sSubString) 1244 { 1245 nMulti = lstrlen( sSubString ); 1246 if ( nMulti == nSrch && 0 == lstrcmp( sSearchStr, sSubString ) ) 1247 { 1248 bIsTerminalServer = true; 1249 break; 1250 } 1251 1252 sSubString += (nMulti + 1); 1253 } 1254 } 1255 delete [] sSuiteList; 1256 } 1257 1258 if ( hKey ) 1259 RegCloseKey( hKey ); 1260 1261 return bIsTerminalServer; 1262 } 1263 1264 //-------------------------------------------------------------------------- 1265 boolean SetupApp::AlreadyRunning() const 1266 { 1267 if ( m_bIgnoreAlreadyRunning ) 1268 { 1269 Log( TEXT("Ignoring already running MSI instance!\r\n") ); 1270 return false; 1271 } 1272 1273 const TCHAR *sMutexName = NULL; 1274 const TCHAR sGUniqueName[] = TEXT( "Global\\_MSISETUP_{EA8130C1-8D3D-4338-9309-1A52D530D846}" ); 1275 const TCHAR sUniqueName[] = TEXT( "_MSISETUP_{EA8130C1-8D3D-4338-9309-1A52D530D846}" ); 1276 1277 if ( IsWin9x() ) 1278 sMutexName = sUniqueName; 1279 else if ( ( GetOSVersion() < 5 ) && ! IsTerminalServerInstalled() ) 1280 sMutexName = sUniqueName; 1281 else 1282 sMutexName = sGUniqueName; 1283 1284 HANDLE hMutex = 0; 1285 1286 hMutex = WIN::CreateMutex( NULL, FALSE, sMutexName ); 1287 1288 if ( !hMutex || ERROR_ALREADY_EXISTS == WIN::GetLastError() ) 1289 { 1290 if ( !hMutex ) 1291 Log( TEXT( "ERROR: AlreadyRunning() could not create mutex!\r\n" ) ); 1292 else 1293 Log( TEXT( "ERROR: There's already a setup running!\r\n" ) ); 1294 1295 return true; 1296 } 1297 Log( TEXT( " No running Setup found\r\n" ) ); 1298 1299 return false; 1300 } 1301 1302 //-------------------------------------------------------------------------- 1303 DWORD SetupApp::WaitForProcess( HANDLE hHandle ) 1304 { 1305 DWORD nResult = NOERROR; 1306 boolean bLoop = true; 1307 1308 MSG aMsg; 1309 ZeroMemory( (void*) &aMsg, sizeof(MSG) ); 1310 1311 while ( bLoop ) 1312 { 1313 switch ( WIN::MsgWaitForMultipleObjects( 1, &hHandle, false, 1314 INFINITE, QS_ALLINPUT ) ) 1315 { 1316 case WAIT_OBJECT_0: bLoop = false; 1317 break; 1318 1319 case (WAIT_OBJECT_0 + 1): 1320 { 1321 if ( WIN::PeekMessage( &aMsg, NULL, NULL, NULL, PM_REMOVE ) ) 1322 { 1323 WIN::TranslateMessage( &aMsg ); 1324 WIN::DispatchMessage( &aMsg ); 1325 } 1326 break; 1327 } 1328 1329 default: 1330 { 1331 nResult = WIN::GetLastError(); 1332 bLoop = false; 1333 } 1334 } 1335 } 1336 1337 return nResult; 1338 } 1339 1340 //-------------------------------------------------------------------------- 1341 void SetupApp::Log( LPCTSTR pMessage, LPCTSTR pText ) const 1342 { 1343 if ( m_pLogFile ) 1344 { 1345 static boolean bInit = false; 1346 1347 if ( !bInit ) 1348 { 1349 bInit = true; 1350 if ( ! IsWin9x() ) 1351 _ftprintf( m_pLogFile, TEXT("%c"), 0xfeff ); 1352 1353 _tsetlocale( LC_ALL, TEXT("") ); 1354 _ftprintf( m_pLogFile, TEXT("\nCodepage=%s\nMultiByte Codepage=[%d]\n"), 1355 _tsetlocale( LC_ALL, NULL ), _getmbcp() ); 1356 } 1357 if ( pText ) 1358 { 1359 _ftprintf( m_pLogFile, pMessage, pText ); 1360 OutputDebugStringFormat( pMessage, pText ); 1361 } 1362 else 1363 { 1364 _ftprintf( m_pLogFile, pMessage ); 1365 OutputDebugStringFormat( pMessage ); 1366 } 1367 1368 fflush( m_pLogFile ); 1369 } 1370 } 1371 1372 //-------------------------------------------------------------------------- 1373 DWORD SetupApp::GetNextArgument( LPCTSTR pStr, LPTSTR *pArg, LPTSTR *pNext, 1374 boolean bStripQuotes ) 1375 { 1376 boolean bInQuotes = false; 1377 boolean bFoundArgEnd = false; 1378 LPCTSTR pChar = pStr; 1379 LPCTSTR pFirst = NULL; 1380 1381 if ( NULL == pChar ) 1382 return ERROR_NO_MORE_ITEMS; 1383 1384 while ( ' ' == (*pChar) || '\t' == (*pChar) ) 1385 pChar = CharNext( pChar ); 1386 1387 if ( '\0' == (*pChar) ) 1388 return ERROR_NO_MORE_ITEMS; 1389 1390 int nCount = 1; 1391 pFirst = pChar; 1392 1393 while ( ! bFoundArgEnd ) 1394 { 1395 if ( '\0' == (*pChar) ) 1396 bFoundArgEnd = true; 1397 else if ( !bInQuotes && ' ' == (*pChar) ) 1398 bFoundArgEnd = true; 1399 else if ( !bInQuotes && '\t' == (*pChar) ) 1400 bFoundArgEnd = true; 1401 else 1402 { 1403 if ( '\"' == (*pChar) ) 1404 { 1405 bInQuotes = !bInQuotes; 1406 if ( bStripQuotes ) 1407 { 1408 if ( pChar == pFirst ) 1409 pFirst = CharNext( pFirst ); 1410 nCount -= 1; 1411 } 1412 } 1413 1414 pChar = CharNext( pChar ); 1415 nCount += 1; 1416 } 1417 } 1418 1419 if ( pArg ) 1420 { 1421 *pArg = new TCHAR[ nCount ]; 1422 StringCchCopyN ( *pArg, nCount, pFirst, nCount-1 ); 1423 } 1424 1425 if ( pNext ) 1426 *pNext = CharNext( pChar ); 1427 1428 return ERROR_SUCCESS; 1429 } 1430 1431 //-------------------------------------------------------------------------- 1432 boolean SetupApp::GetCmdLineParameters( LPTSTR *pCmdLine ) 1433 { 1434 int nRet = ERROR_SUCCESS; 1435 LPTSTR pStart = NULL; 1436 LPTSTR pNext = NULL; 1437 1438 if ( GetNextArgument( *pCmdLine, NULL, &pNext ) != ERROR_SUCCESS ) 1439 { 1440 SetError( ERROR_NO_MORE_ITEMS ); 1441 return false; 1442 } 1443 1444 int nSize = lstrlen( *pCmdLine ) + 2; 1445 TCHAR *pNewCmdLine = new TCHAR[ nSize ]; 1446 pNewCmdLine[0] = '\0'; 1447 1448 while ( GetNextArgument( pNext, &pStart, &pNext ) == ERROR_SUCCESS ) 1449 { 1450 boolean bDeleteStart = true; 1451 1452 if ( (*pStart) == '/' || (*pStart) == '-' ) 1453 { 1454 LPTSTR pSub = CharNext( pStart ); 1455 if ( (*pSub) == 'l' || (*pSub) == 'L' ) 1456 { 1457 pSub = CharNext( pSub ); 1458 if ( (*pSub) == 'a' || (*pSub) == 'A' ) 1459 { // --- handle the lang parameter --- 1460 LPTSTR pLanguage = NULL; 1461 LPTSTR pLastChar; 1462 if ( GetNextArgument( pNext, &pLanguage, &pNext, true ) != ERROR_SUCCESS ) 1463 { 1464 StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pStart ); 1465 nRet = ERROR_INVALID_PARAMETER; 1466 break; 1467 } 1468 1469 m_nLanguageID = _tcstol( pLanguage, &pLastChar, 10 ); 1470 delete [] pLanguage; 1471 } 1472 else 1473 { // --- handle the l(og) parameter --- 1474 boolean bAppend = false; 1475 LPTSTR pFileName = NULL; 1476 1477 while ( *pSub ) 1478 { 1479 if ( *pSub == '+' ) 1480 { 1481 bAppend = true; 1482 break; 1483 } 1484 pSub = CharNext( pSub ); 1485 } 1486 1487 if ( GetNextArgument( pNext, &pFileName, &pNext, true ) != ERROR_SUCCESS ) 1488 { 1489 StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pStart ); 1490 nRet = ERROR_INVALID_PARAMETER; 1491 break; 1492 } 1493 1494 if ( FAILED( StringCchCat( pNewCmdLine, nSize, pStart ) ) ) 1495 { 1496 nRet = ERROR_OUTOFMEMORY; 1497 break; 1498 } 1499 // we need to append a '+' otherwise msiexec would overwrite our log file 1500 if ( !bAppend && FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( "+" ) ) ) ) 1501 { 1502 nRet = ERROR_OUTOFMEMORY; 1503 break; 1504 } 1505 if ( FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( " \"" ) ) ) || 1506 FAILED( StringCchCat( pNewCmdLine, nSize, pFileName ) ) || 1507 FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( "\" " ) ) ) ) 1508 { 1509 nRet = ERROR_OUTOFMEMORY; 1510 break; 1511 } 1512 1513 if ( bAppend ) 1514 m_pLogFile = _tfopen( pFileName, TEXT( "ab" ) ); 1515 else 1516 m_pLogFile = _tfopen( pFileName, TEXT( "wb" ) ); 1517 1518 delete [] pFileName; 1519 } 1520 } 1521 else if ( (*pSub) == 'q' || (*pSub) == 'Q' ) 1522 { // --- Handle quiet file parameter --- 1523 pSub = CharNext( pSub ); 1524 if ( ! (*pSub) || (*pSub) == 'n' || (*pSub) == 'N' ) 1525 m_bQuiet = true; 1526 1527 if ( FAILED( StringCchCat( pNewCmdLine, nSize, pStart ) ) || 1528 FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( " " ) ) ) ) 1529 { 1530 nRet = ERROR_OUTOFMEMORY; 1531 break; 1532 } 1533 } 1534 else if ( _tcsnicmp( pSub, PARAM_RUNNING, _tcslen( PARAM_RUNNING ) ) == 0 ) 1535 { 1536 m_bIgnoreAlreadyRunning = true; 1537 } 1538 else if ( _tcsnicmp( pSub, CMDLN_REG_ALL_MSO_TYPES, _tcslen( CMDLN_REG_ALL_MSO_TYPES ) ) == 0 ) 1539 { 1540 m_bRegAllMsoTypes = true; 1541 } 1542 else if ( _tcsnicmp( pSub, CMDLN_REG_NO_MSO_TYPES, _tcslen( CMDLN_REG_NO_MSO_TYPES ) ) == 0 ) 1543 { 1544 m_bRegNoMsoTypes = true; 1545 } 1546 else if ( (*pSub) == 'i' || (*pSub) == 'I' || (*pSub) == 'f' || (*pSub) == 'F' || 1547 (*pSub) == 'p' || (*pSub) == 'P' || (*pSub) == 'x' || (*pSub) == 'X' || 1548 (*pSub) == 'y' || (*pSub) == 'Y' || (*pSub) == 'z' || (*pSub) == 'Z' ) 1549 { 1550 StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pStart ); 1551 nRet = ERROR_INVALID_PARAMETER; 1552 break; 1553 } 1554 else if ( (*pSub) == 'a' || (*pSub) == 'A' ) 1555 { // --- Handle Administrative Installation --- 1556 SetAdminInstall( true ); 1557 } 1558 else if ( (*pSub) == 'j' || (*pSub) == 'J' ) 1559 { // --- Handle Administrative Installation --- 1560 m_pAdvertise = pStart; 1561 m_bQuiet = true; 1562 bDeleteStart = false; 1563 } 1564 else if ( (*pSub) == '?' || (*pSub) == 'h' || (*pSub) == 'H' ) 1565 { // --- Handle Show Usage --- 1566 nRet = ERROR_SHOW_USAGE; 1567 break; 1568 } 1569 else 1570 { 1571 if ( FAILED( StringCchCat( pNewCmdLine, nSize, pStart ) ) || 1572 FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( " " ) ) ) ) 1573 { 1574 nRet = ERROR_OUTOFMEMORY; 1575 break; 1576 } 1577 } 1578 } 1579 else 1580 { 1581 if ( FAILED( StringCchCat( pNewCmdLine, nSize, pStart ) ) || 1582 FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( " " ) ) ) ) 1583 { 1584 nRet = ERROR_OUTOFMEMORY; 1585 break; 1586 } 1587 } 1588 1589 if ( bDeleteStart ) delete [] pStart; 1590 pStart = NULL; 1591 } 1592 1593 if ( pStart ) delete [] pStart; 1594 1595 *pCmdLine = pNewCmdLine; 1596 1597 if ( nRet != ERROR_SUCCESS ) 1598 { 1599 SetError( nRet ); 1600 return false; 1601 } 1602 else 1603 return true; 1604 } 1605 1606 //-------------------------------------------------------------------------- 1607 boolean SetupApp::IsAdmin() 1608 { 1609 if ( IsWin9x() ) 1610 return true; 1611 1612 PSID aPsidAdmin; 1613 SID_IDENTIFIER_AUTHORITY aAuthority = SECURITY_NT_AUTHORITY; 1614 1615 if ( !AllocateAndInitializeSid( &aAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, 1616 DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, 1617 &aPsidAdmin ) ) 1618 return false; 1619 1620 BOOL bIsAdmin = FALSE; 1621 1622 if ( GetOSVersion() >= 5 ) 1623 { 1624 HMODULE hAdvapi32 = LoadLibrary( ADVAPI32_DLL ); 1625 1626 if ( !hAdvapi32 ) 1627 bIsAdmin = FALSE; 1628 else 1629 { 1630 PFnCheckTokenMembership pfnCheckTokenMembership = (PFnCheckTokenMembership) GetProcAddress( hAdvapi32, ADVAPI32API_CheckTokenMembership); 1631 if ( !pfnCheckTokenMembership || !pfnCheckTokenMembership( NULL, aPsidAdmin, &bIsAdmin ) ) 1632 bIsAdmin = FALSE; 1633 } 1634 FreeLibrary( hAdvapi32 ); 1635 } 1636 else 1637 { 1638 // NT4, check groups of user 1639 HANDLE hAccessToken = 0; 1640 UCHAR *szInfoBuffer = new UCHAR[ 1024 ]; // may need to resize if TokenInfo too big 1641 DWORD dwInfoBufferSize = 1024; 1642 DWORD dwRetInfoBufferSize = 0; 1643 UINT i=0; 1644 1645 if ( WIN::OpenProcessToken( WIN::GetCurrentProcess(), TOKEN_READ, &hAccessToken ) ) 1646 { 1647 bool bSuccess = false; 1648 bSuccess = WIN::GetTokenInformation( hAccessToken, TokenGroups, 1649 szInfoBuffer, dwInfoBufferSize, 1650 &dwRetInfoBufferSize ) == TRUE; 1651 1652 if( dwRetInfoBufferSize > dwInfoBufferSize ) 1653 { 1654 delete [] szInfoBuffer; 1655 szInfoBuffer = new UCHAR[ dwRetInfoBufferSize ]; 1656 dwInfoBufferSize = dwRetInfoBufferSize; 1657 bSuccess = WIN::GetTokenInformation( hAccessToken, TokenGroups, 1658 szInfoBuffer, dwInfoBufferSize, 1659 &dwRetInfoBufferSize ) == TRUE; 1660 } 1661 1662 WIN::CloseHandle( hAccessToken ); 1663 1664 if ( bSuccess ) 1665 { 1666 PTOKEN_GROUPS pGroups = (PTOKEN_GROUPS)(UCHAR*) szInfoBuffer; 1667 for( i=0; i<pGroups->GroupCount; i++ ) 1668 { 1669 if( WIN::EqualSid( aPsidAdmin, pGroups->Groups[i].Sid ) ) 1670 { 1671 bIsAdmin = TRUE; 1672 break; 1673 } 1674 } 1675 } 1676 1677 delete [] szInfoBuffer; 1678 } 1679 } 1680 1681 WIN::FreeSid( aPsidAdmin ); 1682 1683 return bIsAdmin ? true : false; 1684 } 1685 1686 //-------------------------------------------------------------------------- 1687 LPTSTR SetupApp::CopyIniFile( LPCTSTR pIniFile ) 1688 { 1689 m_pTmpName = _ttempnam( TEXT( "C:\\" ), TEXT( "Setup" ) ); 1690 1691 if ( !m_pTmpName ) 1692 { 1693 Log( TEXT( "ERROR: Could not create temp file\n" ) ); 1694 return NULL; 1695 } 1696 1697 FILE *pOut = _tfopen( m_pTmpName, TEXT( "wb" ) ); 1698 FILE *pIn = _tfopen( pIniFile, TEXT( "rb" ) ); 1699 1700 if ( pOut && pIn ) 1701 { 1702 size_t nRead, nWritten; 1703 BYTE pBuf[1024]; 1704 1705 nRead = fread( pBuf, sizeof( BYTE ), 1024, pIn ); 1706 while ( nRead && !ferror( pIn ) ) 1707 { 1708 nWritten = fwrite( pBuf, sizeof( BYTE ), nRead, pOut ); 1709 if ( nWritten != nRead ) 1710 { 1711 Log( TEXT( "ERROR: Could not write all bytes to temp file\n" ) ); 1712 break; 1713 } 1714 nRead = fread( pBuf, sizeof( BYTE ), 1024, pIn ); 1715 } 1716 } 1717 1718 if ( pOut ) fclose( pOut ); 1719 if ( pIn ) fclose( pIn ); 1720 1721 return m_pTmpName; 1722 } 1723 1724 //-------------------------------------------------------------------------- 1725 void SetupApp::ConvertNewline( LPTSTR pText ) const 1726 { 1727 int i=0; 1728 1729 while ( pText[i] != 0 ) 1730 { 1731 if ( ( pText[i] == '\\' ) && ( pText[i+1] == 'n' ) ) 1732 { 1733 pText[i] = 0x0d; 1734 pText[i+1] = 0x0a; 1735 i+=2; 1736 } 1737 else 1738 i+=1; 1739 } 1740 } 1741 1742 //-------------------------------------------------------------------------- 1743 LPTSTR SetupApp::SetProdToAppTitle( LPCTSTR pProdName ) 1744 { 1745 if ( !pProdName ) return m_pAppTitle; 1746 1747 LPTSTR pAppProdTitle = new TCHAR[ MAX_STR_CAPTION ]; 1748 pAppProdTitle[0] = '\0'; 1749 1750 WIN::LoadString( m_hInst, IDS_APP_PROD_TITLE, pAppProdTitle, MAX_STR_CAPTION ); 1751 1752 int nAppLen = lstrlen( pAppProdTitle ); 1753 int nProdLen = lstrlen( pProdName ); 1754 1755 if ( ( nAppLen == 0 ) || ( nProdLen == 0 ) ) 1756 { 1757 delete [] pAppProdTitle; 1758 return m_pAppTitle; 1759 } 1760 1761 int nLen = nAppLen + nProdLen + 3; 1762 1763 if ( nLen > STRSAFE_MAX_CCH ) return m_pAppTitle; 1764 1765 LPTSTR pIndex = _tcsstr( pAppProdTitle, PRODUCT_NAME_VAR ); 1766 1767 if ( pIndex ) 1768 { 1769 int nOffset = pIndex - pAppProdTitle; 1770 int nVarLen = lstrlen( PRODUCT_NAME_VAR ); 1771 1772 LPTSTR pNewTitle = new TCHAR[ nLen ]; 1773 pNewTitle[0] = '\0'; 1774 1775 if ( nOffset > 0 ) 1776 { 1777 StringCchCopyN( pNewTitle, nLen, pAppProdTitle, nOffset ); 1778 } 1779 1780 StringCchCat( pNewTitle, nLen, pProdName ); 1781 1782 if ( nOffset + nVarLen < nAppLen ) 1783 { 1784 StringCchCat( pNewTitle, nLen, pIndex + nVarLen ); 1785 } 1786 1787 delete [] m_pAppTitle; 1788 m_pAppTitle = pNewTitle; 1789 } 1790 1791 delete [] pAppProdTitle; 1792 1793 return m_pAppTitle; 1794 } 1795 1796 1797 //-------------------------------------------------------------------------- 1798 boolean SetupApp::IsPatchInstalled( TCHAR* pBaseDir, TCHAR* pFileName ) 1799 { 1800 if ( !m_bSupportsPatch ) 1801 return false; 1802 1803 PMSIHANDLE hSummaryInfo; 1804 int nLen = lstrlen( pBaseDir ) + lstrlen( pFileName ) + 1; 1805 TCHAR *szDatabasePath = new TCHAR [ nLen ]; 1806 TCHAR sBuf[80]; 1807 1808 StringCchCopy( szDatabasePath, nLen, pBaseDir ); 1809 StringCchCat( szDatabasePath, nLen, pFileName ); 1810 1811 UINT nRet = aoo_MsiGetSummaryInformation( NULL, szDatabasePath, 0, &hSummaryInfo ); 1812 1813 if ( nRet != ERROR_SUCCESS ) 1814 { 1815 StringCchPrintf( sBuf, 80, TEXT("ERROR: IsPatchInstalled: MsiGetSummaryInformation returned %u.\r\n"), nRet ); 1816 Log( sBuf ); 1817 return false; 1818 } 1819 1820 UINT uiDataType; 1821 LPTSTR szPatchID = new TCHAR[ 64 ]; 1822 DWORD cchValueBuf = 64; 1823 nRet = aoo_MsiSummaryInfoGetProperty( hSummaryInfo, PID_REVNUMBER, &uiDataType, NULL, NULL, szPatchID, &cchValueBuf ); 1824 1825 if ( nRet != ERROR_SUCCESS ) 1826 { 1827 StringCchPrintf( sBuf, 80, TEXT("ERROR: IsPatchInstalled: MsiSummaryInfoGetProperty returned %u.\r\n"), nRet ); 1828 Log( sBuf ); 1829 return false; 1830 } 1831 1832 nRet = aoo_MsiGetPatchInfo( szPatchID, INSTALLPROPERTY_LOCALPACKAGE, NULL, NULL ); 1833 1834 StringCchPrintf( sBuf, 80, TEXT(" GetPatchInfo for (%s) returned (%u)\r\n"), szPatchID, nRet ); 1835 Log( sBuf ); 1836 1837 delete []szPatchID; 1838 1839 if ( nRet == ERROR_BAD_CONFIGURATION ) 1840 return false; 1841 else if ( nRet == ERROR_INVALID_PARAMETER ) 1842 return false; 1843 else if ( nRet == ERROR_MORE_DATA ) 1844 return true; 1845 else if ( nRet == ERROR_SUCCESS ) 1846 return true; 1847 else if ( nRet == ERROR_UNKNOWN_PRODUCT ) 1848 return false; 1849 else if ( nRet == ERROR_UNKNOWN_PROPERTY ) 1850 return false; 1851 else return false; 1852 } 1853 1854 //-------------------------------------------------------------------------- 1855 // The real Windows version, not the shimmed one. 1856 // 1857 // GetVersionEx() and the Windows Installer VersionNT / WindowsBuild properties all 1858 // report Windows 8.1 (6.3 / 9600) on Windows 10 and 11 unless the caller carries a 1859 // supportedOS manifest entry. Measured on Windows 11 build 26200, msiexec reports 1860 // VersionNT=603 and WindowsBuild=9600 -- which is why this check cannot be expressed 1861 // as an MSI LaunchCondition, and lives here instead. 1862 // 1863 // RtlGetVersion is not subject to that shim. 1864 static bool GetRealWindowsVersion( DWORD *pMajor, DWORD *pBuild ) 1865 { 1866 typedef LONG ( WINAPI *pfnRtlGetVersion_t )( OSVERSIONINFOW * ); 1867 1868 HMODULE hNtdll = ::GetModuleHandle( TEXT( "ntdll.dll" ) ); 1869 if ( hNtdll == NULL ) 1870 return false; 1871 1872 pfnRtlGetVersion_t pRtlGetVersion = 1873 (pfnRtlGetVersion_t)::GetProcAddress( hNtdll, "RtlGetVersion" ); 1874 if ( pRtlGetVersion == NULL ) 1875 return false; 1876 1877 OSVERSIONINFOW aInfo; 1878 ZeroMemory( &aInfo, sizeof( aInfo ) ); 1879 aInfo.dwOSVersionInfoSize = sizeof( aInfo ); 1880 1881 if ( pRtlGetVersion( &aInfo ) != 0 ) 1882 return false; 1883 1884 *pMajor = aInfo.dwMajorVersion; 1885 *pBuild = aInfo.dwBuildNumber; 1886 return true; 1887 } 1888 1889 //-------------------------------------------------------------------------- 1890 boolean SetupApp::CheckOSVersion() 1891 { 1892 DWORD nMajor = 0; 1893 DWORD nBuild = 0; 1894 1895 if ( !GetRealWindowsVersion( &nMajor, &nBuild ) ) 1896 { 1897 // Could not determine the version. Let the install proceed rather than 1898 // refuse on a machine we simply failed to identify -- if the OS really is too 1899 // old the runtime install will fail next, and say so. 1900 Log( TEXT( "Warning: could not determine the Windows version.\r\n" ) ); 1901 return true; 1902 } 1903 1904 TCHAR sBuf[ 128 ]; 1905 1906 StringCchPrintf( sBuf, 128, TEXT( " Windows major version %u, build %u\r\n" ), 1907 nMajor, nBuild ); 1908 Log( sBuf ); 1909 1910 if ( nMajor < REQUIRED_WINDOWS_MAJOR ) 1911 { 1912 StringCchPrintf( sBuf, 128, 1913 TEXT( "ERROR: Windows %u is older than the required Windows %u.\r\n" ), 1914 nMajor, (DWORD)REQUIRED_WINDOWS_MAJOR ); 1915 Log( sBuf ); 1916 SetError( ERROR_OS_TO_OLD ); 1917 return false; 1918 } 1919 1920 return true; 1921 } 1922 1923 //-------------------------------------------------------------------------- 1924 // Is the VC v14 runtime already usable in THIS process? 1925 // 1926 // Deliberately functional rather than a registry or ProductCode lookup: it needs no 1927 // GUID that goes stale with every servicing update, no registry view juggling, and it 1928 // tests the thing that actually matters -- whether our DLLs will be able to bind. 1929 // 1930 // LOAD_LIBRARY_SEARCH_SYSTEM32 so we probe the machine-wide runtime and cannot be 1931 // fooled by a stray copy sitting next to setup.exe. 1932 // 1933 // This can only answer for setup.exe's own bitness -- a 64 bit process cannot load a 1934 // 32 bit DLL and vice versa. That is fine: the caller only uses it to skip the 1935 // matching redistributable. For the other architecture we just run the bundle, which 1936 // is idempotent and returns 1638 quickly when a newer runtime is already there. 1937 static bool RuntimeAlreadyPresent() 1938 { 1939 const TCHAR *pModules[] = { 1940 TEXT( "vcruntime140.dll" ), 1941 TEXT( "msvcp140.dll" ), 1942 #if defined( _WIN64 ) 1943 // x64 only -- the separate EH runtime introduced with VS2017. 1944 TEXT( "vcruntime140_1.dll" ), 1945 #endif 1946 }; 1947 1948 for ( size_t i = 0; i < sizeof( pModules ) / sizeof( pModules[0] ); ++i ) 1949 { 1950 HMODULE hMod = ::LoadLibraryEx( pModules[i], NULL, 1951 LOAD_LIBRARY_SEARCH_SYSTEM32 ); 1952 if ( hMod == NULL ) 1953 return false; 1954 ::FreeLibrary( hMod ); 1955 } 1956 1957 return true; 1958 } 1959 1960 //-------------------------------------------------------------------------- 1961 boolean SetupApp::InstallRuntimes( TCHAR *sRuntimePath, bool bMatchesOwnArchitecture ) 1962 { 1963 if ( bMatchesOwnArchitecture && RuntimeAlreadyPresent() ) 1964 { 1965 Log( TEXT( " Runtime already present, skipping <%s>\r\n" ), sRuntimePath ); 1966 return true; 1967 } 1968 1969 Log( TEXT( " Will install runtime <%s>\r\n" ), sRuntimePath ); 1970 OutputDebugStringFormat( TEXT( " Will install runtime <%s>\r\n" ), sRuntimePath ); 1971 1972 STARTUPINFO aSUI; 1973 PROCESS_INFORMATION aPI; 1974 1975 ZeroMemory( (void*)&aPI, sizeof( PROCESS_INFORMATION ) ); 1976 ZeroMemory( (void*)&aSUI, sizeof( STARTUPINFO ) ); 1977 1978 aSUI.cb = sizeof(STARTUPINFO); 1979 aSUI.dwFlags = STARTF_USESHOWWINDOW; 1980 aSUI.wShowWindow = SW_SHOW; 1981 1982 DWORD nCmdLineLength = lstrlen( sRuntimePath ) + lstrlen( PARAM_SILENTINSTALL ) + 2; 1983 TCHAR *sCmdLine = new TCHAR[ nCmdLineLength ]; 1984 1985 if ( FAILED( StringCchCopy( sCmdLine, nCmdLineLength, sRuntimePath ) ) || 1986 FAILED( StringCchCat( sCmdLine, nCmdLineLength, PARAM_SILENTINSTALL ) ) ) 1987 { 1988 delete [] sCmdLine; 1989 SetError( ERROR_INSTALL_FAILURE ); 1990 return false; 1991 } 1992 1993 if ( !WIN::CreateProcess( NULL, sCmdLine, NULL, NULL, FALSE, 1994 CREATE_DEFAULT_ERROR_MODE, NULL, NULL, 1995 &aSUI, &aPI ) ) 1996 { 1997 Log( TEXT( "ERROR: Could not create process %s.\r\n" ), sCmdLine ); 1998 SetError( WIN::GetLastError() ); 1999 delete [] sCmdLine; 2000 return false; 2001 } 2002 2003 DWORD nResult = WaitForProcess( aPI.hProcess ); 2004 bool bRet = true; 2005 2006 if( ERROR_SUCCESS != nResult ) 2007 { 2008 Log( TEXT( "ERROR: While waiting for %s.\r\n" ), sCmdLine ); 2009 SetError( nResult ); 2010 bRet = false; 2011 } 2012 else 2013 { 2014 GetExitCodeProcess( aPI.hProcess, &nResult ); 2015 2016 // Burn reports more than one flavour of success. Treating anything non-zero as 2017 // a failure would flag every machine that already carries a newer runtime. 2018 if ( nResult == RUNTIME_INSTALL_OK ) 2019 { 2020 Log( TEXT( " Installation of runtime completed successfully.\r\n" ) ); 2021 } 2022 else if ( nResult == RUNTIME_INSTALL_NEWER ) 2023 { 2024 Log( TEXT( " A newer runtime is already installed, nothing to do.\r\n" ) ); 2025 } 2026 else if ( nResult == RUNTIME_INSTALL_REBOOT_REQ ) 2027 { 2028 Log( TEXT( " Installation of runtime completed, a reboot is required.\r\n" ) ); 2029 } 2030 else 2031 { 2032 TCHAR sBuf[80]; 2033 StringCchPrintf( sBuf, 80, 2034 TEXT("ERROR: install runtime returned %u.\r\n"), nResult ); 2035 Log( sBuf ); 2036 SetError( nResult ); 2037 bRet = false; 2038 } 2039 } 2040 2041 CloseHandle( aPI.hProcess ); 2042 2043 delete [] sCmdLine; 2044 2045 return bRet; 2046 } 2047 2048 //-------------------------------------------------------------------------- 2049 boolean SetupApp::InstallRuntimes() 2050 { 2051 TCHAR *sRuntimePath = 0; 2052 SYSTEM_INFO siSysInfo; 2053 2054 HMODULE hKernel32 = ::LoadLibrary(_T("Kernel32.dll")); 2055 if ( hKernel32 != NULL ) 2056 { 2057 typedef void (CALLBACK* pfnGetNativeSystemInfo_t)(LPSYSTEM_INFO); 2058 pfnGetNativeSystemInfo_t pfnGetNativeSystemInfo; 2059 pfnGetNativeSystemInfo = (pfnGetNativeSystemInfo_t)::GetProcAddress(hKernel32, "GetNativeSystemInfo"); 2060 if ( pfnGetNativeSystemInfo != NULL ) 2061 { 2062 pfnGetNativeSystemInfo(&siSysInfo); 2063 } 2064 else 2065 { 2066 // GetNativeSystemInfo does not exist. Maybe the code is running under Windows 2000. 2067 // Use GetSystemInfo instead. 2068 GetSystemInfo(&siSysInfo); 2069 } 2070 FreeLibrary(hKernel32); 2071 } 2072 else 2073 { 2074 // Failed to check Kernel32.dll. There may be something wrong. 2075 // Use GetSystemInfo instead anyway. 2076 GetSystemInfo(&siSysInfo); 2077 } 2078 2079 OutputDebugStringFormat( TEXT( "found architecture<%d>\r\n" ), siSysInfo.wProcessorArchitecture ); 2080 2081 bool bOk = true; 2082 2083 #if defined( _WIN64 ) 2084 2085 // A 64 bit office ships no 32 bit binaries at all, so it needs only the x64 2086 // runtime. (The 32 bit office is the other way round: it cross-builds 64 bit 2087 // shell extensions, which is why the branch below installs both.) 2088 (void)siSysInfo; 2089 2090 if ( GetPathToFile( RUNTIME_X64_NAME, &sRuntimePath ) ) 2091 bOk = InstallRuntimes( sRuntimePath, true ) ? true : false; 2092 else 2093 { 2094 Log( TEXT( "ERROR: no installer for x64 runtime libraries found!\r\n" ) ); 2095 bOk = false; 2096 } 2097 2098 if ( sRuntimePath ) 2099 delete [] sRuntimePath; 2100 2101 #else 2102 2103 // 32 bit office. On 64 bit Windows it additionally needs the x64 runtime, because 2104 // its shell extensions (shlxthdl_x64, ooofilt_x64, propertyhdl_x64, so_activex_x64) 2105 // are 64 bit and get loaded by 64 bit Explorer and friends. 2106 if ( siSysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ) 2107 { 2108 if ( GetPathToFile( RUNTIME_X64_NAME, &sRuntimePath ) ) 2109 { 2110 if ( !InstallRuntimes( sRuntimePath, false ) ) // cannot probe x64 from a 32 bit process 2111 bOk = false; 2112 } 2113 else 2114 { 2115 Log( TEXT( "ERROR: no installer for x64 runtime libraries found!\r\n" ) ); 2116 bOk = false; 2117 } 2118 2119 if ( sRuntimePath ) 2120 { 2121 delete [] sRuntimePath; 2122 sRuntimePath = 0; 2123 } 2124 } 2125 2126 if ( GetPathToFile( RUNTIME_X86_NAME, &sRuntimePath ) ) 2127 { 2128 if ( !InstallRuntimes( sRuntimePath, true ) ) 2129 bOk = false; 2130 } 2131 else 2132 { 2133 Log( TEXT( "ERROR: no installer for x86 runtime libraries found!\r\n" ) ); 2134 bOk = false; 2135 } 2136 2137 if ( sRuntimePath ) 2138 delete [] sRuntimePath; 2139 2140 #endif 2141 2142 // A failed runtime install used to be swallowed here -- the per-runtime result was 2143 // discarded and this returned true regardless. The office then installed and could 2144 // not start, with nothing to point at. Fail loudly instead. 2145 if ( !bOk ) 2146 { 2147 Log( TEXT( "ERROR: the Visual C++ runtime could not be installed.\r\n" ) ); 2148 SetError( ERROR_RUNTIME_FAILED ); 2149 return false; 2150 } 2151 2152 return true; 2153 } 2154 2155 //-------------------------------------------------------------------------- 2156 //-------------------------------------------------------------------------- 2157 LanguageData::LanguageData( LPTSTR pData ) 2158 { 2159 m_nLanguageID = 0; 2160 m_pTransform = NULL; 2161 2162 LPTSTR pLastChar; 2163 2164 m_nLanguageID = _tcstol( pData, &pLastChar, 10 ); 2165 2166 if ( *pLastChar == ',' ) 2167 { 2168 pLastChar += 1; 2169 int nLen = lstrlen( pLastChar ) + 1; 2170 m_pTransform = new TCHAR [ nLen ]; 2171 StringCchCopy( m_pTransform, nLen, pLastChar ); 2172 } 2173 } 2174 2175 //-------------------------------------------------------------------------- 2176 LanguageData::~LanguageData() 2177 { 2178 if ( m_pTransform ) delete [] m_pTransform; 2179 } 2180 2181 //-------------------------------------------------------------------------- 2182