| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| #define WIN // scope W32 API |
| |
| #if defined _MSC_VER |
| #pragma warning(push, 1) |
| #endif |
| #include <windows.h> |
| #if defined _MSC_VER |
| #pragma warning(pop) |
| #endif |
| #include <tchar.h> |
| #include <assert.h> |
| #include <shlwapi.h> |
| #include <new> |
| #include <time.h> |
| #include <mbctype.h> |
| #include <locale.h> |
| #include <Msiquery.h> |
| #include <MsiDefs.h> |
| #include "strsafe.h" |
| |
| #include "setup.hxx" |
| |
| #include "resource.h" |
| |
| //-------------------------------------------------------------------------- |
| |
| #define MAX_STR_LENGTH 32000 |
| #define MAX_TEXT_LENGTH 1024 |
| #define MAX_LANGUAGE_LEN 80 |
| #define MAX_STR_CAPTION 256 |
| #define VERSION_SIZE 80 |
| #define SECTION_SETUP TEXT( "Setup" ) |
| #define SECTION_LANGUAGE TEXT( "Languages" ) |
| #define PRODUCT_NAME_VAR TEXT( "%PRODUCTNAME" ) |
| #define PRODUCT_VERSION TEXT( "ProductVersion" ) |
| #define ERROR_SHOW_USAGE -2 |
| #define ERROR_SETUP_TO_OLD -3 |
| #define ERROR_SETUP_NOT_FOUND -4 |
| |
| #define PARAM_SETUP_USED TEXT( " SETUP_USED=1 " ) |
| #define PARAM_PACKAGE TEXT( "/I " ) |
| #define PARAM_MINOR_UPGRADE TEXT( "/FVOMUS " ) |
| #define PARAM_ADMIN TEXT( "/A " ) |
| #define PARAM_TRANSFORM TEXT( " TRANSFORMS=" ) |
| #define PARAM_REBOOT TEXT( " REBOOT=Force" ) |
| #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( " /QB" ) |
| |
| #define PARAM_RUNNING TEXT( "ignore_running" ) |
| #define CMDLN_REG_ALL_MSO_TYPES TEXT( "msoreg=1" ) |
| #define CMDLN_REG_NO_MSO_TYPES TEXT( "msoreg=0" ) |
| |
| #define MSI_DLL TEXT( "msi.dll" ) |
| #define ADVAPI32_DLL TEXT( "advapi32.dll" ) |
| #define PROFILE_NAME TEXT( "setup.ini" ) |
| |
| #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}" ) |
| |
| #define MSIAPI_DllGetVersion "DllGetVersion" |
| #define ADVAPI32API_CheckTokenMembership "CheckTokenMembership" |
| |
| typedef HRESULT (CALLBACK* PFnDllGetVersion)( DLLVERSIONINFO *pdvi); |
| typedef BOOL (WINAPI* PFnCheckTokenMembership)(HANDLE TokenHandle, PSID SidToCheck, PBOOL IsMember); |
| |
| #ifdef DEBUG |
| inline void OutputDebugStringFormat( LPCTSTR pFormat, ... ) |
| { |
| TCHAR buffer[1024]; |
| va_list args; |
| |
| va_start( args, pFormat ); |
| StringCchVPrintf( buffer, sizeof(buffer), pFormat, args ); |
| OutputDebugString( buffer ); |
| } |
| #else |
| static inline void OutputDebugStringFormat( LPCTSTR, ... ) |
| { |
| } |
| #endif |
| |
| //-------------------------------------------------------------------------- |
| |
| const TCHAR sInstKey[] = TEXT( "Software\\Microsoft\\Windows\\CurrentVersion\\Installer" ); |
| const TCHAR sInstLocValue[] = TEXT( "InstallerLocation" ); |
| const TCHAR sMsiDll[] = TEXT( "\\msi.dll" ); |
| const TCHAR sMsiExe[] = TEXT( "\\msiexec.exe" ); |
| const TCHAR sDelayReboot[] = TEXT( " /c:\"msiinst /delayreboot\"" ); |
| const TCHAR sMsiQuiet[] = TEXT( " /q" ); |
| const TCHAR sMemMapName[] = TEXT( "Global\\MsiErrorObject" ); |
| |
| //-------------------------------------------------------------------------- |
| SetupAppX::SetupAppX() |
| { |
| m_hInst = NULL; |
| m_hMapFile = NULL; |
| m_pAppTitle = NULL; |
| m_pCmdLine = NULL; |
| |
| m_pDatabase = NULL; |
| m_pReqVersion = NULL; |
| m_pProductName = NULL; |
| m_pAdvertise = NULL; |
| m_pTmpName = NULL; |
| m_pLogFile = NULL; |
| m_pModuleFile = NULL; |
| m_pPatchFiles = NULL; |
| m_pMSIErrorCode = NULL; |
| m_pUpgradeKey = NULL; |
| m_pProductVersion = NULL; |
| |
| m_pErrorText = new TCHAR[ MAX_TEXT_LENGTH ]; |
| m_pErrorText[0] = '\0'; |
| |
| m_nLanguageID = 0; |
| m_nLanguageCount = 0; |
| m_ppLanguageList = NULL; |
| |
| m_bQuiet = false; |
| m_bRegNoMsoTypes = false; |
| m_bRegAllMsoTypes = false; |
| m_bIsMinorUpgrade = false; |
| m_bSupportsPatch = false; |
| |
| m_bIgnoreAlreadyRunning = false; |
| } |
| |
| //-------------------------------------------------------------------------- |
| SetupAppX::~SetupAppX() |
| { |
| if ( m_ppLanguageList ) |
| { |
| for ( int i = 0; i < m_nLanguageCount; i++ ) |
| if ( m_ppLanguageList[i] ) |
| delete m_ppLanguageList[ i ]; |
| delete [] m_ppLanguageList; |
| } |
| |
| time_t aTime; |
| time( &aTime ); |
| tm *pTime = localtime( &aTime ); // Convert time to struct tm form |
| |
| Log( TEXT( "End: %s\n\r\n\r\n" ), _tasctime( pTime ) ); |
| |
| if ( m_pLogFile ) fclose( m_pLogFile ); |
| |
| if ( m_pTmpName ) |
| { |
| _tremove( m_pTmpName ); |
| free( m_pTmpName ); |
| } |
| |
| if ( m_pMSIErrorCode ) UnmapViewOfFile( m_pMSIErrorCode ); |
| if ( m_hMapFile ) CloseHandle( m_hMapFile ); |
| |
| if ( m_pAppTitle ) delete [] m_pAppTitle; |
| if ( m_pDatabase ) delete [] m_pDatabase; |
| if ( m_pReqVersion ) delete [] m_pReqVersion; |
| if ( m_pProductName ) delete [] m_pProductName; |
| if ( m_pAdvertise ) delete [] m_pAdvertise; |
| if ( m_pLogFile ) delete [] m_pLogFile; |
| if ( m_pErrorText ) delete [] m_pErrorText; |
| if ( m_pModuleFile ) delete [] m_pModuleFile; |
| if ( m_pPatchFiles ) delete [] m_pPatchFiles; |
| if ( m_pUpgradeKey ) delete [] m_pUpgradeKey; |
| if ( m_pProductVersion ) delete [] m_pProductVersion; |
| } |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::Initialize( HINSTANCE hInst ) |
| { |
| m_pCmdLine = WIN::GetCommandLine(); |
| m_hInst = hInst; |
| |
| // Load our AppTitle (caption) |
| m_pAppTitle = new TCHAR[ MAX_STR_CAPTION ]; |
| m_pAppTitle[0] = '\0'; |
| WIN::LoadString( hInst, IDS_APP_TITLE, m_pAppTitle, MAX_STR_CAPTION ); |
| |
| // Obtain path we are running from |
| m_pModuleFile = new TCHAR[ MAX_PATH ]; |
| m_pModuleFile[ 0 ] = '\0'; |
| |
| if ( 0 == WIN::GetModuleFileName( hInst, m_pModuleFile, MAX_PATH ) ) |
| { |
| SetError( WIN::GetLastError() ); |
| return false; |
| } |
| |
| if ( ! GetCmdLineParameters( &m_pCmdLine ) ) |
| return false; |
| |
| m_hMapFile = CreateFileMapping( |
| INVALID_HANDLE_VALUE, // use paging file |
| NULL, // default security |
| PAGE_READWRITE, // read/write access |
| 0, // max. object size |
| sizeof( int ), // buffer size |
| sMemMapName ); |
| if ( m_hMapFile ) |
| { |
| m_pMSIErrorCode = (int*) MapViewOfFile( m_hMapFile, // handle to map object |
| FILE_MAP_ALL_ACCESS, // read/write permission |
| 0, |
| 0, |
| sizeof( int ) ); |
| if ( m_pMSIErrorCode ) |
| *m_pMSIErrorCode = 0; |
| else |
| OutputDebugStringFormat( TEXT("Could not map view of file (%d).\n"), GetLastError() ); |
| } |
| else |
| OutputDebugStringFormat( TEXT("Could not create file mapping object (%d).\n"), GetLastError() ); |
| |
| Log( TEXT("Starting: %s\r\n"), m_pModuleFile ); |
| Log( TEXT(" CommandLine=<%s>\r\n"), m_pCmdLine ); |
| |
| if ( m_bQuiet ) |
| Log( TEXT(" Using quiet install mode\r\n") ); |
| |
| time_t aTime; |
| time( &aTime ); |
| tm* pTime = localtime( &aTime ); |
| Log( TEXT(" Begin: %s\n"), _tasctime( pTime ) ); |
| |
| return true; |
| } |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::GetProfileSection( LPCTSTR pFileName, LPCTSTR pSection, |
| DWORD& rSize, LPTSTR *pRetBuf ) |
| { |
| if ( !rSize || !*pRetBuf ) |
| { |
| rSize = 512; |
| *pRetBuf = new TCHAR[ rSize ]; |
| } |
| |
| DWORD nRet = GetPrivateProfileSection( pSection, *pRetBuf, rSize, pFileName ); |
| |
| if ( nRet && ( nRet + 2 > rSize ) ) // buffer was too small, retry with bigger one |
| { |
| if ( nRet < 32767 - 2 ) |
| { |
| delete [] (*pRetBuf); |
| rSize = nRet + 2; |
| *pRetBuf = new TCHAR[ rSize ]; |
| |
| nRet = GetPrivateProfileSection( pSection, *pRetBuf, rSize, pFileName ); |
| } |
| } |
| |
| if ( !nRet ) |
| { |
| SetError( WIN::GetLastError() ); |
| |
| TCHAR sBuf[80]; |
| StringCchPrintf( sBuf, 80, TEXT("ERROR: GetPrivateProfileSection(): GetLastError returned %u\r\n"), GetError() ); |
| Log( sBuf ); |
| return false; |
| } |
| else if ( nRet + 2 > rSize ) |
| { |
| SetError( ERROR_OUTOFMEMORY ); |
| Log( TEXT( "ERROR: GetPrivateProfileSection() out of memory\r\n" ) ); |
| return false; |
| } |
| |
| Log( TEXT( " GetProfileSection read %s\r\n" ), pSection ); |
| |
| return true; |
| } |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::ReadProfile() |
| { |
| boolean bRet = false; |
| TCHAR *sProfilePath = 0; |
| |
| if ( GetPathToFile( PROFILE_NAME, &sProfilePath ) ) |
| { |
| DWORD nSize = 0; |
| LPTSTR pRetBuf = NULL; |
| |
| Log( TEXT( " Open ini file: <%s>\r\n" ), sProfilePath ); |
| |
| bRet = GetProfileSection( sProfilePath, SECTION_SETUP, nSize, &pRetBuf ); |
| |
| if ( !bRet ) |
| { |
| LPTSTR pTmpFile = CopyIniFile( sProfilePath ); |
| delete [] sProfilePath; |
| sProfilePath = pTmpFile; |
| |
| if ( sProfilePath ) |
| { |
| SetError( ERROR_SUCCESS ); |
| |
| Log( TEXT( " Could not open inifile, copied ini file to: <%s>\r\n" ), sProfilePath ); |
| bRet = GetProfileSection( sProfilePath, SECTION_SETUP, nSize, &pRetBuf ); |
| } |
| } |
| |
| if ( bRet ) |
| { |
| LPTSTR pCurLine = pRetBuf; |
| while ( *pCurLine ) |
| { |
| LPTSTR pName = 0; |
| LPTSTR pValue = 0; |
| |
| pCurLine += GetNameValue( pCurLine, &pName, &pValue ); |
| |
| if ( lstrcmpi( TEXT( "database" ), pName ) == 0 ) |
| { |
| m_pDatabase = pValue; |
| Log( TEXT( " Database = %s\r\n" ), pValue ); |
| } |
| else if ( lstrcmpi( TEXT( "msiversion" ), pName ) == 0 ) |
| { |
| m_pReqVersion = pValue; |
| Log( TEXT( " msiversion = %s\r\n" ), pValue ); |
| } |
| else if ( lstrcmpi( TEXT( "productname" ), pName ) == 0 ) |
| { |
| m_pProductName = pValue; |
| Log( TEXT( " productname = %s\r\n" ), pValue ); |
| m_pAppTitle = SetProdToAppTitle( m_pProductName ); |
| } |
| else if ( lstrcmpi( TEXT( "upgradekey" ), pName ) == 0 ) |
| { |
| m_pUpgradeKey = pValue; |
| Log( TEXT( " upgradekey = %s\r\n" ), pValue ); |
| } |
| else if ( lstrcmpi( TEXT( "productversion" ), pName ) == 0 ) |
| { |
| m_pProductVersion = pValue; |
| Log( TEXT( " productversion = %s\r\n" ), pValue ); |
| } |
| else if ( lstrcmpi( TEXT( "productcode" ), pName ) == 0 ) |
| { |
| delete [] pValue; |
| } |
| else |
| { |
| Log( TEXT( "Warning: unknown entry in profile <%s>\r\n" ), pName ); |
| delete [] pValue; |
| } |
| } |
| } |
| |
| if ( bRet && ( !m_pDatabase || !m_pReqVersion || !m_pProductName ) ) |
| { |
| Log( TEXT( "ERROR: incomplete 'Setup' section in profile\r\n" ) ); |
| SetError( ERROR_INVALID_DATA ); |
| bRet = false; |
| } |
| |
| if ( bRet ) |
| bRet = GetProfileSection( sProfilePath, SECTION_LANGUAGE, nSize, &pRetBuf ); |
| |
| if ( bRet ) |
| { |
| LPTSTR pName = 0; |
| LPTSTR pValue = 0; |
| LPTSTR pCurLine = pRetBuf; |
| LPTSTR pLastChar; |
| int nNext = 0; |
| |
| // first line in this section should be the language count |
| nNext = GetNameValue( pCurLine, &pName, &pValue ); |
| if ( lstrcmpi( TEXT( "count" ), pName ) == 0 ) |
| { |
| Log( TEXT( " Languages = %s\r\n" ), pValue ); |
| m_nLanguageCount = _tcstol( pValue, &pLastChar, 10 ); |
| pCurLine += nNext; |
| delete [] pValue; |
| } |
| |
| m_ppLanguageList = new LanguageDataX*[ m_nLanguageCount ]; |
| |
| for ( int i=0; i < m_nLanguageCount; i++ ) |
| { |
| if ( !*pCurLine ) |
| { |
| m_nLanguageCount = i; |
| break; |
| } |
| |
| pCurLine += GetNameValue( pCurLine, &pName, &pValue ); |
| m_ppLanguageList[ i ] = new LanguageDataX( pValue ); |
| Log( TEXT( " Language = %s\r\n" ), pValue ); |
| |
| if ( m_ppLanguageList[ i ]->m_pTransform ) |
| Log( TEXT( " Transform = %s\r\n" ), m_ppLanguageList[ i ]->m_pTransform ); |
| |
| delete [] pValue; |
| } |
| } |
| |
| if ( pRetBuf ) |
| delete [] pRetBuf; |
| } |
| |
| if ( sProfilePath && ! m_pTmpName ) |
| delete [] sProfilePath; |
| |
| return bRet; |
| } |
| |
| //-------------------------------------------------------------------------- |
| void SetupAppX::AddFileToPatchList( TCHAR* pPath, TCHAR* pFile ) |
| { |
| if ( m_pPatchFiles == NULL ) |
| { |
| m_pPatchFiles = new TCHAR[ MAX_STR_LENGTH ]; |
| StringCchCopy( m_pPatchFiles, MAX_STR_LENGTH, TEXT("\"") ); |
| } |
| else |
| StringCchCat( m_pPatchFiles, MAX_STR_LENGTH, TEXT(";") ); |
| |
| StringCchCat( m_pPatchFiles, MAX_STR_LENGTH, pPath ); |
| StringCchCat( m_pPatchFiles, MAX_STR_LENGTH, pFile ); |
| } |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::GetPatches() |
| { |
| boolean bRet = true; |
| |
| int nPatternLen = lstrlen( m_pModuleFile ) + 7; // 1 for null terminator, 1 for back slash, 5 for extensions |
| TCHAR* pPattern = new TCHAR[ nPatternLen ]; |
| TCHAR* pBaseDir = new TCHAR[ nPatternLen ]; |
| |
| // find 'setup.exe' in the path so we can remove it |
| TCHAR *pFilePart = 0; |
| if ( 0 == GetFullPathName( m_pModuleFile, nPatternLen, pPattern, &pFilePart ) ) |
| { |
| SetError( WIN::GetLastError() ); |
| bRet = false; |
| } |
| else |
| { |
| if ( pFilePart ) |
| *pFilePart = '\0'; |
| StringCchCopy( pBaseDir, nPatternLen, pPattern ); |
| StringCchCat( pPattern, nPatternLen, TEXT("*.msp") ); |
| |
| WIN32_FIND_DATA aFindFileData; |
| |
| HANDLE hFindPatches = FindFirstFile( pPattern, &aFindFileData ); |
| |
| if ( hFindPatches != INVALID_HANDLE_VALUE ) |
| { |
| if ( ! IsPatchInstalled( pBaseDir, aFindFileData.cFileName ) ) |
| AddFileToPatchList( pBaseDir, aFindFileData.cFileName ); |
| |
| while ( FindNextFile( hFindPatches, &aFindFileData ) ) |
| { |
| if ( ! IsPatchInstalled( pBaseDir, aFindFileData.cFileName ) ) |
| AddFileToPatchList( pBaseDir, aFindFileData.cFileName ); |
| } |
| |
| if ( m_pPatchFiles != NULL ) |
| StringCchCat( m_pPatchFiles, MAX_STR_LENGTH, TEXT("\"") ); |
| |
| FindClose( hFindPatches ); |
| } |
| } |
| |
| delete [] pPattern; |
| delete [] pBaseDir; |
| |
| return bRet; |
| } |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::GetPathToFile( TCHAR* pFileName, TCHAR** pPath ) |
| { |
| // generate the path to the file = szModuleFile + FileName |
| // note: FileName is a relative path |
| |
| boolean bRet = true; |
| |
| int nTempPath = lstrlen( m_pModuleFile ) + lstrlen( pFileName ) + 2; // 1 for null terminator, 1 for back slash |
| TCHAR* pTempPath = new TCHAR[ nTempPath ]; |
| |
| // find 'setup.exe' in the path so we can remove it |
| TCHAR *pFilePart = 0; |
| if ( 0 == GetFullPathName( m_pModuleFile, nTempPath, pTempPath, &pFilePart ) ) |
| { |
| SetError( WIN::GetLastError() ); |
| bRet = false; |
| } |
| else |
| { |
| if ( pFilePart ) |
| *pFilePart = '\0'; |
| |
| StringCchCat( pTempPath, nTempPath, pFileName ); |
| |
| int nPath = 2 * nTempPath; |
| *pPath = new TCHAR[ nPath ]; |
| |
| // normalize the path |
| int nReturn = GetFullPathName( pTempPath, nPath, *pPath, &pFilePart ); |
| |
| if ( nReturn > nPath ) |
| { |
| // try again, with larger buffer |
| delete [] (*pPath); |
| nPath = nReturn; |
| *pPath = new TCHAR[ nPath ]; |
| |
| nReturn = GetFullPathName( pTempPath, nPath, *pPath, &pFilePart ); |
| } |
| |
| if ( 0 == nReturn ) |
| { |
| // error -- invalid path |
| SetError( WIN::GetLastError() ); |
| bRet = false; |
| } |
| } |
| |
| if ( bRet ) // check for the file's existence |
| { |
| DWORD dwFileAttrib = GetFileAttributes( *pPath ); |
| |
| if (0xFFFFFFFF == dwFileAttrib) |
| { |
| StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pFileName ); |
| SetError( ERROR_FILE_NOT_FOUND ); |
| bRet = false; |
| } |
| } |
| |
| delete [] pTempPath; |
| return bRet; |
| } |
| |
| //-------------------------------------------------------------------------- |
| int SetupAppX::GetNameValue( TCHAR* pLine, TCHAR** pName, TCHAR** pValue ) |
| { |
| int nRet = lstrlen( pLine ) + 1; |
| *pValue = 0; |
| |
| if ( nRet == 1 ) |
| return nRet; |
| |
| LPTSTR pChar = pLine; |
| LPTSTR pLast = NULL; |
| |
| // Skip leading spaces. |
| while (' ' == *pChar || '\t' == *pChar) |
| pChar = CharNext( pChar ); |
| |
| *pName = pChar; |
| |
| // look for the end of the name |
| while( *pChar && (' ' != *pChar) && |
| ( '\t' != *pChar ) && ( '=' != *pChar ) ) |
| pChar = CharNext( pChar ); |
| |
| if ( ! *pChar ) |
| return nRet; |
| |
| pLast = pChar; |
| pChar = CharNext( pChar ); |
| *pLast = '\0'; |
| |
| // look for the start of the value |
| while( ( ' ' == *pChar ) || ( '\t' == *pChar ) || |
| ( '=' == *pChar ) ) |
| pChar = CharNext( pChar ); |
| |
| int nValueLen = lstrlen( pChar ) + 1; |
| *pValue = new TCHAR[ nValueLen ]; |
| |
| if ( *pValue ) |
| StringCchCopy( *pValue, nValueLen, pChar ); |
| |
| return nRet; |
| } |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::ChooseLanguage( long& rLanguage ) |
| { |
| rLanguage = 0; |
| |
| if ( m_bQuiet ) |
| return true; |
| |
| // When there are none or only one language, there is nothing |
| // to do here |
| if ( m_nLanguageCount > 1 ) |
| { |
| TCHAR *sString = new TCHAR[ MAX_LANGUAGE_LEN ]; |
| |
| LANGID nUserDefLang = GetUserDefaultLangID(); |
| LANGID nSysDefLang = GetSystemDefaultLangID(); |
| |
| int nUserPrimary = PRIMARYLANGID( nUserDefLang ); |
| int nSysPrimary = PRIMARYLANGID( nSysDefLang ); |
| |
| long nUserIndex = -1; |
| long nUserPrimIndex = -1; |
| long nSystemIndex = -1; |
| long nSystemPrimIndex = -1; |
| long nParamIndex = -1; |
| |
| for ( long i=0; i<GetLanguageCount(); i++ ) |
| { |
| long nLanguage = GetLanguageID( i ); |
| int nPrimary = PRIMARYLANGID( nLanguage ); |
| GetLanguageName( nLanguage, sString ); |
| Log( TEXT( " Info: found Language: %s\r\n" ), sString ); |
| |
| if ( nLanguage == nUserDefLang ) |
| nUserIndex = i; |
| if ( nPrimary == nUserPrimary ) |
| nUserPrimIndex = i; |
| if ( nLanguage == nSysDefLang ) |
| nSystemIndex = i; |
| if ( nPrimary == nSysPrimary ) |
| nSystemPrimIndex = i; |
| if ( m_nLanguageID && ( nLanguage == m_nLanguageID ) ) |
| nParamIndex = i; |
| } |
| |
| if ( m_nLanguageID && ( nParamIndex == -1 ) ) |
| { |
| Log( TEXT( "Warning: Language chosen with parameter -lang not found.\r\n" ) ); |
| } |
| |
| if ( nParamIndex != -1 ) |
| { |
| Log( TEXT( "Info: Found language chosen with parameter -lang.\r\n" ) ); |
| rLanguage = GetLanguageID( nParamIndex ); |
| } |
| else if ( nUserIndex != -1 ) |
| { |
| Log( TEXT( "Info: Found user default language.\r\n" ) ); |
| rLanguage = GetLanguageID( nUserIndex ); |
| } |
| else if ( nUserPrimIndex != -1 ) |
| { |
| Log( TEXT( "Info: Found user default primary language.\r\n" ) ); |
| rLanguage = GetLanguageID( nUserPrimIndex ); |
| } |
| else if ( nSystemIndex != -1 ) |
| { |
| Log( TEXT( "Info: Found system default language.\r\n" ) ); |
| rLanguage = GetLanguageID( nSystemIndex ); |
| } |
| else if ( nSystemPrimIndex != -1 ) |
| { |
| Log( TEXT( "Info: Found system default primary language.\r\n" ) ); |
| rLanguage = GetLanguageID( nSystemPrimIndex ); |
| } |
| else |
| { |
| Log( TEXT( "Info: Use default language from ini file.\r\n" ) ); |
| rLanguage = GetLanguageID( 0 ); |
| } |
| delete [] sString; |
| } |
| |
| return true; |
| } |
| |
| //-------------------------------------------------------------------------- |
| HMODULE SetupAppX::LoadMsiLibrary() |
| { |
| HMODULE hMsi = NULL; |
| HKEY hInstKey = NULL; |
| |
| // find registered location of Msi.dll |
| if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, sInstKey, 0, KEY_READ, &hInstKey ) ) |
| { |
| long nRet = ERROR_SUCCESS; |
| TCHAR *sMsiFolder = new TCHAR[ MAX_PATH + 1 ]; |
| DWORD dwMsiFolderSize = MAX_PATH + 1; |
| DWORD dwType = 0; |
| |
| if ( ERROR_MORE_DATA == ( nRet = RegQueryValueEx( hInstKey, sInstLocValue, NULL, |
| &dwType, (BYTE*)sMsiFolder, &dwMsiFolderSize ) ) ) |
| { |
| // try again with larger buffer |
| delete [] sMsiFolder; |
| sMsiFolder = new TCHAR[ dwMsiFolderSize ]; |
| |
| nRet = RegQueryValueEx( hInstKey, sInstLocValue, NULL, &dwType, |
| (BYTE*)sMsiFolder, &dwMsiFolderSize ); |
| } |
| |
| if ( ERROR_SUCCESS == nRet && dwType == REG_SZ && dwMsiFolderSize > 0 ) |
| { |
| // load Msi.dll from registered location |
| int nLength = lstrlen( sMsiDll ) + dwMsiFolderSize + 1; // use StringCchLength ? |
| TCHAR *pMsiLocation = new TCHAR[ nLength ]; |
| |
| if ( SUCCEEDED( StringCchCopy( pMsiLocation, nLength, sMsiFolder ) ) && |
| SUCCEEDED( StringCchCat( pMsiLocation, nLength, sMsiDll ) ) ) |
| { |
| hMsi = LoadLibrary( pMsiLocation ); |
| } |
| } |
| } |
| |
| if ( !hMsi ) // use the default location |
| { |
| hMsi = LoadLibrary( sMsiDll ); |
| } |
| |
| return hMsi; |
| } |
| |
| //-------------------------------------------------------------------------- |
| LPCTSTR SetupAppX::GetPathToMSI() |
| { |
| LPTSTR sMsiPath = NULL; |
| HKEY hInstKey = NULL; |
| TCHAR *sMsiFolder = new TCHAR[ MAX_PATH + 1 ]; |
| DWORD nMsiFolderSize = MAX_PATH + 1; |
| |
| sMsiFolder[0] = '\0'; |
| |
| // find registered location of Msi.dll |
| if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, sInstKey, 0, KEY_READ, &hInstKey ) ) |
| { |
| LONG nRet = ERROR_SUCCESS; |
| DWORD dwType = 0; |
| |
| if ( ERROR_MORE_DATA == ( nRet = RegQueryValueEx( hInstKey, sInstLocValue, NULL, |
| &dwType, (BYTE*)sMsiFolder, &nMsiFolderSize ) ) ) |
| { |
| // try again with larger buffer |
| delete [] sMsiFolder; |
| sMsiFolder = new TCHAR[ nMsiFolderSize ]; |
| |
| nRet = RegQueryValueEx( hInstKey, sInstLocValue, NULL, &dwType, |
| (BYTE*)sMsiFolder, &nMsiFolderSize ); |
| } |
| |
| if ( ERROR_SUCCESS != nRet || dwType != REG_SZ || nMsiFolderSize == 0 ) |
| sMsiFolder[0] = '\0'; |
| } |
| |
| if ( sMsiFolder[0] == '\0' ) // use the default location |
| { |
| Log( TEXT( " Could not find path to msiexec.exe in registry" ) ); |
| |
| DWORD nRet = WIN::GetSystemDirectory( sMsiFolder, nMsiFolderSize ); |
| if ( nRet > nMsiFolderSize ) |
| { |
| delete [] sMsiFolder; |
| sMsiFolder = new TCHAR[ nRet ]; |
| nMsiFolderSize = nRet; |
| |
| nRet = WIN::GetSystemDirectory( sMsiFolder, nMsiFolderSize ); |
| } |
| if ( 0 == nRet ) |
| { |
| sMsiFolder[0] = '\0'; |
| SetError( WIN::GetLastError() ); |
| } |
| nMsiFolderSize = nRet; |
| } |
| |
| if ( sMsiFolder[0] != '\0' ) |
| { |
| int nLength = lstrlen( sMsiExe ) + lstrlen( sMsiFolder ) + 1; |
| sMsiPath = new TCHAR[ nLength ]; |
| |
| if ( FAILED( StringCchCopy( sMsiPath, nLength, sMsiFolder ) ) || |
| FAILED( StringCchCat( sMsiPath, nLength, sMsiExe ) ) ) |
| { |
| delete [] sMsiPath; |
| sMsiPath = NULL; |
| } |
| } |
| |
| if ( ! sMsiPath ) |
| Log( TEXT( "ERROR: Can't build path to msiexec.exe!" ) ); |
| |
| return sMsiPath; |
| } |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::LaunchInstaller( LPCTSTR pParam ) |
| { |
| LPCTSTR sMsiPath = GetPathToMSI(); |
| |
| if ( !sMsiPath ) |
| { |
| Log( TEXT( "ERROR: msiexec not found!" ) ); |
| SetError( ERROR_FILE_NOT_FOUND ); |
| return false; |
| } |
| |
| STARTUPINFO aSUI; |
| PROCESS_INFORMATION aPI; |
| |
| Log( TEXT( " Will install using <%s>\r\n" ), sMsiPath ); |
| Log( TEXT( " Parameters are: %s\r\n" ), pParam ); |
| |
| OutputDebugStringFormat( TEXT( " Will install using <%s>\r\n" ), sMsiPath ); |
| OutputDebugStringFormat( TEXT( " Parameters are: %s\r\n" ), pParam ); |
| |
| ZeroMemory( (void*)&aPI, sizeof( PROCESS_INFORMATION ) ); |
| ZeroMemory( (void*)&aSUI, sizeof( STARTUPINFO ) ); |
| |
| aSUI.cb = sizeof(STARTUPINFO); |
| aSUI.dwFlags = STARTF_USESHOWWINDOW; |
| aSUI.wShowWindow = SW_SHOW; |
| |
| DWORD nCmdLineLength = lstrlen( sMsiPath ) + lstrlen( pParam ) + 2; |
| TCHAR *sCmdLine = new TCHAR[ nCmdLineLength ]; |
| |
| if ( FAILED( StringCchCopy( sCmdLine, nCmdLineLength, sMsiPath ) ) || |
| FAILED( StringCchCat( sCmdLine, nCmdLineLength, TEXT( " " ) ) ) || |
| FAILED( StringCchCat( sCmdLine, nCmdLineLength, pParam ) ) ) |
| { |
| delete [] sCmdLine; |
| SetError( ERROR_INSTALL_FAILURE ); |
| return false; |
| } |
| |
| if ( !WIN::CreateProcess( NULL, sCmdLine, NULL, NULL, FALSE, |
| CREATE_DEFAULT_ERROR_MODE, NULL, NULL, |
| &aSUI, &aPI ) ) |
| { |
| Log( TEXT( "ERROR: Could not create process %s.\r\n" ), sCmdLine ); |
| SetError( WIN::GetLastError() ); |
| delete [] sCmdLine; |
| return false; |
| } |
| |
| DWORD nResult = WaitForProcess( aPI.hProcess ); |
| bool bRet = true; |
| |
| if( ERROR_SUCCESS != nResult ) |
| { |
| Log( TEXT( "ERROR: While waiting for %s.\r\n" ), sCmdLine ); |
| SetError( nResult ); |
| bRet = false; |
| } |
| else |
| { |
| GetExitCodeProcess( aPI.hProcess, &nResult ); |
| SetError( nResult ); |
| |
| if ( nResult != ERROR_SUCCESS ) |
| { |
| TCHAR sBuf[80]; |
| StringCchPrintf( sBuf, 80, TEXT("Warning: msiexec returned %u.\r\n"), nResult ); |
| Log( sBuf ); |
| } |
| else |
| Log( TEXT( " Installation completed successfully.\r\n" ) ); |
| } |
| |
| CloseHandle( aPI.hProcess ); |
| |
| delete [] sCmdLine; |
| |
| return bRet; |
| } |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::Install( long nLanguage ) |
| { |
| LPTSTR pTransform = NULL; |
| |
| if ( nLanguage ) // look for transformation |
| { |
| for ( int i = 0; i < m_nLanguageCount; i++ ) |
| { |
| if ( m_ppLanguageList[i]->m_nLanguageID == nLanguage ) |
| { |
| if ( m_ppLanguageList[i]->m_pTransform ) |
| { |
| if ( !GetPathToFile( m_ppLanguageList[i]->m_pTransform, |
| &pTransform ) ) |
| { |
| Log( TEXT( "ERROR: Could not find transform <%s\r\n" ), m_ppLanguageList[i]->m_pTransform ); |
| return false; |
| } |
| } |
| break; |
| } |
| } |
| } |
| |
| TCHAR *pDataBasePath = NULL; |
| |
| if ( ! GetPathToFile( m_pDatabase, &pDataBasePath ) ) |
| { |
| Log( TEXT( "ERROR: Could not find database <%s\r\n" ), m_pDatabase ); |
| SetError( ERROR_INSTALL_SOURCE_ABSENT ); |
| return false; |
| } |
| |
| // we will always use the parameter setup used |
| int nParLen = lstrlen( PARAM_SETUP_USED ); |
| |
| if ( m_bRegNoMsoTypes ) |
| nParLen += lstrlen( PARAM_REG_NO_MSO_TYPES ); |
| else if ( m_bRegAllMsoTypes ) |
| nParLen += lstrlen( PARAM_REG_ALL_MSO_TYPES ); |
| |
| if ( m_pAdvertise ) |
| nParLen += lstrlen( m_pAdvertise ) + 1; // one for the space |
| else if ( m_bIsMinorUpgrade ) |
| nParLen += lstrlen( PARAM_MINOR_UPGRADE ); |
| else |
| nParLen += lstrlen( PARAM_PACKAGE ); |
| |
| nParLen += lstrlen( pDataBasePath ) + 3; // two quotes, one null |
| |
| if ( NeedReboot() ) |
| nParLen += lstrlen( PARAM_REBOOT ); |
| |
| if ( m_pPatchFiles ) |
| { |
| nParLen += lstrlen( PARAM_PATCH ); |
| nParLen += lstrlen( m_pPatchFiles ); |
| } |
| |
| if ( pTransform ) |
| { |
| nParLen += lstrlen( PARAM_TRANSFORM ); |
| nParLen += lstrlen( pTransform ) + 2; // two quotes |
| } |
| |
| if ( m_pCmdLine ) |
| nParLen += lstrlen( m_pCmdLine ) + 1; // one for the space; |
| |
| TCHAR *pParams = new TCHAR[ nParLen ]; |
| |
| StringCchCopy( pParams, nParLen, PARAM_SETUP_USED ); |
| |
| if ( m_bRegNoMsoTypes ) |
| StringCchCat( pParams, nParLen, PARAM_REG_NO_MSO_TYPES ); |
| else if ( m_bRegAllMsoTypes ) |
| StringCchCat( pParams, nParLen, PARAM_REG_ALL_MSO_TYPES ); |
| |
| if ( m_pAdvertise ) |
| StringCchCat( pParams, nParLen, m_pAdvertise ); |
| else if ( IsAdminInstall() ) |
| StringCchCat( pParams, nParLen, PARAM_ADMIN ); |
| else if ( m_bIsMinorUpgrade ) |
| StringCchCat( pParams, nParLen, PARAM_MINOR_UPGRADE ); |
| else |
| StringCchCat( pParams, nParLen, PARAM_PACKAGE ); |
| |
| StringCchCat( pParams, nParLen, TEXT( "\"" ) ); |
| StringCchCat( pParams, nParLen, pDataBasePath ); |
| StringCchCat( pParams, nParLen, TEXT( "\"" ) ); |
| |
| if ( NeedReboot() ) |
| StringCchCat( pParams, nParLen, PARAM_REBOOT ); |
| |
| if ( m_pPatchFiles ) |
| { |
| StringCchCat( pParams, nParLen, PARAM_PATCH ); |
| StringCchCat( pParams, nParLen, m_pPatchFiles ); |
| } |
| |
| if ( pTransform ) |
| { |
| StringCchCat( pParams, nParLen, PARAM_TRANSFORM ); |
| StringCchCat( pParams, nParLen, TEXT( "\"" ) ); |
| StringCchCat( pParams, nParLen, pTransform ); |
| StringCchCat( pParams, nParLen, TEXT( "\"" ) ); |
| } |
| |
| if ( m_pCmdLine ) |
| { |
| StringCchCat( pParams, nParLen, TEXT( " " ) ); |
| StringCchCat( pParams, nParLen, m_pCmdLine ); |
| } |
| |
| return LaunchInstaller( pParams ); |
| } |
| |
| //-------------------------------------------------------------------------- |
| UINT SetupAppX::GetError() const |
| { |
| UINT nErr = 0; |
| |
| if ( m_pMSIErrorCode ) |
| nErr = (UINT) *m_pMSIErrorCode; |
| |
| if ( nErr == 0 ) |
| nErr = m_uiRet; |
| |
| if ( nErr != 0 ) |
| OutputDebugStringFormat( TEXT("Setup will return error (%d).\n"), nErr ); |
| return nErr; |
| } |
| |
| //-------------------------------------------------------------------------- |
| void SetupAppX::DisplayError( UINT nErr ) const |
| { |
| TCHAR sError[ MAX_TEXT_LENGTH ] = {0}; |
| TCHAR sTmp[ MAX_TEXT_LENGTH ] = {0}; |
| |
| UINT nMsgType = MB_OK | MB_ICONERROR; |
| |
| switch ( nErr ) |
| { |
| case ERROR_SUCCESS: break; // 0 |
| |
| case ERROR_FILE_NOT_FOUND: // 2 |
| WIN::LoadString( m_hInst, IDS_FILE_NOT_FOUND, sTmp, MAX_TEXT_LENGTH ); |
| StringCchPrintf( sError, MAX_TEXT_LENGTH, sTmp, m_pErrorText ); |
| break; |
| case ERROR_INVALID_DATA: // 13 |
| WIN::LoadString( m_hInst, IDS_INVALID_PROFILE, sError, MAX_TEXT_LENGTH ); |
| break; |
| case ERROR_OUTOFMEMORY: WIN::LoadString( m_hInst, IDS_OUTOFMEM, sError, MAX_TEXT_LENGTH ); |
| break; |
| case ERROR_INSTALL_USEREXIT: |
| WIN::LoadString( m_hInst, IDS_USER_CANCELLED, sError, MAX_TEXT_LENGTH ); |
| break; |
| case ERROR_INSTALL_ALREADY_RUNNING: // 1618 |
| WIN::LoadString( m_hInst, IDS_ALREADY_RUNNING, sError, MAX_TEXT_LENGTH ); |
| break; |
| case ERROR_INSTALL_SOURCE_ABSENT: |
| WIN::LoadString( m_hInst, IDS_NOMSI, sError, MAX_TEXT_LENGTH ); |
| break; |
| case ERROR_DS_INSUFF_ACCESS_RIGHTS: // 8344 |
| WIN::LoadString( m_hInst, IDS_REQUIRES_ADMIN_PRIV, sError, MAX_TEXT_LENGTH ); |
| break; |
| case E_ABORT: WIN::LoadString( m_hInst, IDS_UNKNOWN_ERROR, sError, MAX_TEXT_LENGTH ); |
| break; |
| case ERROR_INVALID_PARAMETER: // 87 |
| WIN::LoadString( m_hInst, IDS_INVALID_PARAM, sTmp, MAX_TEXT_LENGTH ); |
| StringCchPrintf( sError, MAX_TEXT_LENGTH, sTmp, m_pErrorText ); |
| break; |
| |
| case ERROR_SETUP_TO_OLD: // - 3 |
| WIN::LoadString( m_hInst, IDS_SETUP_TO_OLD, sTmp, MAX_TEXT_LENGTH ); |
| StringCchPrintf( sError, MAX_TEXT_LENGTH, sTmp, m_pReqVersion, m_pErrorText ); |
| break; |
| case ERROR_SETUP_NOT_FOUND: // - 4 |
| WIN::LoadString( m_hInst, IDS_SETUP_NOT_FOUND, sTmp, MAX_TEXT_LENGTH ); |
| StringCchPrintf( sError, MAX_TEXT_LENGTH, sTmp, m_pReqVersion ); |
| break; |
| case ERROR_SHOW_USAGE: // - 2 |
| nMsgType = MB_OK | MB_ICONINFORMATION; |
| WIN::LoadString( m_hInst, IDS_USAGE, sError, MAX_TEXT_LENGTH ); |
| break; |
| |
| default: WIN::LoadString( m_hInst, IDS_UNKNOWN_ERROR, sError, MAX_TEXT_LENGTH ); |
| break; |
| } |
| |
| if ( sError[0] ) |
| { |
| if ( !m_bQuiet ) |
| { |
| ConvertNewline( sError ); |
| WIN::MessageBox( NULL, sError, m_pAppTitle, nMsgType ); |
| } |
| |
| Log( TEXT( "ERROR: %s\r\n" ), sError ); |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| long SetupAppX::GetLanguageID( long nIndex ) const |
| { |
| if ( nIndex >=0 && nIndex < m_nLanguageCount ) |
| return m_ppLanguageList[ nIndex ]->m_nLanguageID; |
| else |
| return 0; |
| } |
| |
| //-------------------------------------------------------------------------- |
| void SetupAppX::GetLanguageName( long nLanguage, LPTSTR sName ) const |
| { |
| switch ( nLanguage ) |
| { |
| case 1028: WIN::LoadString( m_hInst, IDS_LANGUAGE_ZH_TW, sName, MAX_LANGUAGE_LEN ); break; |
| case 1029: WIN::LoadString( m_hInst, IDS_LANGUAGE_CS, sName, MAX_LANGUAGE_LEN ); break; |
| case 1030: WIN::LoadString( m_hInst, IDS_LANGUAGE_DA, sName, MAX_LANGUAGE_LEN ); break; |
| case 1031: WIN::LoadString( m_hInst, IDS_LANGUAGE_DE_DE, sName, MAX_LANGUAGE_LEN ); break; |
| case 1032: WIN::LoadString( m_hInst, IDS_LANGUAGE_EL, sName, MAX_LANGUAGE_LEN ); break; |
| case 1033: WIN::LoadString( m_hInst, IDS_LANGUAGE_EN_US, sName, MAX_LANGUAGE_LEN ); break; |
| case 1034: WIN::LoadString( m_hInst, IDS_LANGUAGE_ES, sName, MAX_LANGUAGE_LEN ); break; |
| case 1035: WIN::LoadString( m_hInst, IDS_LANGUAGE_FI, sName, MAX_LANGUAGE_LEN ); break; |
| case 1036: WIN::LoadString( m_hInst, IDS_LANGUAGE_FR_FR, sName, MAX_LANGUAGE_LEN ); break; |
| case 1037: WIN::LoadString( m_hInst, IDS_LANGUAGE_HE, sName, MAX_LANGUAGE_LEN ); break; |
| case 1038: WIN::LoadString( m_hInst, IDS_LANGUAGE_HU, sName, MAX_LANGUAGE_LEN ); break; |
| case 1040: WIN::LoadString( m_hInst, IDS_LANGUAGE_IT_IT, sName, MAX_LANGUAGE_LEN ); break; |
| case 1041: WIN::LoadString( m_hInst, IDS_LANGUAGE_JA, sName, MAX_LANGUAGE_LEN ); break; |
| case 1042: WIN::LoadString( m_hInst, IDS_LANGUAGE_KO, sName, MAX_LANGUAGE_LEN ); break; |
| case 1043: WIN::LoadString( m_hInst, IDS_LANGUAGE_NL_NL, sName, MAX_LANGUAGE_LEN ); break; |
| case 1044: WIN::LoadString( m_hInst, IDS_LANGUAGE_NO_NO, sName, MAX_LANGUAGE_LEN ); break; |
| case 1045: WIN::LoadString( m_hInst, IDS_LANGUAGE_PL, sName, MAX_LANGUAGE_LEN ); break; |
| case 1046: WIN::LoadString( m_hInst, IDS_LANGUAGE_PT_BR, sName, MAX_LANGUAGE_LEN ); break; |
| case 1049: WIN::LoadString( m_hInst, IDS_LANGUAGE_RU, sName, MAX_LANGUAGE_LEN ); break; |
| case 1051: WIN::LoadString( m_hInst, IDS_LANGUAGE_SK, sName, MAX_LANGUAGE_LEN ); break; |
| case 1053: WIN::LoadString( m_hInst, IDS_LANGUAGE_SV_SE, sName, MAX_LANGUAGE_LEN ); break; |
| case 1054: WIN::LoadString( m_hInst, IDS_LANGUAGE_TH, sName, MAX_LANGUAGE_LEN ); break; |
| case 1055: WIN::LoadString( m_hInst, IDS_LANGUAGE_TR, sName, MAX_LANGUAGE_LEN ); break; |
| case 1061: WIN::LoadString( m_hInst, IDS_LANGUAGE_ET, sName, MAX_LANGUAGE_LEN ); break; |
| case 2052: WIN::LoadString( m_hInst, IDS_LANGUAGE_ZH_CN, sName, MAX_LANGUAGE_LEN ); break; |
| case 2070: WIN::LoadString( m_hInst, IDS_LANGUAGE_PT_PT, sName, MAX_LANGUAGE_LEN ); break; |
| |
| default: |
| { |
| TCHAR sTmp[ MAX_LANGUAGE_LEN ] = {0}; |
| |
| WIN::LoadString( m_hInst, IDS_UNKNOWN_LANG, sTmp, MAX_LANGUAGE_LEN ); |
| StringCchPrintf( sName, MAX_LANGUAGE_LEN, sTmp, nLanguage ); |
| } |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::CheckVersion() |
| { |
| boolean bRet = false; |
| HMODULE hMsi = LoadMsiLibrary(); |
| |
| Log( TEXT( " Looking for installed MSI with version >= %s\r\n" ), m_pReqVersion ); |
| |
| if ( !hMsi ) |
| { |
| Log( TEXT( "Error: No MSI found!\r\n" ) ); |
| SetError( (UINT) ERROR_SETUP_NOT_FOUND ); |
| } |
| else |
| { |
| PFnDllGetVersion pDllGetVersion = (PFnDllGetVersion) GetProcAddress( hMsi, MSIAPI_DllGetVersion ); |
| |
| if ( pDllGetVersion ) |
| { |
| DLLVERSIONINFO aInfo; |
| |
| aInfo.cbSize = sizeof( DLLVERSIONINFO ); |
| if ( NOERROR == pDllGetVersion( &aInfo ) ) |
| { |
| TCHAR pMsiVersion[ VERSION_SIZE ]; |
| StringCchPrintf( pMsiVersion, VERSION_SIZE, TEXT("%d.%d.%4d"), |
| aInfo.dwMajorVersion, |
| aInfo.dwMinorVersion, |
| aInfo.dwBuildNumber ); |
| if ( _tcsncmp( pMsiVersion, m_pReqVersion, _tcslen( pMsiVersion ) ) < 0 ) |
| { |
| StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pMsiVersion ); |
| SetError( (UINT) ERROR_SETUP_TO_OLD ); |
| Log( TEXT( "Warning: Old MSI version found <%s>, update needed!\r\n" ), pMsiVersion ); |
| } |
| else |
| { |
| Log( TEXT( " Found MSI version <%s>, no update needed\r\n" ), pMsiVersion ); |
| bRet = true; |
| } |
| if ( aInfo.dwMajorVersion >= 3 ) |
| m_bSupportsPatch = true; |
| else |
| Log( TEXT("Warning: Patching not supported! MSI-Version <%s>\r\n"), pMsiVersion ); |
| } |
| } |
| |
| FreeLibrary( hMsi ); |
| } |
| |
| return bRet; |
| } |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::CheckForUpgrade() |
| { |
| // When we have patch files we will never try an Minor upgrade |
| if ( m_pPatchFiles ) return true; |
| |
| if ( !m_pUpgradeKey || ( _tcslen( m_pUpgradeKey ) == 0 ) ) |
| { |
| Log( TEXT( " No Upgrade Key Found -> continue with standard installation!\r\n" ) ); |
| return true; |
| } |
| |
| HKEY hInstKey = NULL; |
| |
| if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, m_pUpgradeKey, 0, KEY_READ, &hInstKey ) ) |
| { |
| Log( TEXT( " Found Upgrade Key in Registry (HKLM) -> will try minor upgrade!\r\n" ) ); |
| m_bIsMinorUpgrade = true; |
| } |
| else if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_CURRENT_USER, m_pUpgradeKey, 0, KEY_READ, &hInstKey ) ) |
| { |
| Log( TEXT( " Found Upgrade Key in Registry (HKCU) -> will try minor upgrade!\r\n" ) ); |
| m_bIsMinorUpgrade = true; |
| } |
| else |
| { |
| Log( TEXT( " Didn't Find Upgrade Key in Registry -> continue with standard installation!\r\n" ) ); |
| return true; |
| } |
| |
| if ( m_pProductVersion && ( _tcslen( m_pProductVersion ) > 0 ) ) |
| { |
| TCHAR *sProductVersion = new TCHAR[ MAX_PATH + 1 ]; |
| DWORD nSize = MAX_PATH + 1; |
| |
| sProductVersion[0] = '\0'; |
| |
| // get product version |
| if ( ERROR_SUCCESS == RegQueryValueEx( hInstKey, PRODUCT_VERSION, NULL, NULL, (LPBYTE)sProductVersion, &nSize ) ) |
| { |
| if ( lstrcmpi( sProductVersion, m_pProductVersion ) == 0 ) |
| { |
| Log( TEXT( " Same Product Version already installed, no minor upgrade!\r\n" ) ); |
| m_bIsMinorUpgrade = false; |
| } |
| } |
| |
| delete [] sProductVersion; |
| } |
| |
| return true; |
| } |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::IsTerminalServerInstalled() const |
| { |
| boolean bIsTerminalServer = false; |
| |
| const TCHAR sSearchStr[] = TEXT("Terminal Server"); |
| const TCHAR sKey[] = TEXT("System\\CurrentControlSet\\Control\\ProductOptions"); |
| const TCHAR sValue[] = TEXT("ProductSuite"); |
| |
| DWORD dwSize = 0; |
| HKEY hKey = 0; |
| DWORD dwType = 0; |
| |
| if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, sKey, 0, KEY_READ, &hKey ) && |
| ERROR_SUCCESS == RegQueryValueEx( hKey, sValue, NULL, &dwType, NULL, &dwSize ) && |
| dwSize > 0 && |
| REG_MULTI_SZ == dwType ) |
| { |
| TCHAR* sSuiteList = new TCHAR[ (dwSize*sizeof(byte)/sizeof(TCHAR)) + 1 ]; |
| |
| ZeroMemory(sSuiteList, dwSize); |
| |
| if ( ERROR_SUCCESS == RegQueryValueEx( hKey, sValue, NULL, &dwType, (LPBYTE)sSuiteList, &dwSize) ) |
| { |
| DWORD nMulti = 0; |
| DWORD nSrch = lstrlen( sSearchStr ); |
| const TCHAR *sSubString = sSuiteList; |
| |
| while (*sSubString) |
| { |
| nMulti = lstrlen( sSubString ); |
| if ( nMulti == nSrch && 0 == lstrcmp( sSearchStr, sSubString ) ) |
| { |
| bIsTerminalServer = true; |
| break; |
| } |
| |
| sSubString += (nMulti + 1); |
| } |
| } |
| delete [] sSuiteList; |
| } |
| |
| if ( hKey ) |
| RegCloseKey( hKey ); |
| |
| return bIsTerminalServer; |
| } |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::AlreadyRunning() const |
| { |
| if ( m_bIgnoreAlreadyRunning ) |
| { |
| Log( TEXT("Ignoring already running MSI instance!\r\n") ); |
| return false; |
| } |
| |
| const TCHAR *sMutexName = NULL; |
| const TCHAR sGUniqueName[] = TEXT( "Global\\_MSISETUP_{EA8130C1-8D3D-4338-9309-1A52D530D846}" ); |
| const TCHAR sUniqueName[] = TEXT( "_MSISETUP_{EA8130C1-8D3D-4338-9309-1A52D530D846}" ); |
| |
| if ( IsWin9x() ) |
| sMutexName = sUniqueName; |
| else if ( ( GetOSVersion() < 5 ) && ! IsTerminalServerInstalled() ) |
| sMutexName = sUniqueName; |
| else |
| sMutexName = sGUniqueName; |
| |
| HANDLE hMutex = 0; |
| |
| hMutex = WIN::CreateMutex( NULL, FALSE, sMutexName ); |
| |
| if ( !hMutex || ERROR_ALREADY_EXISTS == WIN::GetLastError() ) |
| { |
| if ( !hMutex ) |
| Log( TEXT( "ERROR: AlreadyRunning() could not create mutex!\r\n" ) ); |
| else |
| Log( TEXT( "ERROR: There's already a setup running!\r\n" ) ); |
| |
| return true; |
| } |
| Log( TEXT( " No running Setup found\r\n" ) ); |
| |
| return false; |
| } |
| |
| //-------------------------------------------------------------------------- |
| DWORD SetupAppX::WaitForProcess( HANDLE hHandle ) |
| { |
| DWORD nResult = NOERROR; |
| boolean bLoop = true; |
| |
| MSG aMsg; |
| ZeroMemory( (void*) &aMsg, sizeof(MSG) ); |
| |
| while ( bLoop ) |
| { |
| switch ( WIN::MsgWaitForMultipleObjects( 1, &hHandle, false, |
| INFINITE, QS_ALLINPUT ) ) |
| { |
| case WAIT_OBJECT_0: bLoop = false; |
| break; |
| |
| case (WAIT_OBJECT_0 + 1): |
| { |
| if ( WIN::PeekMessage( &aMsg, NULL, NULL, NULL, PM_REMOVE ) ) |
| { |
| WIN::TranslateMessage( &aMsg ); |
| WIN::DispatchMessage( &aMsg ); |
| } |
| break; |
| } |
| |
| default: |
| { |
| nResult = WIN::GetLastError(); |
| bLoop = false; |
| } |
| } |
| } |
| |
| return nResult; |
| } |
| |
| //-------------------------------------------------------------------------- |
| void SetupAppX::Log( LPCTSTR pMessage, LPCTSTR pText ) const |
| { |
| if ( m_pLogFile ) |
| { |
| static boolean bInit = false; |
| |
| if ( !bInit ) |
| { |
| bInit = true; |
| if ( ! IsWin9x() ) |
| _ftprintf( m_pLogFile, TEXT("%c"), 0xfeff ); |
| |
| _tsetlocale( LC_ALL, TEXT("") ); |
| _ftprintf( m_pLogFile, TEXT("\nCodepage=%s\nMultiByte Codepage=[%d]\n"), |
| _tsetlocale( LC_ALL, NULL ), _getmbcp() ); |
| } |
| if ( pText ) |
| { |
| _ftprintf( m_pLogFile, pMessage, pText ); |
| OutputDebugStringFormat( pMessage, pText ); |
| } |
| else |
| { |
| _ftprintf( m_pLogFile, pMessage ); |
| OutputDebugStringFormat( pMessage ); |
| } |
| |
| fflush( m_pLogFile ); |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| DWORD SetupAppX::GetNextArgument( LPCTSTR pStr, LPTSTR *pArg, LPTSTR *pNext, |
| boolean bStripQuotes ) |
| { |
| boolean bInQuotes = false; |
| boolean bFoundArgEnd = false; |
| LPCTSTR pChar = pStr; |
| LPCTSTR pFirst = NULL; |
| |
| if ( NULL == pChar ) |
| return ERROR_NO_MORE_ITEMS; |
| |
| while ( ' ' == (*pChar) || '\t' == (*pChar) ) |
| pChar = CharNext( pChar ); |
| |
| if ( '\0' == (*pChar) ) |
| return ERROR_NO_MORE_ITEMS; |
| |
| int nCount = 1; |
| pFirst = pChar; |
| |
| while ( ! bFoundArgEnd ) |
| { |
| if ( '\0' == (*pChar) ) |
| bFoundArgEnd = true; |
| else if ( !bInQuotes && ' ' == (*pChar) ) |
| bFoundArgEnd = true; |
| else if ( !bInQuotes && '\t' == (*pChar) ) |
| bFoundArgEnd = true; |
| else |
| { |
| if ( '\"' == (*pChar) ) |
| { |
| bInQuotes = !bInQuotes; |
| if ( bStripQuotes ) |
| { |
| if ( pChar == pFirst ) |
| pFirst = CharNext( pFirst ); |
| nCount -= 1; |
| } |
| } |
| |
| pChar = CharNext( pChar ); |
| nCount += 1; |
| } |
| } |
| |
| if ( pArg ) |
| { |
| *pArg = new TCHAR[ nCount ]; |
| StringCchCopyN ( *pArg, nCount, pFirst, nCount-1 ); |
| } |
| |
| if ( pNext ) |
| *pNext = CharNext( pChar ); |
| |
| return ERROR_SUCCESS; |
| } |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::GetCmdLineParameters( LPTSTR *pCmdLine ) |
| { |
| int nRet = ERROR_SUCCESS; |
| LPTSTR pStart = NULL; |
| LPTSTR pNext = NULL; |
| |
| if ( GetNextArgument( *pCmdLine, NULL, &pNext ) != ERROR_SUCCESS ) |
| { |
| SetError( ERROR_NO_MORE_ITEMS ); |
| return false; |
| } |
| |
| int nSize = lstrlen( *pCmdLine ) + 2; |
| TCHAR *pNewCmdLine = new TCHAR[ nSize ]; |
| pNewCmdLine[0] = '\0'; |
| |
| while ( GetNextArgument( pNext, &pStart, &pNext ) == ERROR_SUCCESS ) |
| { |
| boolean bDeleteStart = true; |
| |
| if ( (*pStart) == '/' || (*pStart) == '-' ) |
| { |
| LPTSTR pSub = CharNext( pStart ); |
| if ( (*pSub) == 'l' || (*pSub) == 'L' ) |
| { |
| pSub = CharNext( pSub ); |
| if ( (*pSub) == 'a' || (*pSub) == 'A' ) |
| { // --- handle the lang parameter --- |
| LPTSTR pLanguage = NULL; |
| LPTSTR pLastChar; |
| if ( GetNextArgument( pNext, &pLanguage, &pNext, true ) != ERROR_SUCCESS ) |
| { |
| StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pStart ); |
| nRet = ERROR_INVALID_PARAMETER; |
| break; |
| } |
| |
| m_nLanguageID = _tcstol( pLanguage, &pLastChar, 10 ); |
| delete [] pLanguage; |
| } |
| else |
| { // --- handle the l(og) parameter --- |
| boolean bAppend = false; |
| LPTSTR pFileName = NULL; |
| |
| while ( *pSub ) |
| { |
| if ( *pSub == '+' ) |
| { |
| bAppend = true; |
| break; |
| } |
| pSub = CharNext( pSub ); |
| } |
| |
| if ( GetNextArgument( pNext, &pFileName, &pNext, true ) != ERROR_SUCCESS ) |
| { |
| StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pStart ); |
| nRet = ERROR_INVALID_PARAMETER; |
| break; |
| } |
| |
| if ( FAILED( StringCchCat( pNewCmdLine, nSize, pStart ) ) ) |
| { |
| nRet = ERROR_OUTOFMEMORY; |
| break; |
| } |
| // we need to append a '+' otherwise msiexec would overwrite our log file |
| if ( !bAppend && FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( "+" ) ) ) ) |
| { |
| nRet = ERROR_OUTOFMEMORY; |
| break; |
| } |
| if ( FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( " \"" ) ) ) || |
| FAILED( StringCchCat( pNewCmdLine, nSize, pFileName ) ) || |
| FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( "\" " ) ) ) ) |
| { |
| nRet = ERROR_OUTOFMEMORY; |
| break; |
| } |
| |
| if ( bAppend ) |
| m_pLogFile = _tfopen( pFileName, TEXT( "ab" ) ); |
| else |
| m_pLogFile = _tfopen( pFileName, TEXT( "wb" ) ); |
| |
| delete [] pFileName; |
| } |
| } |
| else if ( (*pSub) == 'q' || (*pSub) == 'Q' ) |
| { // --- Handle quiet file parameter --- |
| pSub = CharNext( pSub ); |
| if ( ! (*pSub) || (*pSub) == 'n' || (*pSub) == 'N' ) |
| m_bQuiet = true; |
| |
| if ( FAILED( StringCchCat( pNewCmdLine, nSize, pStart ) ) || |
| FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( " " ) ) ) ) |
| { |
| nRet = ERROR_OUTOFMEMORY; |
| break; |
| } |
| } |
| else if ( _tcsnicmp( pSub, PARAM_RUNNING, _tcslen( PARAM_RUNNING ) ) == 0 ) |
| { |
| m_bIgnoreAlreadyRunning = true; |
| } |
| else if ( _tcsnicmp( pSub, CMDLN_REG_ALL_MSO_TYPES, _tcslen( CMDLN_REG_ALL_MSO_TYPES ) ) == 0 ) |
| { |
| m_bRegAllMsoTypes = true; |
| } |
| else if ( _tcsnicmp( pSub, CMDLN_REG_NO_MSO_TYPES, _tcslen( CMDLN_REG_NO_MSO_TYPES ) ) == 0 ) |
| { |
| m_bRegNoMsoTypes = true; |
| } |
| else if ( (*pSub) == 'i' || (*pSub) == 'I' || (*pSub) == 'f' || (*pSub) == 'F' || |
| (*pSub) == 'p' || (*pSub) == 'P' || (*pSub) == 'x' || (*pSub) == 'X' || |
| (*pSub) == 'y' || (*pSub) == 'Y' || (*pSub) == 'z' || (*pSub) == 'Z' ) |
| { |
| StringCchCopy( m_pErrorText, MAX_TEXT_LENGTH, pStart ); |
| nRet = ERROR_INVALID_PARAMETER; |
| break; |
| } |
| else if ( (*pSub) == 'a' || (*pSub) == 'A' ) |
| { // --- Handle Administrative Installation --- |
| SetAdminInstall( true ); |
| } |
| else if ( (*pSub) == 'j' || (*pSub) == 'J' ) |
| { // --- Handle Administrative Installation --- |
| m_pAdvertise = pStart; |
| m_bQuiet = true; |
| bDeleteStart = false; |
| } |
| else if ( (*pSub) == '?' || (*pSub) == 'h' || (*pSub) == 'H' ) |
| { // --- Handle Show Usage --- |
| nRet = ERROR_SHOW_USAGE; |
| break; |
| } |
| else |
| { |
| if ( FAILED( StringCchCat( pNewCmdLine, nSize, pStart ) ) || |
| FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( " " ) ) ) ) |
| { |
| nRet = ERROR_OUTOFMEMORY; |
| break; |
| } |
| } |
| } |
| else |
| { |
| if ( FAILED( StringCchCat( pNewCmdLine, nSize, pStart ) ) || |
| FAILED( StringCchCat( pNewCmdLine, nSize, TEXT( " " ) ) ) ) |
| { |
| nRet = ERROR_OUTOFMEMORY; |
| break; |
| } |
| } |
| |
| if ( bDeleteStart ) delete [] pStart; |
| pStart = NULL; |
| } |
| |
| if ( pStart ) delete [] pStart; |
| |
| *pCmdLine = pNewCmdLine; |
| |
| if ( nRet != ERROR_SUCCESS ) |
| { |
| SetError( nRet ); |
| return false; |
| } |
| else |
| return true;; |
| } |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::IsAdmin() |
| { |
| if ( IsWin9x() ) |
| return true; |
| |
| PSID aPsidAdmin; |
| SID_IDENTIFIER_AUTHORITY aAuthority = SECURITY_NT_AUTHORITY; |
| |
| if ( !AllocateAndInitializeSid( &aAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, |
| DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, |
| &aPsidAdmin ) ) |
| return false; |
| |
| BOOL bIsAdmin = FALSE; |
| |
| if ( GetOSVersion() >= 5 ) |
| { |
| HMODULE hAdvapi32 = LoadLibrary( ADVAPI32_DLL ); |
| |
| if ( !hAdvapi32 ) |
| bIsAdmin = FALSE; |
| else |
| { |
| PFnCheckTokenMembership pfnCheckTokenMembership = (PFnCheckTokenMembership) GetProcAddress( hAdvapi32, ADVAPI32API_CheckTokenMembership); |
| if ( !pfnCheckTokenMembership || !pfnCheckTokenMembership( NULL, aPsidAdmin, &bIsAdmin ) ) |
| bIsAdmin = FALSE; |
| } |
| FreeLibrary( hAdvapi32 ); |
| } |
| else |
| { |
| // NT4, check groups of user |
| HANDLE hAccessToken = 0; |
| UCHAR *szInfoBuffer = new UCHAR[ 1024 ]; // may need to resize if TokenInfo too big |
| DWORD dwInfoBufferSize = 1024; |
| DWORD dwRetInfoBufferSize = 0; |
| UINT i=0; |
| |
| if ( WIN::OpenProcessToken( WIN::GetCurrentProcess(), TOKEN_READ, &hAccessToken ) ) |
| { |
| bool bSuccess = false; |
| bSuccess = WIN::GetTokenInformation( hAccessToken, TokenGroups, |
| szInfoBuffer, dwInfoBufferSize, |
| &dwRetInfoBufferSize ) == TRUE; |
| |
| if( dwRetInfoBufferSize > dwInfoBufferSize ) |
| { |
| delete [] szInfoBuffer; |
| szInfoBuffer = new UCHAR[ dwRetInfoBufferSize ]; |
| dwInfoBufferSize = dwRetInfoBufferSize; |
| bSuccess = WIN::GetTokenInformation( hAccessToken, TokenGroups, |
| szInfoBuffer, dwInfoBufferSize, |
| &dwRetInfoBufferSize ) == TRUE; |
| } |
| |
| WIN::CloseHandle( hAccessToken ); |
| |
| if ( bSuccess ) |
| { |
| PTOKEN_GROUPS pGroups = (PTOKEN_GROUPS)(UCHAR*) szInfoBuffer; |
| for( i=0; i<pGroups->GroupCount; i++ ) |
| { |
| if( WIN::EqualSid( aPsidAdmin, pGroups->Groups[i].Sid ) ) |
| { |
| bIsAdmin = TRUE; |
| break; |
| } |
| } |
| } |
| |
| delete [] szInfoBuffer; |
| } |
| } |
| |
| WIN::FreeSid( aPsidAdmin ); |
| |
| return bIsAdmin ? true : false; |
| } |
| |
| //-------------------------------------------------------------------------- |
| LPTSTR SetupAppX::CopyIniFile( LPCTSTR pIniFile ) |
| { |
| m_pTmpName = _ttempnam( TEXT( "C:\\" ), TEXT( "Setup" ) ); |
| |
| if ( !m_pTmpName ) |
| { |
| Log( TEXT( "ERROR: Could not create temp file\n" ) ); |
| return NULL; |
| } |
| |
| FILE *pOut = _tfopen( m_pTmpName, TEXT( "wb" ) ); |
| FILE *pIn = _tfopen( pIniFile, TEXT( "rb" ) ); |
| |
| if ( pOut && pIn ) |
| { |
| size_t nRead, nWritten; |
| BYTE pBuf[1024]; |
| |
| nRead = fread( pBuf, sizeof( BYTE ), 1024, pIn ); |
| while ( nRead && !ferror( pIn ) ) |
| { |
| nWritten = fwrite( pBuf, sizeof( BYTE ), nRead, pOut ); |
| if ( nWritten != nRead ) |
| { |
| Log( TEXT( "ERROR: Could not write all bytes to temp file\n" ) ); |
| break; |
| } |
| nRead = fread( pBuf, sizeof( BYTE ), 1024, pIn ); |
| } |
| } |
| |
| if ( pOut ) fclose( pOut ); |
| if ( pIn ) fclose( pIn ); |
| |
| return m_pTmpName; |
| } |
| |
| //-------------------------------------------------------------------------- |
| void SetupAppX::ConvertNewline( LPTSTR pText ) const |
| { |
| int i=0; |
| |
| while ( pText[i] != 0 ) |
| { |
| if ( ( pText[i] == '\\' ) && ( pText[i+1] == 'n' ) ) |
| { |
| pText[i] = 0x0d; |
| pText[i+1] = 0x0a; |
| i+=2; |
| } |
| else |
| i+=1; |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| LPTSTR SetupAppX::SetProdToAppTitle( LPCTSTR pProdName ) |
| { |
| if ( !pProdName ) return m_pAppTitle; |
| |
| LPTSTR pAppProdTitle = new TCHAR[ MAX_STR_CAPTION ]; |
| pAppProdTitle[0] = '\0'; |
| |
| WIN::LoadString( m_hInst, IDS_APP_PROD_TITLE, pAppProdTitle, MAX_STR_CAPTION ); |
| |
| int nAppLen = lstrlen( pAppProdTitle ); |
| int nProdLen = lstrlen( pProdName ); |
| |
| if ( ( nAppLen == 0 ) || ( nProdLen == 0 ) ) |
| { |
| delete [] pAppProdTitle; |
| return m_pAppTitle; |
| } |
| |
| int nLen = nAppLen + nProdLen + 3; |
| |
| if ( nLen > STRSAFE_MAX_CCH ) return m_pAppTitle; |
| |
| LPTSTR pIndex = _tcsstr( pAppProdTitle, PRODUCT_NAME_VAR ); |
| |
| if ( pIndex ) |
| { |
| int nOffset = pIndex - pAppProdTitle; |
| int nVarLen = lstrlen( PRODUCT_NAME_VAR ); |
| |
| LPTSTR pNewTitle = new TCHAR[ nLen ]; |
| pNewTitle[0] = '\0'; |
| |
| if ( nOffset > 0 ) |
| { |
| StringCchCopyN( pNewTitle, nLen, pAppProdTitle, nOffset ); |
| } |
| |
| StringCchCat( pNewTitle, nLen, pProdName ); |
| |
| if ( nOffset + nVarLen < nAppLen ) |
| { |
| StringCchCat( pNewTitle, nLen, pIndex + nVarLen ); |
| } |
| |
| delete [] m_pAppTitle; |
| m_pAppTitle = pNewTitle; |
| } |
| |
| delete [] pAppProdTitle; |
| |
| return m_pAppTitle; |
| } |
| |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::IsPatchInstalled( TCHAR* pBaseDir, TCHAR* pFileName ) |
| { |
| if ( !m_bSupportsPatch ) |
| return false; |
| |
| PMSIHANDLE hSummaryInfo; |
| int nLen = lstrlen( pBaseDir ) + lstrlen( pFileName ) + 1; |
| TCHAR *szDatabasePath = new TCHAR [ nLen ]; |
| TCHAR sBuf[80]; |
| |
| StringCchCopy( szDatabasePath, nLen, pBaseDir ); |
| StringCchCat( szDatabasePath, nLen, pFileName ); |
| |
| UINT nRet = MsiGetSummaryInformation( NULL, szDatabasePath, 0, &hSummaryInfo ); |
| |
| if ( nRet != ERROR_SUCCESS ) |
| { |
| StringCchPrintf( sBuf, 80, TEXT("ERROR: IsPatchInstalled: MsiGetSummaryInformation returned %u.\r\n"), nRet ); |
| Log( sBuf ); |
| return false; |
| } |
| |
| UINT uiDataType; |
| LPTSTR szPatchID = new TCHAR[ 64 ]; |
| DWORD cchValueBuf = 64; |
| nRet = MsiSummaryInfoGetProperty( hSummaryInfo, PID_REVNUMBER, &uiDataType, NULL, NULL, szPatchID, &cchValueBuf ); |
| |
| if ( nRet != ERROR_SUCCESS ) |
| { |
| StringCchPrintf( sBuf, 80, TEXT("ERROR: IsPatchInstalled: MsiSummaryInfoGetProperty returned %u.\r\n"), nRet ); |
| Log( sBuf ); |
| return false; |
| } |
| |
| nRet = MsiGetPatchInfo( szPatchID, INSTALLPROPERTY_LOCALPACKAGE, NULL, NULL ); |
| |
| StringCchPrintf( sBuf, 80, TEXT(" GetPatchInfo for (%s) returned (%u)\r\n"), szPatchID, nRet ); |
| Log( sBuf ); |
| |
| delete []szPatchID; |
| |
| if ( nRet == ERROR_BAD_CONFIGURATION ) |
| return false; |
| else if ( nRet == ERROR_INVALID_PARAMETER ) |
| return false; |
| else if ( nRet == ERROR_MORE_DATA ) |
| return true; |
| else if ( nRet == ERROR_SUCCESS ) |
| return true; |
| else if ( nRet == ERROR_UNKNOWN_PRODUCT ) |
| return false; |
| else if ( nRet == ERROR_UNKNOWN_PROPERTY ) |
| return false; |
| else return false; |
| |
| return false; |
| } |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::InstallRuntimes( TCHAR *sProductCode, TCHAR *sRuntimePath ) |
| { |
| INSTALLSTATE nRet = MsiQueryProductState( sProductCode ); |
| OutputDebugStringFormat( TEXT( "MsiQueryProductState returned <%d>\r\n" ), nRet ); |
| if ( nRet == INSTALLSTATE_DEFAULT ) |
| return true; |
| |
| Log( TEXT( " Will install runtime <%s>\r\n" ), sRuntimePath ); |
| OutputDebugStringFormat( TEXT( " Will install runtime <%s>\r\n" ), sRuntimePath ); |
| |
| STARTUPINFO aSUI; |
| PROCESS_INFORMATION aPI; |
| |
| ZeroMemory( (void*)&aPI, sizeof( PROCESS_INFORMATION ) ); |
| ZeroMemory( (void*)&aSUI, sizeof( STARTUPINFO ) ); |
| |
| aSUI.cb = sizeof(STARTUPINFO); |
| aSUI.dwFlags = STARTF_USESHOWWINDOW; |
| aSUI.wShowWindow = SW_SHOW; |
| |
| DWORD nCmdLineLength = lstrlen( sRuntimePath ) + lstrlen( PARAM_SILENTINSTALL ) + 2; |
| TCHAR *sCmdLine = new TCHAR[ nCmdLineLength ]; |
| |
| if ( FAILED( StringCchCopy( sCmdLine, nCmdLineLength, sRuntimePath ) ) || |
| FAILED( StringCchCat( sCmdLine, nCmdLineLength, PARAM_SILENTINSTALL ) ) ) |
| { |
| delete [] sCmdLine; |
| SetError( ERROR_INSTALL_FAILURE ); |
| return false; |
| } |
| |
| if ( !WIN::CreateProcess( NULL, sCmdLine, NULL, NULL, FALSE, |
| CREATE_DEFAULT_ERROR_MODE, NULL, NULL, |
| &aSUI, &aPI ) ) |
| { |
| Log( TEXT( "ERROR: Could not create process %s.\r\n" ), sCmdLine ); |
| SetError( WIN::GetLastError() ); |
| delete [] sCmdLine; |
| return false; |
| } |
| |
| DWORD nResult = WaitForProcess( aPI.hProcess ); |
| bool bRet = true; |
| |
| if( ERROR_SUCCESS != nResult ) |
| { |
| Log( TEXT( "ERROR: While waiting for %s.\r\n" ), sCmdLine ); |
| SetError( nResult ); |
| bRet = false; |
| } |
| else |
| { |
| GetExitCodeProcess( aPI.hProcess, &nResult ); |
| SetError( nResult ); |
| |
| if ( nResult != ERROR_SUCCESS ) |
| { |
| TCHAR sBuf[80]; |
| StringCchPrintf( sBuf, 80, TEXT("Warning: install runtime returned %u.\r\n"), nResult ); |
| Log( sBuf ); |
| } |
| else |
| Log( TEXT( " Installation of runtime completed successfully.\r\n" ) ); |
| } |
| |
| CloseHandle( aPI.hProcess ); |
| |
| delete [] sCmdLine; |
| |
| return bRet; |
| } |
| |
| //-------------------------------------------------------------------------- |
| boolean SetupAppX::InstallRuntimes() |
| { |
| TCHAR *sRuntimePath = 0; |
| SYSTEM_INFO siSysInfo; |
| |
| HMODULE hKernel32 = ::LoadLibrary(_T("Kernel32.dll")); |
| if ( hKernel32 != NULL ) |
| { |
| typedef void (CALLBACK* pfnGetNativeSystemInfo_t)(LPSYSTEM_INFO); |
| pfnGetNativeSystemInfo_t pfnGetNativeSystemInfo; |
| pfnGetNativeSystemInfo = (pfnGetNativeSystemInfo_t)::GetProcAddress(hKernel32, "GetNativeSystemInfo"); |
| if ( pfnGetNativeSystemInfo != NULL ) |
| { |
| pfnGetNativeSystemInfo(&siSysInfo); |
| } |
| else |
| { |
| // GetNativeSystemInfo does not exist. Maybe the code is running under Windows 2000. |
| // Use GetSystemInfo instead. |
| GetSystemInfo(&siSysInfo); |
| } |
| FreeLibrary(hKernel32); |
| } |
| else |
| { |
| // Failed to check Kernel32.dll. There may be something wrong. |
| // Use GetSystemInfo instead anyway. |
| GetSystemInfo(&siSysInfo); |
| } |
| |
| OutputDebugStringFormat( TEXT( "found architecture<%d>\r\n" ), siSysInfo.wProcessorArchitecture ); |
| |
| if ( siSysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ) |
| { |
| if ( GetPathToFile( RUNTIME_X64_NAME, &sRuntimePath ) ) |
| InstallRuntimes( PRODUCTCODE_X64, sRuntimePath ); |
| else |
| Log( TEXT( "ERROR: no installer for x64 runtime libraries found!" ) ); |
| |
| if ( sRuntimePath ) |
| { |
| delete [] sRuntimePath; |
| sRuntimePath = 0; |
| } |
| } |
| |
| if ( GetPathToFile( RUNTIME_X86_NAME, &sRuntimePath ) ) |
| InstallRuntimes( PRODUCTCODE_X86, sRuntimePath ); |
| else |
| Log( TEXT( "ERROR: no installer for x86 runtime libraries found!" ) ); |
| |
| if ( sRuntimePath ) |
| delete [] sRuntimePath; |
| |
| return true; |
| } |
| |
| //-------------------------------------------------------------------------- |
| //-------------------------------------------------------------------------- |
| LanguageDataX::LanguageDataX( LPTSTR pData ) |
| { |
| m_nLanguageID = 0; |
| m_pTransform = NULL; |
| |
| LPTSTR pLastChar; |
| |
| m_nLanguageID = _tcstol( pData, &pLastChar, 10 ); |
| |
| if ( *pLastChar == ',' ) |
| { |
| pLastChar += 1; |
| int nLen = lstrlen( pLastChar ) + 1; |
| m_pTransform = new TCHAR [ nLen ]; |
| StringCchCopy( m_pTransform, nLen, pLastChar ); |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| LanguageDataX::~LanguageDataX() |
| { |
| if ( m_pTransform ) delete [] m_pTransform; |
| } |
| |
| //-------------------------------------------------------------------------- |
| //-------------------------------------------------------------------------- |
| SetupApp* Create_SetupAppX() |
| { |
| return new SetupAppX; |
| } |
| |
| //-------------------------------------------------------------------------- |