xref: /AOO41X/main/setup_native/source/win32/customactions/patch/swappatchfiles.cxx (revision 03c97e340010506c11d4ffaab7f577e5f7050fe6)
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   sPatchFile = sProgramDir + TEXT("patchlist.txt");
545 
546     std::queue< std::_tstring > aSectionNames;
547     std::queue< std::_tstring > aKeyNames;
548 
549     OutputDebugStringA( "Starting Custom Action" );
550 
551     // std::_tstring    mystr;
552     // mystr = "Patchfile: " + sPatchFile;
553     // MessageBox( NULL, mystr.c_str(), "Patchfile", MB_OK );
554 
555     aSectionNames = getProfileSections( sPatchFile );
556     while ( !aSectionNames.empty() )
557     {
558         std::_tstring   sSectionName = aSectionNames.front();
559         if ( std::_tstring(TEXT("_root")) == sSectionName ) { sSectionName = TEXT(""); }
560         // mystr = "Section: " + sSectionName;
561         // MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
562 
563         aKeyNames = getProfileKeys( sPatchFile, sSectionName );
564         while ( !aKeyNames.empty() )
565         {
566             std::_tstring   sKeyName = aKeyNames.front();
567             std::_tstring   sValue = getProfileString( sPatchFile, sSectionName, sKeyName );
568 
569             if ( sValue.length() )
570             {
571                 std::_tstring   sFileName1 = sKeyName;
572                 std::_tstring   sExtension = sValue;
573                 std::_tstring   sFileName2;
574 
575                 sFileName1 = strip( sFileName1, '\"' );
576                 sExtension = strip( sExtension, '\"' );
577 
578                 sFileName1 = sInstDir + sSectionName + sFileName1;
579                 sFileName2 = sFileName1 + sExtension;
580 
581                 // mystr = "Convert: " + sFileName1 + " to " + sFileName2;
582                 // MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
583 
584                 SwapFiles( sFileName1, sFileName2 );
585             }
586 
587             aKeyNames.pop();
588         }
589 
590         aSectionNames.pop();
591     }
592 
593     return ERROR_SUCCESS;
594 }
595 
596 extern "C" UINT __stdcall UninstallPatchedFiles( MSIHANDLE handle )
597 {
598     TCHAR   szValue[8192];
599     DWORD   nValueSize = sizeof(szValue);
600     HKEY    hKey;
601 
602     std::_tstring   sInstDir;
603 
604     std::_tstring   sProductKey = GetMsiProperty( handle, TEXT("FINDPRODUCT") );
605 
606     if ( ERROR_SUCCESS == RegOpenKey( HKEY_CURRENT_USER,  sProductKey.c_str(), &hKey ) )
607     {
608         if ( ERROR_SUCCESS == RegQueryValueEx( hKey, TEXT("INSTALLLOCATION"), NULL, NULL, (LPBYTE)szValue, &nValueSize ) )
609         {
610             sInstDir = szValue;
611         }
612         RegCloseKey( hKey );
613     }
614     else if ( ERROR_SUCCESS == RegOpenKey( HKEY_LOCAL_MACHINE,  sProductKey.c_str(), &hKey ) )
615     {
616         if ( ERROR_SUCCESS == RegQueryValueEx( hKey, TEXT("INSTALLLOCATION"), NULL, NULL, (LPBYTE)szValue, &nValueSize ) )
617         {
618             sInstDir = szValue;
619         }
620         RegCloseKey( hKey );
621     }
622     else
623         return ERROR_SUCCESS;
624 
625     std::_tstring   sProgramDir = sInstDir + TEXT("Basis\\program\\");
626     std::_tstring   sPatchFile = sProgramDir + TEXT("patchlist.txt");
627 
628     std::queue< std::_tstring > aSectionNames;
629     std::queue< std::_tstring > aKeyNames;
630 
631     // std::_tstring    mystr;
632     // mystr = "Patchfile: " + sPatchFile;
633     // MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
634 
635     aSectionNames = getProfileSections( sPatchFile );
636     while ( !aSectionNames.empty() )
637     {
638         std::_tstring   sSectionName = aSectionNames.front();
639         if ( std::_tstring(TEXT("_root")) == sSectionName ) { sSectionName = TEXT(""); }
640         // mystr = "Section: " + sSectionName;
641         // MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
642 
643         aKeyNames = getProfileKeys( sPatchFile, sSectionName );
644         while( !aKeyNames.empty() )
645         {
646             std::_tstring   sKeyName = aKeyNames.front();
647             std::_tstring   sValue = getProfileString( sPatchFile, sSectionName, sKeyName );
648 
649             if ( sValue.length() )
650             {
651                 std::_tstring   sFileName1 = sKeyName;
652                 std::_tstring   sExtension = sValue;
653                 std::_tstring   sFileName2;
654 
655                 sFileName1 = strip( sFileName1, '\"' );
656                 sExtension = strip( sExtension, '\"' );
657 
658                 sFileName1 = sInstDir + sSectionName + sFileName1;
659                 sFileName2 = sFileName1 + sExtension;
660 
661                 // mystr = "Convert: " + sFileName1 + " to " + sFileName2;
662                 // MessageBox( NULL, mystr.c_str(), "Titel", MB_OK );
663 
664                 SwapFiles( sFileName2, sFileName1 );
665             }
666 
667             aKeyNames.pop();
668         }
669 
670         aSectionNames.pop();
671     }
672 
673     return ERROR_SUCCESS;
674 }
675 
676 extern "C" UINT __stdcall IsOfficeRunning( MSIHANDLE handle )
677 {
678     std::_tstring   sInstDir = GetMsiProperty( handle, TEXT("INSTALLLOCATION") );
679     std::_tstring   sResourceDir = sInstDir + TEXT("Basis\\program\\resource\\");
680     std::_tstring   sPattern = sResourceDir + TEXT("vcl*.res");
681 
682     WIN32_FIND_DATA aFindFileData;
683     HANDLE  hFind = FindFirstFile( sPattern.c_str(), &aFindFileData );
684 
685     if ( IsValidHandle(hFind) )
686     {
687         BOOL    fSuccess = false;
688         bool    fRenameSucceeded;
689 
690         do
691         {
692             std::_tstring   sResourceFile = sResourceDir + aFindFileData.cFileName;
693             std::_tstring   sIntermediate = sResourceFile + TEXT(".tmp");
694 
695             fRenameSucceeded = MoveFileExImpl( sResourceFile.c_str(), sIntermediate.c_str(), MOVEFILE_REPLACE_EXISTING );
696             if ( fRenameSucceeded )
697             {
698                 MoveFileExImpl( sIntermediate.c_str(), sResourceFile.c_str(), 0 );
699                 fSuccess = FindNextFile( hFind, &aFindFileData );
700             }
701         } while ( fSuccess && fRenameSucceeded );
702 
703         if ( !fRenameSucceeded )
704         {
705             MsiSetProperty(handle, TEXT("OFFICERUNS"), TEXT("1"));
706             SetMsiErrorCode( MSI_ERROR_OFFICE_IS_RUNNING );
707         }
708 
709         FindClose( hFind );
710     }
711 
712 
713     return ERROR_SUCCESS;
714 }
715 
716 extern "C" UINT __stdcall SetFeatureState( MSIHANDLE handle )
717 {
718     std::_tstring   mystr;
719 
720     // 1. Reading Product Code from setup.ini of installed Office
721 
722     std::_tstring sInstallPath = GetMsiProperty(handle, TEXT("INSTALLLOCATION"));
723     // MessageBox(NULL, sInstallPath.c_str(), "INSTALLLOCATION", MB_OK);
724     std::_tstring sSetupiniPath = sInstallPath + TEXT("program\\setup.ini");
725 
726     TCHAR szProductCode[32767];
727 
728     GetPrivateProfileString(
729         TEXT("Bootstrap"),
730         TEXT("ProductCode"),
731         TEXT("NOTFOUND"),
732         szProductCode,
733         elementsof(szProductCode),
734         sSetupiniPath.c_str()
735         );
736 
737     if ( !_tcsicmp( szProductCode, TEXT("NOTFOUND") ) )
738     {
739         // No setup.ini or no "ProductCode" in setup.ini. This is an invalid directory.
740         // MessageBox(NULL, "NOTFOUND set", "DEBUG", MB_OK);
741         return ERROR_SUCCESS;
742     }
743 
744     // 2. Converting Product code
745 
746     std::_tstring productCode = TEXT(szProductCode);
747     productCode = ConvertGuid(std::_tstring(productCode.c_str() + 1, productCode.length() - 2));
748     mystr = TEXT("Changed product code: ") + productCode;
749     // MessageBox(NULL, mystr.c_str(), "ProductCode", MB_OK);
750 
751     // 3. Setting path in the Windows registry to find installed features
752 
753     std::_tstring registryKey;
754     HKEY registryRoot;
755 
756     if ( IsSetMsiProperty(handle, TEXT("ALLUSERS")) )
757     {
758         registryRoot = HKEY_LOCAL_MACHINE;
759         registryKey = TEXT("Software\\Classes\\Installer\\Features\\") + productCode;
760         mystr = registryKey;
761         // MessageBox( NULL, mystr.c_str(), "ALLUSERS", MB_OK );
762     }
763     else
764     {
765         registryRoot = HKEY_CURRENT_USER;
766         registryKey = TEXT("Software\\Microsoft\\Installer\\Features\\") + productCode;
767         mystr = registryKey;
768         // MessageBox( NULL, mystr.c_str(), "ALLUSERS", MB_OK );
769     }
770 
771     // 4. Collecting all installed features from Windows registry
772 
773     HKEY hKey;
774     if (RegOpenKey(registryRoot, registryKey.c_str(), &hKey) == ERROR_SUCCESS)
775     {
776         int counter = 0;
777         // DWORD counter = 0;
778         LONG lEnumResult;
779 
780         do
781         {
782             TCHAR szValueName[8192];
783             DWORD nValueNameSize = sizeof(szValueName);
784             LPDWORD pValueNameSize = &nValueNameSize;
785             TCHAR szValueData[8192];
786             DWORD nValueDataSize = sizeof(szValueData);
787 
788             lEnumResult = RegEnumValue( hKey, counter, szValueName, pValueNameSize, NULL, NULL, (LPBYTE)szValueData, &nValueDataSize);
789 
790             if ( ERROR_SUCCESS == lEnumResult )
791             {
792                 std::_tstring sValueName = szValueName;
793                 std::_tstring sValueData = szValueData;
794 
795                 // mystr = sValueName;
796                 // MessageBox( NULL, mystr.c_str(), "ValueName", MB_OK );
797                 // mystr = sValueData;
798                 // MessageBox( NULL, mystr.c_str(), "ValueData", MB_OK );
799 
800                 // Does this feature exist in this patch?
801                 if ( IsSetMsiProperty(handle, sValueName) )
802                 {
803                     // Feature is not installed, if szValueData starts with a "square" (ascii 6)
804                     if ( 6 == szValueData[0] )
805                     {
806                         MsiSetFeatureState(handle,sValueName.c_str(),INSTALLSTATE_ABSENT); // do not install this feature
807                         // mystr = TEXT("Do NOT install: ") + sValueName;
808                         // MessageBox( NULL, mystr.c_str(), "ValueName", MB_OK );
809                     }
810                     else
811                     {
812                         MsiSetFeatureState(handle,sValueName.c_str(),INSTALLSTATE_LOCAL); // do install this feature
813                         // mystr = TEXT("Do install: ") + sValueName;
814                         // MessageBox( NULL, mystr.c_str(), "ValueName", MB_OK );
815                     }
816                 }
817             }
818 
819             counter = counter + 1;
820 
821         } while ( ERROR_SUCCESS == lEnumResult );
822 
823         RegCloseKey( hKey );
824     }
825 
826     return ERROR_SUCCESS;
827 }
828 
829 extern "C" UINT __stdcall SetNewFeatureState( MSIHANDLE handle )
830 {
831     std::_tstring mystr;
832     std::_tstring sValueName;
833 
834     sValueName = TEXT("gm_o_Onlineupdate");
835 
836     if (IsSetMsiProperty(handle, TEXT("SELECT_OU_FEATURE")))
837     {
838         MsiSetFeatureState(handle,sValueName.c_str(),INSTALLSTATE_LOCAL); // do install this feature
839         // mystr = TEXT("OnlineUpdate wird installiert!");
840         // MessageBox(NULL, mystr.c_str(), "INSTALLSTATE_LOCAL", MB_OK);
841     }
842     else
843     {
844         MsiSetFeatureState(handle,sValueName.c_str(),INSTALLSTATE_ABSENT); // do not install this feature
845         // mystr = TEXT("OnlineUpdate wird NICHT installiert!");
846         // MessageBox(NULL, mystr.c_str(), "INSTALLSTATE_ABSENT", MB_OK);
847     }
848 
849     return ERROR_SUCCESS;
850 }
851 
852 extern "C" UINT __stdcall ShowOnlineUpdateDialog( MSIHANDLE handle )
853 {
854     // Checking existence of file "updchk.uno.dll", which shows, that
855     // Online Update functionality is always available. Then the dialog
856     // that offers the Online Update is superfluous.
857 
858     std::_tstring sInstDir = GetMsiProperty( handle, TEXT("INSTALLLOCATION") );
859     std::_tstring sProgramDir = sInstDir + TEXT("Basis\\program\\");
860     std::_tstring sSearchFile = sProgramDir + TEXT("updchk.uno.dll");
861 
862     WIN32_FIND_DATA data;
863     HANDLE hdl = FindFirstFile(sSearchFile.c_str(), &data);
864     if (hdl != INVALID_HANDLE_VALUE)  // the file exists
865     {
866         // std::_tstring mystr;
867         // mystr = "Found file: " + sSearchFile;
868         // MessageBox( NULL, mystr.c_str(), "Found file", MB_OK );
869 
870         // And finally setting property SHOW_ONLINEUPDATE_DIALOG
871         // to hide this dialog
872         UnsetMsiProperty(handle, TEXT("SHOW_ONLINEUPDATE_DIALOG"));
873 
874         // Setting SELECT_OU_FEATURE to 1, which is probably superfluous
875         // because this is already the default value. But only this
876         // guarantees, that CustomAction SetNewFeatureState always sets
877         // the correct FeatureState for "gm_o_Onlineupdate", if it is
878         // already installed.
879         SetMsiProperty(handle, TEXT("SELECT_OU_FEATURE"));
880     }
881     else
882     {
883         // std::_tstring mystr;
884         // mystr = "Did not find file: " + sSearchFile;
885         // MessageBox( NULL, mystr.c_str(), "File not found", MB_OK );
886 
887         // If the file does not exist, the Online Update dialog
888         // has to be shown.
889         SetMsiProperty(handle, TEXT("SHOW_ONLINEUPDATE_DIALOG"));
890         FindClose(hdl);
891     }
892 
893     return ERROR_SUCCESS;
894 }
895