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