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