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 _WIN32_WINDOWS 0x0410
25 
26 #ifdef _MSC_VER
27 #pragma warning(push, 1) /* disable warnings within system headers */
28 #endif
29 #define WIN32_LEAN_AND_MEAN
30 #include <windows.h>
31 #include <msiquery.h>
32 #ifdef _MSC_VER
33 #pragma warning(pop)
34 #endif
35 
36 #include <malloc.h>
37 #include <assert.h>
38 
39 #ifdef UNICODE
40 #define _UNICODE
41 #define _tstring	wstring
42 #else
43 #define _tstring	string
44 #endif
45 #include <tchar.h>
46 #include <string>
47 #include <queue>
48 #include <stdio.h>
49 
50 #include <systools/win32/uwinapi.h>
51 #include <../tools/seterror.hxx>
52 
53 #define	WININIT_FILENAME	"wininit.ini"
54 #define RENAME_SECTION		"rename"
55 
56 #ifdef DEBUG
57 inline void OutputDebugStringFormat( LPCTSTR pFormat, ... )
58 {
59 	_TCHAR	buffer[1024];
60 	va_list	args;
61 
62 	va_start( args, pFormat );
63 	_vsntprintf( buffer, elementsof(buffer), pFormat, args );
64 	OutputDebugString( buffer );
65 }
66 #else
67 static inline void OutputDebugStringFormat( LPCTSTR, ... )
68 {
69 }
70 #endif
71 
72 static std::_tstring GetMsiProperty( MSIHANDLE handle, const std::_tstring& sProperty )
73 {
74 	std::_tstring	result;
75 	TCHAR	szDummy[1] = TEXT("");
76 	DWORD	nChars = 0;
77 
78 	if ( MsiGetProperty( handle, sProperty.c_str(), szDummy, &nChars ) == ERROR_MORE_DATA )
79 	{
80 		DWORD nBytes = ++nChars * sizeof(TCHAR);
81 		LPTSTR buffer = reinterpret_cast<LPTSTR>(_alloca(nBytes));
82 		ZeroMemory( buffer, nBytes );
83 		MsiGetProperty(handle, sProperty.c_str(), buffer, &nChars);
84 		result = buffer;
85 	}
86 
87 	return	result;
88 }
89 
90 // The provided GUID must be without surounding '{}'
91 static std::_tstring GetGuidPart(const std::_tstring& guid, int index)
92 {
93 	assert((guid.length() == 36) && "No GUID or wrong format!");
94 	assert(((index > -1) && (index < 5)) && "Out of range!");
95 
96 	if (index == 0) return std::_tstring(guid.c_str(), 8);
97 	if (index == 1) return std::_tstring(guid.c_str() + 9, 4);
98 	if (index == 2) return std::_tstring(guid.c_str() + 14, 4);
99 	if (index == 3) return std::_tstring(guid.c_str() + 19, 4);
100 	if (index == 4) return std::_tstring(guid.c_str() + 24, 12);
101 
102 	return std::_tstring();
103 }
104 
105 static void Swap(char* p1, char* p2)
106 {
107 	char tmp = *p1;
108 	*p1 = *p2;
109 	*p2 = tmp;
110 }
111 
112 static std::_tstring Invert(const std::_tstring& str)
113 {
114 	char* buff = reinterpret_cast<char*>(_alloca(str.length()));
115 	strncpy(buff, str.c_str(), str.length());
116 
117 	char* front = buff;
118 	char* back = buff + str.length() - 1;
119 
120 	while (front < back)
121 		Swap(front++, back--);
122 
123 	return std::_tstring(buff, str.length());
124 }
125 
126 // Convert the upgrade code (which is a GUID) according
127 // to the way the windows installer does when writing it
128 // to the registry
129 // The first 8 bytes will be inverted, from the the last
130 // 8 bytes always the nibbles will be inverted for further
131 // details look in the MSDN under compressed registry keys
132 static std::_tstring ConvertGuid(const std::_tstring& guid)
133 {
134 	std::_tstring convertedGuid;
135 
136 	std::_tstring part = GetGuidPart(guid, 0);
137 	convertedGuid = Invert(part);
138 
139 	part = GetGuidPart(guid, 1);
140 	convertedGuid += Invert(part);
141 
142 	part = GetGuidPart(guid, 2);
143 	convertedGuid += Invert(part);
144 
145 	part = GetGuidPart(guid, 3);
146 	convertedGuid += Invert(std::_tstring(part.c_str(), 2));
147 	convertedGuid += Invert(std::_tstring(part.c_str() + 2, 2));
148 
149 	part = GetGuidPart(guid, 4);
150 	int pos = 0;
151 	for (int i = 0; i < 6; i++)
152 	{
153 		convertedGuid += Invert(std::_tstring(part.c_str() + pos, 2));
154 		pos += 2;
155 	}
156 	return convertedGuid;
157 }
158 
159 static inline bool IsSetMsiProperty(MSIHANDLE handle, const std::_tstring& sProperty)
160 {
161 	std::_tstring value = GetMsiProperty(handle, sProperty);
162 	return (value.length() > 0);
163 }
164 
165 static inline void UnsetMsiProperty(MSIHANDLE handle, const std::_tstring& sProperty)
166 {
167 	MsiSetProperty(handle, sProperty.c_str(), NULL);
168 }
169 
170 static inline void SetMsiProperty(MSIHANDLE handle, const std::_tstring& sProperty)
171 {
172 	MsiSetProperty(handle, sProperty.c_str(), TEXT("1"));
173 }
174 
175 static BOOL MoveFileEx9x( LPCSTR lpExistingFileNameA, LPCSTR lpNewFileNameA, DWORD dwFlags )
176 {
177 	BOOL	fSuccess = FALSE;	// assume failure
178 
179 	// Windows 9x has a special mechanism to move files after reboot
180 
181 	if ( dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT )
182 	{
183 		CHAR	szExistingFileNameA[MAX_PATH];
184 		CHAR	szNewFileNameA[MAX_PATH] = "NUL";
185 
186 		// Path names in WININIT.INI must be in short path name form
187 
188 		if (
189 			GetShortPathNameA( lpExistingFileNameA, szExistingFileNameA, MAX_PATH ) &&
190 			(!lpNewFileNameA || GetShortPathNameA( lpNewFileNameA, szNewFileNameA, MAX_PATH ))
191 			)
192 		{
193 			CHAR	szBuffer[32767];	// The buffer size must not exceed 32K
194 			DWORD	dwBufLen = GetPrivateProfileSectionA( RENAME_SECTION, szBuffer, elementsof(szBuffer), WININIT_FILENAME );
195 
196 			CHAR	szRename[MAX_PATH];	// This is enough for at most to times 67 chracters
197 			strcpy( szRename, szNewFileNameA );
198 			strcat( szRename, "=" );
199 			strcat( szRename, szExistingFileNameA );
200 			size_t	lnRename = strlen(szRename);
201 
202 			if ( dwBufLen + lnRename + 2 <= elementsof(szBuffer) )
203 			{
204 				CopyMemory( &szBuffer[dwBufLen], szRename, lnRename );
205 				szBuffer[dwBufLen + lnRename ] = 0;
206 				szBuffer[dwBufLen + lnRename + 1 ] = 0;
207 
208 				fSuccess = WritePrivateProfileSectionA( RENAME_SECTION, szBuffer, WININIT_FILENAME );
209 			}
210 			else
211 				SetLastError( ERROR_BUFFER_OVERFLOW );
212 		}
213 	}
214 	else
215 	{
216 
217 		fSuccess = MoveFileA( lpExistingFileNameA, lpNewFileNameA );
218 
219 		if ( !fSuccess && GetLastError() != ERROR_ACCESS_DENIED &&
220 			0 != (dwFlags & (MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) )
221 		{
222 			BOOL	bFailIfExist = 0 == (dwFlags & MOVEFILE_REPLACE_EXISTING);
223 
224 			fSuccess = CopyFileA( lpExistingFileNameA, lpNewFileNameA, bFailIfExist );
225 
226 			if ( fSuccess )
227 				fSuccess = DeleteFileA( lpExistingFileNameA );
228 		}
229 
230 	}
231 
232 	return fSuccess;
233 }
234 
235 static BOOL MoveFileExImpl( LPCSTR lpExistingFileNameA, LPCSTR lpNewFileNameA, DWORD dwFlags )
236 {
237 	if ( 0 > ((LONG)GetVersion())) // High order bit indicates Win 9x
238 		return MoveFileEx9x( lpExistingFileNameA, lpNewFileNameA, dwFlags );
239 	else
240 		return MoveFileExA( lpExistingFileNameA, lpNewFileNameA, dwFlags );
241 }
242 
243 static bool SwapFiles( const std::_tstring& sFileName1, const std::_tstring& sFileName2 )
244 {
245 	std::_tstring	sTempFileName = sFileName1 + TEXT(".tmp");
246 
247 	bool fSuccess = true;
248 
249 	//Try to move the original file to a temp file
250 	fSuccess = MoveFileExImpl( sFileName1.c_str(), sTempFileName.c_str(), MOVEFILE_REPLACE_EXISTING);
251 
252 	std::_tstring	mystr;
253 
254 	if ( fSuccess )
255 	{
256 		fSuccess = MoveFileExImpl( sFileName2.c_str(), sFileName1.c_str(), MOVEFILE_REPLACE_EXISTING );
257 
258 		if ( fSuccess )
259 		{
260 			fSuccess = MoveFileExImpl( sTempFileName.c_str(), sFileName2.c_str(),
261 										MOVEFILE_REPLACE_EXISTING );
262 			if ( !fSuccess )
263 			{
264 				MoveFileExImpl( sFileName1.c_str(), sFileName2.c_str(), MOVEFILE_REPLACE_EXISTING );
265 			}
266 		}
267 		else
268 		{
269 			MoveFileExImpl( sTempFileName.c_str(), sFileName1.c_str(), MOVEFILE_REPLACE_EXISTING  );
270 		}
271 	}
272 	else
273 	{
274 		//It could be that there is no original file and therefore copying the original to a temp
275 		// file failed. Examine if there is no original and if so then move file2 to file1
276 
277 		WIN32_FIND_DATA data;
278 		HANDLE hdl = FindFirstFile(sFileName1.c_str(), &data);
279 		if (hdl == INVALID_HANDLE_VALUE)
280 		{
281 			fSuccess = MoveFileExImpl( sFileName2.c_str(), sFileName1.c_str(), MOVEFILE_REPLACE_EXISTING );
282 
283 			// if ( fSuccess )
284 			// {
285 			//	mystr = "Success";
286 			//	MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
287 			// }
288 			// else
289 			// {
290 			//	char buff[256];
291 			//	wsprintf(buff, "Failure %d", GetLastError());
292 			//	MessageBox( NULL, buff, "Titel", MB_OK );
293 			// }
294 		}
295 		else
296 		{
297 			FindClose(hdl);
298 		}
299 	}
300 
301 	OutputDebugStringFormat( TEXT("%s <-> %s: %s"), sFileName1.c_str(), sFileName2.c_str(), fSuccess ? TEXT("OK") : TEXT("FAILED") );
302 
303 	if (!fSuccess )
304 	{
305 		DWORD	dwError = GetLastError();
306 		LPVOID lpMsgBuf;
307 		if ( FormatMessage(
308 			FORMAT_MESSAGE_ALLOCATE_BUFFER |
309 			FORMAT_MESSAGE_FROM_SYSTEM |
310 			FORMAT_MESSAGE_IGNORE_INSERTS,
311 			NULL,
312 			GetLastError(),
313 			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
314 			(LPTSTR) &lpMsgBuf,
315 			0,
316 			NULL ))
317 		{
318 			OutputDebugStringFormat( TEXT("Error Code %d: %s"), dwError, lpMsgBuf );
319 			LocalFree( lpMsgBuf );
320 		}
321 		else
322 			OutputDebugStringFormat( TEXT("Error Code %d: Unknown"), dwError );
323         SetMsiErrorCode( dwError );
324 	}
325 
326 	return fSuccess;
327 }
328 
329 static std::_tstring strip( const std::_tstring& s, _TCHAR c )
330 {
331 	std::_tstring	result = s;
332 
333 	std::_tstring::size_type f;
334 
335 	do
336 	{
337 		f = result.find( c );
338 		if ( f != std::_tstring::npos )
339 			result.erase( f, 1 );
340 	} while ( f != std::_tstring::npos );
341 
342 	return result;
343 }
344 
345 static std::_tstring trim( const std::_tstring& rString )
346 {
347 	std::_tstring temp = rString;
348 
349 	while ( temp.length() && temp[0] == ' ' || temp[0] == '\t' )
350 		temp.erase( 0, 1 );
351 
352 	std::_tstring::size_type	len = temp.length();
353 
354 	while ( len && temp[len-1] == ' ' || temp[len-1] == '\t' )
355 	{
356 		temp.erase( len - 1, 1 );
357 		len = temp.length();
358 	}
359 
360 	return temp;
361 }
362 
363 static bool readLine( FILE *fp, std::_tstring& rLine )
364 {
365 	_TCHAR szBuffer[1024];
366 	bool	bSuccess = false;
367 	bool	bEOL = false;
368 	std::_tstring	line;
369 
370 
371 	while ( !bEOL && _fgetts( szBuffer, sizeof(szBuffer), fp ) )
372 	{
373 		int	len = _tcslen(szBuffer);
374 
375 		bSuccess = true;
376 
377 		while ( len && szBuffer[len - 1] == '\n' )
378 		{
379 			szBuffer[--len] = 0;
380 			bEOL = true;
381 		}
382 
383 		line.append( szBuffer );
384 	}
385 
386 	rLine = line;
387 	return bSuccess;
388 }
389 
390 
391 static std::_tstring getProfileString(
392 	const std::_tstring& aFileName,
393 	const std::_tstring& aSectionName,
394 	const std::_tstring& aKeyName,
395 	const std::_tstring& aDefault = _T("") )
396 {
397 	FILE	*fp = _tfopen( aFileName.c_str(), _T("r") );
398 	std::_tstring	retValue = aDefault.length() ? aDefault : _T("");
399 
400 	if ( fp )
401 	{
402 		std::_tstring line;
403 		std::_tstring section;
404 
405 		while ( readLine( fp, line ) )
406 		{
407 			line = trim( line );
408 
409 			if ( line.length() && line[0] == '[' )
410 			{
411 				line.erase( 0, 1 );
412 				std::_tstring::size_type end = line.find( ']', 0 );
413 
414 				if ( std::_tstring::npos != end )
415 					section = trim( line.substr( 0, end ) );
416 			}
417 			else
418 			{
419 
420 				std::_tstring::size_type iEqualSign = line.find( '=', 0 );
421 
422 				if ( iEqualSign != std::_tstring::npos )
423 				{
424 					std::_tstring	keyname = line.substr( 0, iEqualSign );
425 					keyname = trim( keyname );
426 
427 					std::_tstring	value = line.substr( iEqualSign + 1 /*, std::_tstring::npos */ );
428 					value = trim( value );
429 
430 					if (
431 						0 == _tcsicmp( section.c_str(), aSectionName.c_str() ) &&
432 						0 == _tcsicmp( keyname.c_str(), aKeyName.c_str() )
433 						 )
434 					{
435 						retValue = value;
436 						break;
437 					}
438 				}
439 			}
440 		}
441 
442 		fclose( fp );
443 	}
444 
445 	return retValue;
446 }
447 
448 static std::queue< std::_tstring > getProfileSections( const std::_tstring& aFileName )
449 {
450 	FILE	*fp = _tfopen( aFileName.c_str(), _T("r") );
451 	std::queue< std::_tstring >	aResult;
452 
453 	OutputDebugStringFormat( TEXT("*** Retrieving Section Names ****") );
454 
455 	if ( fp )
456 	{
457 		std::_tstring line;
458 		std::_tstring section;
459 
460 		while ( readLine( fp, line ) )
461 		{
462 			line = trim( line );
463 
464 			if ( line.length() && line[0] == '[' )
465 			{
466 				line.erase( 0, 1 );
467 				std::_tstring::size_type end = line.find( ']', 0 );
468 
469 				if ( std::_tstring::npos != end )
470 					section = trim( line.substr( 0, end ) );
471 
472 				aResult.push( section );
473 
474 				OutputDebugStringFormat( TEXT("Section: %s"), section.c_str() );
475 
476 			}
477 		}
478 
479 		fclose( fp );
480 	}
481 
482 	OutputDebugStringFormat( TEXT("*** Done Section Names ***") );
483 
484 	return aResult;
485 }
486 
487 static std::queue< std::_tstring > getProfileKeys( const std::_tstring& aFileName, const std::_tstring& aSectionName )
488 {
489 	FILE	*fp = _tfopen( aFileName.c_str(), _T("r") );
490 	std::queue< std::_tstring >	aResult;
491 
492 	OutputDebugStringFormat( TEXT("*** Retrieving Key Names for [%s] ***"), aSectionName.c_str() );
493 
494 	if ( fp )
495 	{
496 		std::_tstring line;
497 		std::_tstring section;
498 
499 		while ( readLine( fp, line ) )
500 		{
501 			line = trim( line );
502 
503 			if ( line.length() && line[0] == '[' )
504 			{
505 				line.erase( 0, 1 );
506 				std::_tstring::size_type end = line.find( ']', 0 );
507 
508 				if ( std::_tstring::npos != end )
509 					section = trim( line.substr( 0, end ) );
510 			}
511 			else
512 			{
513 
514 				std::_tstring::size_type iEqualSign = line.find( '=', 0 );
515 
516 				if ( iEqualSign != std::_tstring::npos )
517 				{
518 					std::_tstring	keyname = line.substr( 0, iEqualSign );
519 					keyname = trim( keyname );
520 
521 					if ( 0 == _tcsicmp( section.c_str(), aSectionName.c_str() ) )
522 					{
523 						aResult.push( keyname );
524 
525 						OutputDebugStringFormat( keyname.c_str() );
526 
527 					}
528 				}
529 			}
530 		}
531 
532 		fclose( fp );
533 	}
534 
535 	OutputDebugStringFormat( TEXT("*** Done Key Names for [%s] ***"), aSectionName.c_str() );
536 
537 	return aResult;
538 }
539 
540 extern "C" UINT __stdcall InstallPatchedFiles( MSIHANDLE handle )
541 {
542 	std::_tstring	sInstDir = GetMsiProperty( handle, TEXT("INSTALLLOCATION") );
543 //	std::_tstring	sProgramDir = sInstDir + TEXT("Basis\\program\\");
544 	std::_tstring	sProgramDir = sInstDir + TEXT("program\\");
545 	std::_tstring	sPatchFile = sProgramDir + TEXT("patchlist.txt");
546 
547 	std::queue< std::_tstring > aSectionNames;
548 	std::queue< std::_tstring > aKeyNames;
549 
550 	OutputDebugStringA( "Starting Custom Action" );
551 
552 	// std::_tstring	mystr;
553 	// mystr = "Patchfile: " + sPatchFile;
554 	// MessageBox( NULL, mystr.c_str(), "Patchfile", MB_OK );
555 
556 	aSectionNames = getProfileSections( sPatchFile );
557 	while ( !aSectionNames.empty() )
558 	{
559 		std::_tstring	sSectionName = aSectionNames.front();
560 		if ( std::_tstring(TEXT("_root")) == sSectionName ) { sSectionName = TEXT(""); }
561 		// mystr = "Section: " + sSectionName;
562 		// MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
563 
564 		aKeyNames = getProfileKeys( sPatchFile, sSectionName );
565 		while ( !aKeyNames.empty() )
566 		{
567 			std::_tstring	sKeyName = aKeyNames.front();
568 			std::_tstring	sValue = getProfileString( sPatchFile, sSectionName, sKeyName );
569 
570 			if ( sValue.length() )
571 			{
572 				std::_tstring	sFileName1 = sKeyName;
573 				std::_tstring	sExtension = sValue;
574 				std::_tstring	sFileName2;
575 
576 				sFileName1 = strip( sFileName1, '\"' );
577 				sExtension = strip( sExtension, '\"' );
578 
579 				sFileName1 = sInstDir + sSectionName + sFileName1;
580 				sFileName2 = sFileName1 + sExtension;
581 
582 				// mystr = "Convert: " + sFileName1 + " to " + sFileName2;
583 				// MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
584 
585 				SwapFiles( sFileName1, sFileName2 );
586 			}
587 
588 			aKeyNames.pop();
589 		}
590 
591 		aSectionNames.pop();
592 	}
593 
594 	return ERROR_SUCCESS;
595 }
596 
597 extern "C" UINT __stdcall UninstallPatchedFiles( MSIHANDLE handle )
598 {
599 	TCHAR	szValue[8192];
600 	DWORD	nValueSize = sizeof(szValue);
601 	HKEY	hKey;
602 
603 	std::_tstring	sInstDir;
604 
605 	std::_tstring	sProductKey = GetMsiProperty( handle, TEXT("FINDPRODUCT") );
606 
607 	if ( ERROR_SUCCESS == RegOpenKey( HKEY_CURRENT_USER,  sProductKey.c_str(), &hKey ) )
608 	{
609 		if ( ERROR_SUCCESS == RegQueryValueEx( hKey, TEXT("INSTALLLOCATION"), NULL, NULL, (LPBYTE)szValue, &nValueSize ) )
610 		{
611 			sInstDir = szValue;
612 		}
613 		RegCloseKey( hKey );
614 	}
615 	else if ( ERROR_SUCCESS == RegOpenKey( HKEY_LOCAL_MACHINE,  sProductKey.c_str(), &hKey ) )
616 	{
617 		if ( ERROR_SUCCESS == RegQueryValueEx( hKey, TEXT("INSTALLLOCATION"), NULL, NULL, (LPBYTE)szValue, &nValueSize ) )
618 		{
619 			sInstDir = szValue;
620 		}
621 		RegCloseKey( hKey );
622 	}
623 	else
624 		return ERROR_SUCCESS;
625 
626 	std::_tstring	sProgramDir = sInstDir + TEXT("Basis\\program\\");
627 	std::_tstring	sPatchFile = sProgramDir + TEXT("patchlist.txt");
628 
629 	std::queue< std::_tstring >	aSectionNames;
630 	std::queue< std::_tstring >	aKeyNames;
631 
632 	// std::_tstring	mystr;
633 	// mystr = "Patchfile: " + sPatchFile;
634 	// MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
635 
636 	aSectionNames = getProfileSections( sPatchFile );
637 	while ( !aSectionNames.empty() )
638 	{
639 		std::_tstring	sSectionName = aSectionNames.front();
640 		if ( std::_tstring(TEXT("_root")) == sSectionName ) { sSectionName = TEXT(""); }
641 		// mystr = "Section: " + sSectionName;
642 		// MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
643 
644 		aKeyNames = getProfileKeys( sPatchFile, sSectionName );
645 		while( !aKeyNames.empty() )
646 		{
647 			std::_tstring	sKeyName = aKeyNames.front();
648 			std::_tstring	sValue = getProfileString( sPatchFile, sSectionName, sKeyName );
649 
650 			if ( sValue.length() )
651 			{
652 				std::_tstring	sFileName1 = sKeyName;
653 				std::_tstring	sExtension = sValue;
654 				std::_tstring	sFileName2;
655 
656 				sFileName1 = strip( sFileName1, '\"' );
657 				sExtension = strip( sExtension, '\"' );
658 
659 				sFileName1 = sInstDir + sSectionName + sFileName1;
660 				sFileName2 = sFileName1 + sExtension;
661 
662 				// mystr = "Convert: " + sFileName1 + " to " + sFileName2;
663 				// MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
664 
665 				SwapFiles( sFileName2, sFileName1 );
666 			}
667 
668 			aKeyNames.pop();
669 		}
670 
671 		aSectionNames.pop();
672 	}
673 
674 	return ERROR_SUCCESS;
675 }
676 
677 extern "C" UINT __stdcall IsOfficeRunning( MSIHANDLE handle )
678 {
679 	std::_tstring	sInstDir = GetMsiProperty( handle, TEXT("INSTALLLOCATION") );
680 	std::_tstring	sResourceDir = sInstDir + TEXT("Basis\\program\\resource\\");
681 	std::_tstring	sPattern = sResourceDir + TEXT("vcl*.res");
682 
683 	WIN32_FIND_DATA	aFindFileData;
684 	HANDLE	hFind = FindFirstFile( sPattern.c_str(), &aFindFileData );
685 
686 	if ( IsValidHandle(hFind) )
687 	{
688 		BOOL	fSuccess = false;
689 		bool	fRenameSucceeded;
690 
691 		do
692 		{
693 			std::_tstring	sResourceFile = sResourceDir + aFindFileData.cFileName;
694 			std::_tstring	sIntermediate = sResourceFile + TEXT(".tmp");
695 
696 			fRenameSucceeded = MoveFileExImpl( sResourceFile.c_str(), sIntermediate.c_str(), MOVEFILE_REPLACE_EXISTING );
697 			if ( fRenameSucceeded )
698 			{
699 				MoveFileExImpl( sIntermediate.c_str(), sResourceFile.c_str(), 0 );
700 				fSuccess = FindNextFile( hFind, &aFindFileData );
701 			}
702 		} while ( fSuccess && fRenameSucceeded );
703 
704 		if ( !fRenameSucceeded )
705         {
706 			MsiSetProperty(handle, TEXT("OFFICERUNS"), TEXT("1"));
707             SetMsiErrorCode( MSI_ERROR_OFFICE_IS_RUNNING );
708         }
709 
710 		FindClose( hFind );
711 	}
712 
713 
714 	return ERROR_SUCCESS;
715 }
716 
717 extern "C" UINT __stdcall SetFeatureState( MSIHANDLE handle )
718 {
719 	std::_tstring	mystr;
720 
721 	// 1. Reading Product Code from setup.ini of installed Office
722 
723 	std::_tstring sInstallPath = GetMsiProperty(handle, TEXT("INSTALLLOCATION"));
724 	// MessageBox(NULL, sInstallPath.c_str(), "INSTALLLOCATION", MB_OK);
725 	std::_tstring sSetupiniPath = sInstallPath + TEXT("program\\setup.ini");
726 
727 	TCHAR szProductCode[32767];
728 
729 	GetPrivateProfileString(
730 		TEXT("Bootstrap"),
731 		TEXT("ProductCode"),
732 		TEXT("NOTFOUND"),
733 		szProductCode,
734 		elementsof(szProductCode),
735 		sSetupiniPath.c_str()
736 		);
737 
738 	if ( !_tcsicmp( szProductCode, TEXT("NOTFOUND") ) )
739 	{
740 		// No setup.ini or no "ProductCode" in setup.ini. This is an invalid directory.
741 		// MessageBox(NULL, "NOTFOUND set", "DEBUG", MB_OK);
742 		return ERROR_SUCCESS;
743 	}
744 
745 	// 2. Converting Product code
746 
747 	std::_tstring productCode = TEXT(szProductCode);
748 	productCode = ConvertGuid(std::_tstring(productCode.c_str() + 1, productCode.length() - 2));
749 	mystr = TEXT("Changed product code: ") + productCode;
750 	// MessageBox(NULL, mystr.c_str(), "ProductCode", MB_OK);
751 
752 	// 3. Setting path in the Windows registry to find installed features
753 
754 	std::_tstring registryKey;
755 	HKEY registryRoot;
756 
757 	if ( IsSetMsiProperty(handle, TEXT("ALLUSERS")) )
758 	{
759 		registryRoot = HKEY_LOCAL_MACHINE;
760 		registryKey = TEXT("Software\\Classes\\Installer\\Features\\") + productCode;
761 		mystr = registryKey;
762 		// MessageBox( NULL, mystr.c_str(), "ALLUSERS", MB_OK );
763 	}
764 	else
765 	{
766 		registryRoot = HKEY_CURRENT_USER;
767 		registryKey = TEXT("Software\\Microsoft\\Installer\\Features\\") + productCode;
768 		mystr = registryKey;
769 		// MessageBox( NULL, mystr.c_str(), "ALLUSERS", MB_OK );
770 	}
771 
772 	// 4. Collecting all installed features from Windows registry
773 
774 	HKEY hKey;
775 	if (RegOpenKey(registryRoot, registryKey.c_str(), &hKey) == ERROR_SUCCESS)
776 	{
777 		int counter = 0;
778 		// DWORD counter = 0;
779 		LONG lEnumResult;
780 
781 		do
782 		{
783 			TCHAR szValueName[8192];
784 			DWORD nValueNameSize = sizeof(szValueName);
785 			LPDWORD pValueNameSize = &nValueNameSize;
786 			TCHAR szValueData[8192];
787 			DWORD nValueDataSize = sizeof(szValueData);
788 
789 			lEnumResult = RegEnumValue( hKey, counter, szValueName, pValueNameSize, NULL, NULL, (LPBYTE)szValueData, &nValueDataSize);
790 
791 			if ( ERROR_SUCCESS == lEnumResult )
792 			{
793 				std::_tstring sValueName = szValueName;
794 				std::_tstring sValueData = szValueData;
795 
796 				// mystr = sValueName;
797 				// MessageBox( NULL, mystr.c_str(), "ValueName", MB_OK );
798 				// mystr = sValueData;
799 				// MessageBox( NULL, mystr.c_str(), "ValueData", MB_OK );
800 
801 				// Does this feature exist in this patch?
802 				if ( IsSetMsiProperty(handle, sValueName) )
803 				{
804 					// Feature is not installed, if szValueData starts with a "square" (ascii 6)
805 					if ( 6 == szValueData[0] )
806 					{
807 						MsiSetFeatureState(handle,sValueName.c_str(),INSTALLSTATE_ABSENT); // do not install this feature
808 						// mystr = TEXT("Do NOT install: ") + sValueName;
809 						// MessageBox( NULL, mystr.c_str(), "ValueName", MB_OK );
810 					}
811 					else
812 					{
813 						MsiSetFeatureState(handle,sValueName.c_str(),INSTALLSTATE_LOCAL); // do install this feature
814 						// mystr = TEXT("Do install: ") + sValueName;
815 						// MessageBox( NULL, mystr.c_str(), "ValueName", MB_OK );
816 					}
817 				}
818 			}
819 
820 			counter = counter + 1;
821 
822 		} while ( ERROR_SUCCESS == lEnumResult );
823 
824 		RegCloseKey( hKey );
825 	}
826 
827 	return ERROR_SUCCESS;
828 }
829 
830 extern "C" UINT __stdcall SetNewFeatureState( MSIHANDLE handle )
831 {
832 	std::_tstring mystr;
833     std::_tstring sValueName;
834 
835     sValueName = TEXT("gm_o_Onlineupdate");
836 
837     if (IsSetMsiProperty(handle, TEXT("SELECT_OU_FEATURE")))
838     {
839         MsiSetFeatureState(handle,sValueName.c_str(),INSTALLSTATE_LOCAL); // do install this feature
840         // mystr = TEXT("OnlineUpdate wird installiert!");
841         // MessageBox(NULL, mystr.c_str(), "INSTALLSTATE_LOCAL", MB_OK);
842     }
843     else
844     {
845         MsiSetFeatureState(handle,sValueName.c_str(),INSTALLSTATE_ABSENT); // do not install this feature
846         // mystr = TEXT("OnlineUpdate wird NICHT installiert!");
847         // MessageBox(NULL, mystr.c_str(), "INSTALLSTATE_ABSENT", MB_OK);
848     }
849 
850 	return ERROR_SUCCESS;
851 }
852 
853 extern "C" UINT __stdcall ShowOnlineUpdateDialog( MSIHANDLE handle )
854 {
855     // Checking existence of file "updchk.uno.dll", which shows, that
856     // Online Update functionality is always available. Then the dialog
857     // that offers the Online Update is superfluous.
858 
859     std::_tstring sInstDir = GetMsiProperty( handle, TEXT("INSTALLLOCATION") );
860     std::_tstring sProgramDir = sInstDir + TEXT("Basis\\program\\");
861     std::_tstring sSearchFile = sProgramDir + TEXT("updchk.uno.dll");
862 
863     WIN32_FIND_DATA data;
864     HANDLE hdl = FindFirstFile(sSearchFile.c_str(), &data);
865     if (hdl != INVALID_HANDLE_VALUE)  // the file exists
866     {
867         // std::_tstring mystr;
868         // mystr = "Found file: " + sSearchFile;
869         // MessageBox( NULL, mystr.c_str(), "Found file", MB_OK );
870 
871         // And finally setting property SHOW_ONLINEUPDATE_DIALOG
872         // to hide this dialog
873         UnsetMsiProperty(handle, TEXT("SHOW_ONLINEUPDATE_DIALOG"));
874 
875         // Setting SELECT_OU_FEATURE to 1, which is probably superfluous
876         // because this is already the default value. But only this
877         // guarantees, that CustomAction SetNewFeatureState always sets
878         // the correct FeatureState for "gm_o_Onlineupdate", if it is
879         // already installed.
880         SetMsiProperty(handle, TEXT("SELECT_OU_FEATURE"));
881     }
882     else
883     {
884         // std::_tstring mystr;
885         // mystr = "Did not find file: " + sSearchFile;
886         // MessageBox( NULL, mystr.c_str(), "File not found", MB_OK );
887 
888         // If the file does not exist, the Online Update dialog
889         // has to be shown.
890         SetMsiProperty(handle, TEXT("SHOW_ONLINEUPDATE_DIALOG"));
891         FindClose(hdl);
892     }
893 
894 	return ERROR_SUCCESS;
895 }
896