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