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