2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/debug.h"
39 #include "wine/unicode.h"
42 #define REG_PROGRESS_VALUE 13200
43 #define COMPONENT_PROGRESS_VALUE 24000
45 WINE_DEFAULT_DEBUG_CHANNEL(msi);
47 static const WCHAR szCreateFolders[] =
48 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
49 static const WCHAR szCostFinalize[] =
50 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
51 static const WCHAR szWriteRegistryValues[] =
52 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
53 static const WCHAR szFileCost[] =
54 {'F','i','l','e','C','o','s','t',0};
55 static const WCHAR szInstallInitialize[] =
56 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
57 static const WCHAR szInstallValidate[] =
58 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
59 static const WCHAR szLaunchConditions[] =
60 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
61 static const WCHAR szProcessComponents[] =
62 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
63 static const WCHAR szRegisterTypeLibraries[] =
64 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
65 static const WCHAR szCreateShortcuts[] =
66 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
67 static const WCHAR szPublishProduct[] =
68 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
69 static const WCHAR szWriteIniValues[] =
70 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
71 static const WCHAR szSelfRegModules[] =
72 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
73 static const WCHAR szPublishFeatures[] =
74 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
75 static const WCHAR szRegisterProduct[] =
76 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
77 static const WCHAR szInstallExecute[] =
78 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
79 static const WCHAR szInstallExecuteAgain[] =
80 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
81 static const WCHAR szInstallFinalize[] =
82 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
83 static const WCHAR szForceReboot[] =
84 {'F','o','r','c','e','R','e','b','o','o','t',0};
85 static const WCHAR szResolveSource[] =
86 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
87 static const WCHAR szAllocateRegistrySpace[] =
88 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
89 static const WCHAR szBindImage[] =
90 {'B','i','n','d','I','m','a','g','e',0};
91 static const WCHAR szDeleteServices[] =
92 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
93 static const WCHAR szDisableRollback[] =
94 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
95 static const WCHAR szExecuteAction[] =
96 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
97 static const WCHAR szInstallAdminPackage[] =
98 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
99 static const WCHAR szInstallSFPCatalogFile[] =
100 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
101 static const WCHAR szIsolateComponents[] =
102 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
103 static const WCHAR szMigrateFeatureStates[] =
104 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
105 static const WCHAR szMsiUnpublishAssemblies[] =
106 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
107 static const WCHAR szInstallODBC[] =
108 {'I','n','s','t','a','l','l','O','D','B','C',0};
109 static const WCHAR szInstallServices[] =
110 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
111 static const WCHAR szPublishComponents[] =
112 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
113 static const WCHAR szRegisterComPlus[] =
114 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
115 static const WCHAR szRegisterUser[] =
116 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
117 static const WCHAR szRemoveEnvironmentStrings[] =
118 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
119 static const WCHAR szRemoveExistingProducts[] =
120 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
121 static const WCHAR szRemoveFolders[] =
122 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
123 static const WCHAR szRemoveIniValues[] =
124 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
125 static const WCHAR szRemoveODBC[] =
126 {'R','e','m','o','v','e','O','D','B','C',0};
127 static const WCHAR szRemoveRegistryValues[] =
128 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
129 static const WCHAR szRemoveShortcuts[] =
130 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
131 static const WCHAR szRMCCPSearch[] =
132 {'R','M','C','C','P','S','e','a','r','c','h',0};
133 static const WCHAR szScheduleReboot[] =
134 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
135 static const WCHAR szSelfUnregModules[] =
136 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
137 static const WCHAR szSetODBCFolders[] =
138 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
139 static const WCHAR szStartServices[] =
140 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
141 static const WCHAR szStopServices[] =
142 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
143 static const WCHAR szUnpublishComponents[] =
144 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
145 static const WCHAR szUnpublishFeatures[] =
146 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
147 static const WCHAR szUnregisterComPlus[] =
148 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
149 static const WCHAR szUnregisterTypeLibraries[] =
150 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
151 static const WCHAR szValidateProductID[] =
152 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
153 static const WCHAR szWriteEnvironmentStrings[] =
154 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
156 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
158 static const WCHAR Query_t[] =
159 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
160 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
161 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
162 ' ','\'','%','s','\'',0};
165 row = MSI_QueryGetRecord( package->db, Query_t, action );
168 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
169 msiobj_release(&row->hdr);
172 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
176 static const WCHAR template_s[]=
177 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179 static const WCHAR template_e[]=
180 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
181 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183 static const WCHAR format[] =
184 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
188 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190 sprintfW(message,template_s,timet,action);
192 sprintfW(message,template_e,timet,action,rc);
194 row = MSI_CreateRecord(1);
195 MSI_RecordSetStringW(row,1,message);
197 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
198 msiobj_release(&row->hdr);
208 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
210 enum parse_state state = state_quote;
213 int ignore, in_quotes = 0, count = 0, len = 0;
215 for (p = str; *p; p++)
220 case state_whitespace:
224 if (!count) goto done;
231 if (in_quotes) count--;
236 if (!count) in_quotes = 0;
248 if (in_quotes) count--;
252 state = state_whitespace;
253 if (!count) goto done;
258 if (!count) in_quotes = 0;
269 if (in_quotes) count--;
273 state = state_whitespace;
274 if (!count || (count > 1 && !len)) goto done;
280 if (!count) in_quotes = 0;
289 if (!ignore) *out++ = *p;
293 if (!len) *value = 0;
300 static void remove_quotes( WCHAR *str )
303 int len = strlenW( str );
305 while ((p = strchrW( p, '"' )))
307 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
312 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
322 return ERROR_SUCCESS;
327 while (*ptr == ' ') ptr++;
330 ptr2 = strchrW( ptr, '=' );
331 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
334 if (!len) return ERROR_INVALID_COMMAND_LINE;
336 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
337 memcpy( prop, ptr, len * sizeof(WCHAR) );
339 if (!preserve_case) struprW( prop );
342 while (*ptr2 == ' ') ptr2++;
345 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
346 len = parse_prop( ptr2, val, &num_quotes );
349 WARN("unbalanced quotes\n");
352 return ERROR_INVALID_COMMAND_LINE;
354 remove_quotes( val );
355 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
357 r = msi_set_property( package->db, prop, val );
358 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
359 msi_reset_folders( package, TRUE );
367 return ERROR_SUCCESS;
370 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
373 LPWSTR p, *ret = NULL;
379 /* count the number of substrings */
380 for ( pc = str, count = 0; pc; count++ )
382 pc = strchrW( pc, sep );
387 /* allocate space for an array of substring pointers and the substrings */
388 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
389 (lstrlenW(str)+1) * sizeof(WCHAR) );
393 /* copy the string and set the pointers */
394 p = (LPWSTR) &ret[count+1];
396 for( count = 0; (ret[count] = p); count++ )
398 p = strchrW( p, sep );
406 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
408 static const WCHAR szSystemLanguageID[] =
409 { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
411 LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
412 UINT ret = ERROR_FUNCTION_FAILED;
414 prod_code = msi_dup_property( package->db, szProductCode );
415 patch_product = msi_get_suminfo_product( patch );
417 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
419 if ( strstrW( patch_product, prod_code ) )
424 si = MSI_GetSummaryInformationW( patch, 0 );
427 ERR("no summary information!\n");
431 template = msi_suminfo_dup_string( si, PID_TEMPLATE );
434 ERR("no template property!\n");
435 msiobj_release( &si->hdr );
442 msiobj_release( &si->hdr );
446 langid = msi_dup_property( package->db, szSystemLanguageID );
449 msiobj_release( &si->hdr );
453 p = strchrW( template, ';' );
454 if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
456 TRACE("applicable transform\n");
460 /* FIXME: check platform */
462 msiobj_release( &si->hdr );
466 msi_free( patch_product );
467 msi_free( prod_code );
468 msi_free( template );
474 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
475 MSIDATABASE *patch_db, LPCWSTR name )
477 UINT ret = ERROR_FUNCTION_FAILED;
478 IStorage *stg = NULL;
481 TRACE("%p %s\n", package, debugstr_w(name) );
485 ERR("expected a colon in %s\n", debugstr_w(name));
486 return ERROR_FUNCTION_FAILED;
489 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
492 ret = msi_check_transform_applicable( package, stg );
493 if (ret == ERROR_SUCCESS)
494 msi_table_apply_transform( package->db, stg );
496 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
497 IStorage_Release( stg );
500 ERR("failed to open substorage %s\n", debugstr_w(name));
502 return ERROR_SUCCESS;
505 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
507 LPWSTR guid_list, *guids, product_code;
508 UINT i, ret = ERROR_FUNCTION_FAILED;
510 product_code = msi_dup_property( package->db, szProductCode );
513 /* FIXME: the property ProductCode should be written into the DB somewhere */
514 ERR("no product code to check\n");
515 return ERROR_SUCCESS;
518 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
519 guids = msi_split_string( guid_list, ';' );
520 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
522 if (!strcmpW( guids[i], product_code ))
526 msi_free( guid_list );
527 msi_free( product_code );
532 static UINT msi_set_media_source_prop(MSIPACKAGE *package)
535 MSIRECORD *rec = NULL;
540 static const WCHAR query[] = {'S','E','L','E','C','T',' ',
541 '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
542 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
543 '`','S','o','u','r','c','e','`',' ','I','S',' ',
544 'N','O','T',' ','N','U','L','L',0};
546 r = MSI_DatabaseOpenViewW(package->db, query, &view);
547 if (r != ERROR_SUCCESS)
550 r = MSI_ViewExecute(view, 0);
551 if (r != ERROR_SUCCESS)
554 if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
556 prop = MSI_RecordGetString(rec, 1);
557 patch = msi_dup_property(package->db, szPatch);
558 msi_set_property(package->db, prop, patch);
563 if (rec) msiobj_release(&rec->hdr);
564 msiobj_release(&view->hdr);
569 UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
572 UINT r = ERROR_SUCCESS;
575 pi = msi_alloc_zero( sizeof(MSIPATCHINFO) );
577 return ERROR_OUTOFMEMORY;
579 pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER );
583 return ERROR_OUTOFMEMORY;
589 msi_free( pi->patchcode );
591 return ERROR_PATCH_PACKAGE_INVALID;
594 p = strchrW( p + 1, '}' );
597 msi_free( pi->patchcode );
599 return ERROR_PATCH_PACKAGE_INVALID;
604 FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
608 TRACE("patch code %s\n", debugstr_w(pi->patchcode));
610 pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
613 msi_free( pi->patchcode );
615 return ERROR_OUTOFMEMORY;
622 struct msi_patch_offset
629 struct msi_patch_offset_list
632 UINT count, min, max;
633 UINT offset_to_apply;
636 static struct msi_patch_offset_list *msi_patch_offset_list_create(void)
638 struct msi_patch_offset_list *pos = msi_alloc(sizeof(struct msi_patch_offset_list));
639 list_init( &pos->files );
640 pos->count = pos->max = 0;
646 static void msi_patch_offset_list_free(struct msi_patch_offset_list *pos)
648 struct msi_patch_offset *po, *po2;
650 LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct msi_patch_offset, entry )
652 msi_free( po->Name );
659 static void msi_patch_offset_get_patches(MSIDATABASE *db, UINT last_sequence, struct msi_patch_offset_list *pos)
664 static const WCHAR query_patch[] = {
665 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
666 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
667 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
669 r = MSI_DatabaseOpenViewW( db, query_patch, &view );
670 if (r != ERROR_SUCCESS)
673 rec = MSI_CreateRecord( 1 );
674 MSI_RecordSetInteger(rec, 1, last_sequence);
676 r = MSI_ViewExecute( view, rec );
677 msiobj_release( &rec->hdr );
678 if (r != ERROR_SUCCESS)
681 while (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
683 UINT sequence = MSI_RecordGetInteger( rec, 2 );
686 * We only use the max/min sequence numbers for now.
689 pos->min = min(pos->min, sequence);
690 pos->max = max(pos->max, sequence);
693 msiobj_release( &rec->hdr );
696 msiobj_release( &view->hdr );
699 static void msi_patch_offset_get_files(MSIDATABASE *db, UINT last_sequence, struct msi_patch_offset_list *pos)
704 static const WCHAR query_files[] = {
705 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
706 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
707 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
709 r = MSI_DatabaseOpenViewW( db, query_files, &view );
710 if (r != ERROR_SUCCESS)
713 rec = MSI_CreateRecord( 1 );
714 MSI_RecordSetInteger(rec, 1, last_sequence);
716 r = MSI_ViewExecute( view, rec );
717 msiobj_release( &rec->hdr );
718 if (r != ERROR_SUCCESS)
721 while (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
723 UINT attributes = MSI_RecordGetInteger( rec, 7 );
724 if (attributes & msidbFileAttributesPatchAdded)
726 struct msi_patch_offset *po = msi_alloc(sizeof(struct msi_patch_offset));
728 po->Name = msi_dup_record_field( rec, 1 );
729 po->Sequence = MSI_RecordGetInteger( rec, 8 );
731 pos->min = min(pos->min, po->Sequence);
732 pos->max = max(pos->max, po->Sequence);
734 list_add_tail( &pos->files, &po->entry );
737 msiobj_release( &rec->hdr );
740 msiobj_release( &view->hdr );
743 static UINT msi_patch_offset_modify_db(MSIDATABASE *db, struct msi_patch_offset_list *pos)
745 static const WCHAR query_files[] =
746 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
747 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','>','=',' ','?',' ',
748 'A','N','D',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
749 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
750 struct msi_patch_offset *po;
755 r = MSI_DatabaseOpenViewW( db, query_files, &view );
756 if (r != ERROR_SUCCESS)
757 return ERROR_SUCCESS;
759 rec = MSI_CreateRecord( 2 );
760 MSI_RecordSetInteger( rec, 1, pos->min );
761 MSI_RecordSetInteger( rec, 2, pos->max );
763 r = MSI_ViewExecute( view, rec );
764 msiobj_release( &rec->hdr );
765 if (r != ERROR_SUCCESS)
768 LIST_FOR_EACH_ENTRY( po, &pos->files, struct msi_patch_offset, entry )
771 while ( (r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS )
773 LPCWSTR file = MSI_RecordGetString( rec, 1 );
776 if (!strcmpiW(file, po->Name))
779 seq = MSI_RecordGetInteger( rec, 8 );
780 MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply );
781 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
782 if (r != ERROR_SUCCESS)
783 ERR("Failed to update offset for file %s.\n", debugstr_w(file));
785 msiobj_release( &rec->hdr );
789 msiobj_release( &rec->hdr );
792 if (r_fetch != ERROR_SUCCESS)
797 msiobj_release( &view->hdr );
799 return ERROR_SUCCESS;
802 static const WCHAR patch_media_query[] = {
803 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
804 'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
805 'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
806 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
819 static UINT msi_add_patch_media( MSIPACKAGE *package, IStorage *patch )
821 static const WCHAR delete_query[] = {
822 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
823 'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0};
824 static const WCHAR insert_query[] = {
825 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ',
826 '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',',
827 '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',',
828 '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ',
829 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
831 MSIRECORD *rec = NULL;
833 struct list media_list;
834 struct patch_media *media, *next;
836 r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view );
837 if (r != ERROR_SUCCESS) return r;
839 r = MSI_ViewExecute( view, 0 );
840 if (r != ERROR_SUCCESS)
842 msiobj_release( &view->hdr );
843 TRACE("query failed %u\n", r);
847 list_init( &media_list );
848 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
850 disk_id = MSI_RecordGetInteger( rec, 1 );
851 TRACE("disk_id %u\n", disk_id);
852 if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
854 msiobj_release( &rec->hdr );
857 if (!(media = msi_alloc( sizeof( *media )))) goto done;
858 media->disk_id = disk_id;
859 media->last_sequence = MSI_RecordGetInteger( rec, 2 );
860 media->prompt = msi_dup_record_field( rec, 3 );
861 media->cabinet = msi_dup_record_field( rec, 4 );
862 media->volume = msi_dup_record_field( rec, 5 );
863 media->source = msi_dup_record_field( rec, 6 );
865 list_add_tail( &media_list, &media->entry );
866 msiobj_release( &rec->hdr );
868 LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry )
870 MSIQUERY *delete_view, *insert_view;
872 r = MSI_DatabaseOpenViewW( package->db, delete_query, &delete_view );
873 if (r != ERROR_SUCCESS) goto done;
875 rec = MSI_CreateRecord( 1 );
876 MSI_RecordSetInteger( rec, 1, media->disk_id );
878 r = MSI_ViewExecute( delete_view, rec );
879 msiobj_release( &delete_view->hdr );
880 msiobj_release( &rec->hdr );
881 if (r != ERROR_SUCCESS) goto done;
883 r = MSI_DatabaseOpenViewW( package->db, insert_query, &insert_view );
884 if (r != ERROR_SUCCESS) goto done;
886 disk_id = package->db->media_transform_disk_id;
887 TRACE("disk id %u\n", disk_id);
888 TRACE("last sequence %u\n", media->last_sequence);
889 TRACE("prompt %s\n", debugstr_w(media->prompt));
890 TRACE("cabinet %s\n", debugstr_w(media->cabinet));
891 TRACE("volume %s\n", debugstr_w(media->volume));
892 TRACE("source %s\n", debugstr_w(media->source));
894 rec = MSI_CreateRecord( 6 );
895 MSI_RecordSetInteger( rec, 1, disk_id );
896 MSI_RecordSetInteger( rec, 2, media->last_sequence );
897 MSI_RecordSetStringW( rec, 3, media->prompt );
898 MSI_RecordSetStringW( rec, 4, media->cabinet );
899 MSI_RecordSetStringW( rec, 5, media->volume );
900 MSI_RecordSetStringW( rec, 6, media->source );
902 r = MSI_ViewExecute( insert_view, rec );
903 msiobj_release( &insert_view->hdr );
904 msiobj_release( &rec->hdr );
905 if (r != ERROR_SUCCESS) goto done;
907 r = msi_add_cabinet_stream( package, disk_id, patch, media->cabinet );
908 if (r != ERROR_SUCCESS) WARN("failed to add cabinet stream %u\n", r);
909 package->db->media_transform_disk_id++;
913 msiobj_release( &view->hdr );
914 LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry )
916 list_remove( &media->entry );
917 msi_free( media->prompt );
918 msi_free( media->cabinet );
919 msi_free( media->volume );
920 msi_free( media->source );
926 static UINT msi_set_patch_offsets(MSIDATABASE *db)
929 MSIRECORD *rec = NULL;
932 r = MSI_DatabaseOpenViewW( db, patch_media_query, &view );
933 if (r != ERROR_SUCCESS)
936 r = MSI_ViewExecute( view, 0 );
937 if (r != ERROR_SUCCESS)
940 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
942 UINT last_sequence = MSI_RecordGetInteger( rec, 2 );
943 struct msi_patch_offset_list *pos;
945 /* FIXME: Set/Check Source field instead? */
946 if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
948 msiobj_release( &rec->hdr );
952 pos = msi_patch_offset_list_create();
954 msi_patch_offset_get_files( db, last_sequence, pos );
955 msi_patch_offset_get_patches( db, last_sequence, pos );
959 UINT offset = db->media_transform_offset - pos->min;
960 last_sequence = offset + pos->max;
963 * This is for the patch table, which is not yet properly transformed.
965 last_sequence += pos->min;
967 pos->offset_to_apply = offset;
968 msi_patch_offset_modify_db( db, pos );
970 MSI_RecordSetInteger( rec, 2, last_sequence );
971 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
972 if (r != ERROR_SUCCESS)
973 ERR("Failed to update Media table entry, expect breakage (%u).\n", r);
975 db->media_transform_offset = last_sequence + 1;
978 msi_patch_offset_list_free( pos );
979 msiobj_release( &rec->hdr );
983 msiobj_release( &view->hdr );
987 UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
989 UINT i, r = ERROR_SUCCESS;
992 /* apply substorage transforms */
993 substorage = msi_split_string( patch->transforms, ';' );
994 for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
996 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
997 if (r == ERROR_SUCCESS)
999 msi_add_patch_media( package, patch_db->storage );
1000 msi_set_patch_offsets( package->db );
1004 msi_free( substorage );
1005 if (r != ERROR_SUCCESS)
1008 msi_set_media_source_prop( package );
1010 patch->state = MSIPATCHSTATE_APPLIED;
1011 list_add_tail( &package->patches, &patch->entry );
1012 return ERROR_SUCCESS;
1015 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
1017 static const WCHAR dotmsp[] = {'.','m','s','p',0};
1018 MSIDATABASE *patch_db = NULL;
1019 WCHAR localfile[MAX_PATH];
1021 MSIPATCHINFO *patch = NULL;
1022 UINT r = ERROR_SUCCESS;
1024 TRACE("%p %s\n", package, debugstr_w( file ) );
1026 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
1027 if ( r != ERROR_SUCCESS )
1029 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
1033 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
1036 msiobj_release( &patch_db->hdr );
1037 return ERROR_FUNCTION_FAILED;
1040 r = msi_check_patch_applicable( package, si );
1041 if (r != ERROR_SUCCESS)
1043 TRACE("patch not applicable\n");
1048 r = msi_parse_patch_summary( si, &patch );
1049 if ( r != ERROR_SUCCESS )
1052 r = msi_get_local_package_name( localfile, dotmsp );
1053 if ( r != ERROR_SUCCESS )
1056 TRACE("copying to local package %s\n", debugstr_w(localfile));
1058 if (!CopyFileW( file, localfile, FALSE ))
1060 ERR("Unable to copy package (%s -> %s) (error %u)\n",
1061 debugstr_w(file), debugstr_w(localfile), GetLastError());
1065 patch->localfile = strdupW( localfile );
1067 r = msi_apply_patch_db( package, patch_db, patch );
1068 if ( r != ERROR_SUCCESS )
1069 WARN("patch failed to apply %u\n", r);
1072 msiobj_release( &si->hdr );
1073 msiobj_release( &patch_db->hdr );
1074 if (patch && r != ERROR_SUCCESS)
1076 if (patch->localfile)
1077 DeleteFileW( patch->localfile );
1079 msi_free( patch->patchcode );
1080 msi_free( patch->transforms );
1081 msi_free( patch->localfile );
1087 /* get the PATCH property, and apply all the patches it specifies */
1088 static UINT msi_apply_patches( MSIPACKAGE *package )
1090 LPWSTR patch_list, *patches;
1091 UINT i, r = ERROR_SUCCESS;
1093 patch_list = msi_dup_property( package->db, szPatch );
1095 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
1097 patches = msi_split_string( patch_list, ';' );
1098 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
1099 r = msi_apply_patch_package( package, patches[i] );
1101 msi_free( patches );
1102 msi_free( patch_list );
1107 static UINT msi_apply_transforms( MSIPACKAGE *package )
1109 static const WCHAR szTransforms[] = {
1110 'T','R','A','N','S','F','O','R','M','S',0 };
1111 LPWSTR xform_list, *xforms;
1112 UINT i, r = ERROR_SUCCESS;
1114 xform_list = msi_dup_property( package->db, szTransforms );
1115 xforms = msi_split_string( xform_list, ';' );
1117 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
1119 if (xforms[i][0] == ':')
1120 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
1125 if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
1128 WCHAR *p = strrchrW( package->PackagePath, '\\' );
1129 DWORD len = p - package->PackagePath + 1;
1131 if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
1134 msi_free( xform_list );
1135 return ERROR_OUTOFMEMORY;
1137 memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
1138 memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
1140 r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
1141 if (transform != xforms[i]) msi_free( transform );
1146 msi_free( xform_list );
1151 static BOOL ui_sequence_exists( MSIPACKAGE *package )
1156 static const WCHAR ExecSeqQuery [] =
1157 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1158 '`','I','n','s','t','a','l','l',
1159 'U','I','S','e','q','u','e','n','c','e','`',
1160 ' ','W','H','E','R','E',' ',
1161 '`','S','e','q','u','e','n','c','e','`',' ',
1162 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1163 '`','S','e','q','u','e','n','c','e','`',0};
1165 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1166 if (rc == ERROR_SUCCESS)
1168 msiobj_release(&view->hdr);
1175 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
1177 LPWSTR source, check;
1179 if (msi_get_property_int( package->db, szInstalled, 0 ))
1183 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
1184 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
1185 RegCloseKey( hkey );
1192 db = msi_dup_property( package->db, szOriginalDatabase );
1194 return ERROR_OUTOFMEMORY;
1196 p = strrchrW( db, '\\' );
1199 p = strrchrW( db, '/' );
1203 return ERROR_SUCCESS;
1208 source = msi_alloc( len * sizeof(WCHAR) );
1209 lstrcpynW( source, db, len );
1213 check = msi_dup_property( package->db, szSourceDir );
1214 if (!check || replace)
1216 UINT r = msi_set_property( package->db, szSourceDir, source );
1217 if (r == ERROR_SUCCESS)
1218 msi_reset_folders( package, TRUE );
1222 check = msi_dup_property( package->db, szSOURCEDIR );
1223 if (!check || replace)
1224 msi_set_property( package->db, szSOURCEDIR, source );
1229 return ERROR_SUCCESS;
1232 static BOOL needs_ui_sequence(MSIPACKAGE *package)
1234 INT level = msi_get_property_int(package->db, szUILevel, 0);
1235 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
1238 UINT msi_set_context(MSIPACKAGE *package)
1242 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
1244 num = msi_get_property_int(package->db, szAllUsers, 0);
1245 if (num == 1 || num == 2)
1246 package->Context = MSIINSTALLCONTEXT_MACHINE;
1248 return ERROR_SUCCESS;
1251 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
1254 LPCWSTR cond, action;
1255 MSIPACKAGE *package = param;
1257 action = MSI_RecordGetString(row,1);
1260 ERR("Error is retrieving action name\n");
1261 return ERROR_FUNCTION_FAILED;
1264 /* check conditions */
1265 cond = MSI_RecordGetString(row,2);
1267 /* this is a hack to skip errors in the condition code */
1268 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
1270 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
1271 return ERROR_SUCCESS;
1274 if (needs_ui_sequence(package))
1275 rc = ACTION_PerformUIAction(package, action, -1);
1277 rc = ACTION_PerformAction(package, action, -1);
1279 msi_dialog_check_messages( NULL );
1281 if (package->CurrentInstallState != ERROR_SUCCESS)
1282 rc = package->CurrentInstallState;
1284 if (rc == ERROR_FUNCTION_NOT_CALLED)
1287 if (rc != ERROR_SUCCESS)
1288 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
1293 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
1297 static const WCHAR query[] =
1298 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1300 ' ','W','H','E','R','E',' ',
1301 '`','S','e','q','u','e','n','c','e','`',' ',
1302 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1303 '`','S','e','q','u','e','n','c','e','`',0};
1305 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
1307 r = MSI_OpenQuery( package->db, &view, query, szTable );
1308 if (r == ERROR_SUCCESS)
1310 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
1311 msiobj_release(&view->hdr);
1317 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
1321 static const WCHAR ExecSeqQuery[] =
1322 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1323 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
1324 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
1325 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
1326 'O','R','D','E','R',' ', 'B','Y',' ',
1327 '`','S','e','q','u','e','n','c','e','`',0 };
1328 static const WCHAR IVQuery[] =
1329 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
1330 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
1331 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
1332 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
1333 ' ','\'', 'I','n','s','t','a','l','l',
1334 'V','a','l','i','d','a','t','e','\'', 0};
1337 if (package->script->ExecuteSequenceRun)
1339 TRACE("Execute Sequence already Run\n");
1340 return ERROR_SUCCESS;
1343 package->script->ExecuteSequenceRun = TRUE;
1345 /* get the sequence number */
1348 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
1350 return ERROR_FUNCTION_FAILED;
1351 seq = MSI_RecordGetInteger(row,1);
1352 msiobj_release(&row->hdr);
1355 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
1356 if (rc == ERROR_SUCCESS)
1358 TRACE("Running the actions\n");
1360 msi_set_property(package->db, szSourceDir, NULL);
1362 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
1363 msiobj_release(&view->hdr);
1369 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
1373 static const WCHAR ExecSeqQuery [] =
1374 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1375 '`','I','n','s','t','a','l','l',
1376 'U','I','S','e','q','u','e','n','c','e','`',
1377 ' ','W','H','E','R','E',' ',
1378 '`','S','e','q','u','e','n','c','e','`',' ',
1379 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1380 '`','S','e','q','u','e','n','c','e','`',0};
1382 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1383 if (rc == ERROR_SUCCESS)
1385 TRACE("Running the actions\n");
1387 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
1388 msiobj_release(&view->hdr);
1394 /********************************************************
1395 * ACTION helper functions and functions that perform the actions
1396 *******************************************************/
1397 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1398 UINT* rc, UINT script, BOOL force )
1403 arc = ACTION_CustomAction(package, action, script, force);
1405 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1413 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
1417 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1419 if (!strcmpW( Component, comp->Component )) return comp;
1424 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
1426 MSIFEATURE *feature;
1428 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1430 if (!strcmpW( Feature, feature->Feature )) return feature;
1435 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
1439 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1441 if (!strcmpW( key, file->File )) return file;
1446 MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key )
1448 MSIFILEPATCH *patch;
1450 /* FIXME: There might be more than one patch */
1451 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
1453 if (!strcmpW( key, patch->File->File )) return patch;
1458 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
1462 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
1464 if (!strcmpW( dir, folder->Directory )) return folder;
1470 * Recursively create all directories in the path.
1471 * shamelessly stolen from setupapi/queue.c
1473 BOOL msi_create_full_path( const WCHAR *path )
1479 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
1480 strcpyW( new_path, path );
1482 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
1483 new_path[len - 1] = 0;
1485 while (!CreateDirectoryW( new_path, NULL ))
1488 DWORD last_error = GetLastError();
1489 if (last_error == ERROR_ALREADY_EXISTS) break;
1490 if (last_error != ERROR_PATH_NOT_FOUND)
1495 if (!(slash = strrchrW( new_path, '\\' )))
1500 len = slash - new_path;
1502 if (!msi_create_full_path( new_path ))
1507 new_path[len] = '\\';
1509 msi_free( new_path );
1513 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
1517 row = MSI_CreateRecord( 4 );
1518 MSI_RecordSetInteger( row, 1, a );
1519 MSI_RecordSetInteger( row, 2, b );
1520 MSI_RecordSetInteger( row, 3, c );
1521 MSI_RecordSetInteger( row, 4, d );
1522 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
1523 msiobj_release( &row->hdr );
1525 msi_dialog_check_messages( NULL );
1528 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
1530 static const WCHAR query[] =
1531 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1532 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
1533 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
1534 WCHAR message[1024];
1538 if (!package->LastAction || strcmpW( package->LastAction, action ))
1540 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
1542 if (MSI_RecordIsNull( row, 3 ))
1544 msiobj_release( &row->hdr );
1547 /* update the cached action format */
1548 msi_free( package->ActionFormat );
1549 package->ActionFormat = msi_dup_record_field( row, 3 );
1550 msi_free( package->LastAction );
1551 package->LastAction = strdupW( action );
1552 msiobj_release( &row->hdr );
1555 MSI_RecordSetStringW( record, 0, package->ActionFormat );
1556 MSI_FormatRecordW( package, record, message, &size );
1557 row = MSI_CreateRecord( 1 );
1558 MSI_RecordSetStringW( row, 1, message );
1559 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
1560 msiobj_release( &row->hdr );
1563 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1565 MSIPACKAGE *package = param;
1566 LPCWSTR dir, component, full_path;
1571 component = MSI_RecordGetString(row, 2);
1573 return ERROR_SUCCESS;
1575 comp = msi_get_loaded_component(package, component);
1577 return ERROR_SUCCESS;
1581 TRACE("component is disabled\n");
1582 return ERROR_SUCCESS;
1585 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
1587 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
1588 comp->Action = comp->Installed;
1589 return ERROR_SUCCESS;
1591 comp->Action = INSTALLSTATE_LOCAL;
1593 dir = MSI_RecordGetString(row,1);
1596 ERR("Unable to get folder id\n");
1597 return ERROR_SUCCESS;
1600 uirow = MSI_CreateRecord(1);
1601 MSI_RecordSetStringW(uirow, 1, dir);
1602 msi_ui_actiondata(package, szCreateFolders, uirow);
1603 msiobj_release(&uirow->hdr);
1605 full_path = msi_get_target_folder( package, dir );
1608 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
1609 return ERROR_SUCCESS;
1611 TRACE("folder is %s\n", debugstr_w(full_path));
1613 folder = msi_get_loaded_folder( package, dir );
1614 if (folder->State == 0) msi_create_full_path( full_path );
1616 return ERROR_SUCCESS;
1619 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1621 static const WCHAR query[] =
1622 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1623 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1627 /* create all the empty folders specified in the CreateFolder table */
1628 rc = MSI_DatabaseOpenViewW(package->db, query, &view );
1629 if (rc != ERROR_SUCCESS)
1630 return ERROR_SUCCESS;
1632 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1633 msiobj_release(&view->hdr);
1638 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
1640 MSIPACKAGE *package = param;
1641 LPCWSTR dir, component, full_path;
1646 component = MSI_RecordGetString(row, 2);
1648 return ERROR_SUCCESS;
1650 comp = msi_get_loaded_component(package, component);
1652 return ERROR_SUCCESS;
1656 TRACE("component is disabled\n");
1657 return ERROR_SUCCESS;
1660 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
1662 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
1663 comp->Action = comp->Installed;
1664 return ERROR_SUCCESS;
1666 comp->Action = INSTALLSTATE_ABSENT;
1668 dir = MSI_RecordGetString( row, 1 );
1671 ERR("Unable to get folder id\n");
1672 return ERROR_SUCCESS;
1675 full_path = msi_get_target_folder( package, dir );
1678 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
1679 return ERROR_SUCCESS;
1681 TRACE("folder is %s\n", debugstr_w(full_path));
1683 uirow = MSI_CreateRecord( 1 );
1684 MSI_RecordSetStringW( uirow, 1, dir );
1685 msi_ui_actiondata( package, szRemoveFolders, uirow );
1686 msiobj_release( &uirow->hdr );
1688 RemoveDirectoryW( full_path );
1689 folder = msi_get_loaded_folder( package, dir );
1691 return ERROR_SUCCESS;
1694 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1696 static const WCHAR query[] =
1697 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1698 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1703 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1704 if (rc != ERROR_SUCCESS)
1705 return ERROR_SUCCESS;
1707 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1708 msiobj_release( &view->hdr );
1713 static UINT load_component( MSIRECORD *row, LPVOID param )
1715 MSIPACKAGE *package = param;
1718 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1720 return ERROR_FUNCTION_FAILED;
1722 list_add_tail( &package->components, &comp->entry );
1724 /* fill in the data */
1725 comp->Component = msi_dup_record_field( row, 1 );
1727 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1729 comp->ComponentId = msi_dup_record_field( row, 2 );
1730 comp->Directory = msi_dup_record_field( row, 3 );
1731 comp->Attributes = MSI_RecordGetInteger(row,4);
1732 comp->Condition = msi_dup_record_field( row, 5 );
1733 comp->KeyPath = msi_dup_record_field( row, 6 );
1735 comp->Installed = INSTALLSTATE_UNKNOWN;
1736 comp->Action = INSTALLSTATE_UNKNOWN;
1737 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
1739 comp->assembly = msi_load_assembly( package, comp );
1740 return ERROR_SUCCESS;
1743 static UINT load_all_components( MSIPACKAGE *package )
1745 static const WCHAR query[] = {
1746 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1747 '`','C','o','m','p','o','n','e','n','t','`',0 };
1751 if (!list_empty(&package->components))
1752 return ERROR_SUCCESS;
1754 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1755 if (r != ERROR_SUCCESS)
1758 if (!msi_init_assembly_caches( package ))
1760 ERR("can't initialize assembly caches\n");
1761 msiobj_release( &view->hdr );
1762 return ERROR_FUNCTION_FAILED;
1765 r = MSI_IterateRecords(view, NULL, load_component, package);
1766 msiobj_release(&view->hdr);
1768 msi_destroy_assembly_caches( package );
1773 MSIPACKAGE *package;
1774 MSIFEATURE *feature;
1777 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1781 cl = msi_alloc( sizeof (*cl) );
1783 return ERROR_NOT_ENOUGH_MEMORY;
1784 cl->component = comp;
1785 list_add_tail( &feature->Components, &cl->entry );
1787 return ERROR_SUCCESS;
1790 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1794 fl = msi_alloc( sizeof(*fl) );
1796 return ERROR_NOT_ENOUGH_MEMORY;
1797 fl->feature = child;
1798 list_add_tail( &parent->Children, &fl->entry );
1800 return ERROR_SUCCESS;
1803 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1805 _ilfs* ilfs = param;
1809 component = MSI_RecordGetString(row,1);
1811 /* check to see if the component is already loaded */
1812 comp = msi_get_loaded_component( ilfs->package, component );
1815 ERR("unknown component %s\n", debugstr_w(component));
1816 return ERROR_FUNCTION_FAILED;
1819 add_feature_component( ilfs->feature, comp );
1820 comp->Enabled = TRUE;
1822 return ERROR_SUCCESS;
1825 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1827 MSIFEATURE *feature;
1832 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1834 if ( !strcmpW( feature->Feature, name ) )
1841 static UINT load_feature(MSIRECORD * row, LPVOID param)
1843 MSIPACKAGE* package = param;
1844 MSIFEATURE* feature;
1845 static const WCHAR Query1[] =
1846 {'S','E','L','E','C','T',' ',
1847 '`','C','o','m','p','o','n','e','n','t','_','`',
1848 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1849 'C','o','m','p','o','n','e','n','t','s','`',' ',
1850 'W','H','E','R','E',' ',
1851 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1856 /* fill in the data */
1858 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1860 return ERROR_NOT_ENOUGH_MEMORY;
1862 list_init( &feature->Children );
1863 list_init( &feature->Components );
1865 feature->Feature = msi_dup_record_field( row, 1 );
1867 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1869 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1870 feature->Title = msi_dup_record_field( row, 3 );
1871 feature->Description = msi_dup_record_field( row, 4 );
1873 if (!MSI_RecordIsNull(row,5))
1874 feature->Display = MSI_RecordGetInteger(row,5);
1876 feature->Level= MSI_RecordGetInteger(row,6);
1877 feature->Directory = msi_dup_record_field( row, 7 );
1878 feature->Attributes = MSI_RecordGetInteger(row,8);
1880 feature->Installed = INSTALLSTATE_UNKNOWN;
1881 feature->Action = INSTALLSTATE_UNKNOWN;
1882 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1884 list_add_tail( &package->features, &feature->entry );
1886 /* load feature components */
1888 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1889 if (rc != ERROR_SUCCESS)
1890 return ERROR_SUCCESS;
1892 ilfs.package = package;
1893 ilfs.feature = feature;
1895 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1896 msiobj_release(&view->hdr);
1898 return ERROR_SUCCESS;
1901 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1903 MSIPACKAGE* package = param;
1904 MSIFEATURE *parent, *child;
1906 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1908 return ERROR_FUNCTION_FAILED;
1910 if (!child->Feature_Parent)
1911 return ERROR_SUCCESS;
1913 parent = find_feature_by_name( package, child->Feature_Parent );
1915 return ERROR_FUNCTION_FAILED;
1917 add_feature_child( parent, child );
1918 return ERROR_SUCCESS;
1921 static UINT load_all_features( MSIPACKAGE *package )
1923 static const WCHAR query[] = {
1924 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1925 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1926 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1930 if (!list_empty(&package->features))
1931 return ERROR_SUCCESS;
1933 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1934 if (r != ERROR_SUCCESS)
1937 r = MSI_IterateRecords( view, NULL, load_feature, package );
1938 if (r != ERROR_SUCCESS)
1941 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1942 msiobj_release( &view->hdr );
1947 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1958 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1960 static const WCHAR query[] = {
1961 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1962 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1963 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1964 MSIQUERY *view = NULL;
1965 MSIRECORD *row = NULL;
1968 TRACE("%s\n", debugstr_w(file->File));
1970 r = MSI_OpenQuery(package->db, &view, query, file->File);
1971 if (r != ERROR_SUCCESS)
1974 r = MSI_ViewExecute(view, NULL);
1975 if (r != ERROR_SUCCESS)
1978 r = MSI_ViewFetch(view, &row);
1979 if (r != ERROR_SUCCESS)
1982 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1983 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1984 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1985 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1986 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1989 if (view) msiobj_release(&view->hdr);
1990 if (row) msiobj_release(&row->hdr);
1994 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1997 static const WCHAR query[] = {
1998 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1999 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2000 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
2002 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
2005 WARN("query failed\n");
2006 return ERROR_FUNCTION_FAILED;
2009 file->disk_id = MSI_RecordGetInteger( row, 1 );
2010 msiobj_release( &row->hdr );
2011 return ERROR_SUCCESS;
2014 static UINT load_file(MSIRECORD *row, LPVOID param)
2016 MSIPACKAGE* package = param;
2020 /* fill in the data */
2022 file = msi_alloc_zero( sizeof (MSIFILE) );
2024 return ERROR_NOT_ENOUGH_MEMORY;
2026 file->File = msi_dup_record_field( row, 1 );
2028 component = MSI_RecordGetString( row, 2 );
2029 file->Component = msi_get_loaded_component( package, component );
2031 if (!file->Component)
2033 WARN("Component not found: %s\n", debugstr_w(component));
2034 msi_free(file->File);
2036 return ERROR_SUCCESS;
2039 file->FileName = msi_dup_record_field( row, 3 );
2040 msi_reduce_to_long_filename( file->FileName );
2042 file->ShortName = msi_dup_record_field( row, 3 );
2043 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
2045 file->FileSize = MSI_RecordGetInteger( row, 4 );
2046 file->Version = msi_dup_record_field( row, 5 );
2047 file->Language = msi_dup_record_field( row, 6 );
2048 file->Attributes = MSI_RecordGetInteger( row, 7 );
2049 file->Sequence = MSI_RecordGetInteger( row, 8 );
2051 file->state = msifs_invalid;
2053 /* if the compressed bits are not set in the file attributes,
2054 * then read the information from the package word count property
2056 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
2058 file->IsCompressed = FALSE;
2060 else if (file->Attributes &
2061 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
2063 file->IsCompressed = TRUE;
2065 else if (file->Attributes & msidbFileAttributesNoncompressed)
2067 file->IsCompressed = FALSE;
2071 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
2074 load_file_hash(package, file);
2075 load_file_disk_id(package, file);
2077 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
2079 list_add_tail( &package->files, &file->entry );
2081 return ERROR_SUCCESS;
2084 static UINT load_all_files(MSIPACKAGE *package)
2088 static const WCHAR Query[] =
2089 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2090 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
2091 '`','S','e','q','u','e','n','c','e','`', 0};
2093 if (!list_empty(&package->files))
2094 return ERROR_SUCCESS;
2096 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2097 if (rc != ERROR_SUCCESS)
2098 return ERROR_SUCCESS;
2100 rc = MSI_IterateRecords(view, NULL, load_file, package);
2101 msiobj_release(&view->hdr);
2103 return ERROR_SUCCESS;
2106 static UINT load_media( MSIRECORD *row, LPVOID param )
2108 MSIPACKAGE *package = param;
2109 UINT disk_id = MSI_RecordGetInteger( row, 1 );
2110 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
2112 /* FIXME: load external cabinets and directory sources too */
2113 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
2114 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
2115 return ERROR_SUCCESS;
2118 static UINT load_all_media( MSIPACKAGE *package )
2120 static const WCHAR query[] =
2121 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
2122 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
2126 if (!list_empty( &package->cabinet_streams )) return ERROR_SUCCESS;
2128 r = MSI_DatabaseOpenViewW( package->db, query, &view );
2129 if (r != ERROR_SUCCESS) return ERROR_SUCCESS;
2131 MSI_IterateRecords( view, NULL, load_media, package );
2132 msiobj_release( &view->hdr );
2133 return ERROR_SUCCESS;
2136 static UINT load_patch(MSIRECORD *row, LPVOID param)
2138 MSIPACKAGE *package = param;
2139 MSIFILEPATCH *patch;
2142 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
2144 return ERROR_NOT_ENOUGH_MEMORY;
2146 file_key = msi_dup_record_field( row, 1 );
2147 patch->File = msi_get_loaded_file( package, file_key );
2152 ERR("Failed to find target for patch in File table\n");
2154 return ERROR_FUNCTION_FAILED;
2157 patch->Sequence = MSI_RecordGetInteger( row, 2 );
2159 /* FIXME: The database should be properly transformed */
2160 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
2162 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
2163 patch->Attributes = MSI_RecordGetInteger( row, 4 );
2164 patch->IsApplied = FALSE;
2167 * Header field - for patch validation.
2168 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
2171 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
2173 list_add_tail( &package->filepatches, &patch->entry );
2175 return ERROR_SUCCESS;
2178 static UINT load_all_patches(MSIPACKAGE *package)
2182 static const WCHAR Query[] =
2183 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2184 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
2185 '`','S','e','q','u','e','n','c','e','`',0};
2187 if (!list_empty(&package->filepatches))
2188 return ERROR_SUCCESS;
2190 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2191 if (rc != ERROR_SUCCESS)
2192 return ERROR_SUCCESS;
2194 rc = MSI_IterateRecords(view, NULL, load_patch, package);
2195 msiobj_release(&view->hdr);
2197 return ERROR_SUCCESS;
2200 static UINT load_folder( MSIRECORD *row, LPVOID param )
2202 MSIPACKAGE *package = param;
2203 static WCHAR szEmpty[] = { 0 };
2204 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
2207 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
2208 list_init( &folder->children );
2209 folder->Directory = msi_dup_record_field( row, 1 );
2210 folder->Parent = msi_dup_record_field( row, 2 );
2211 p = msi_dup_record_field(row, 3);
2213 TRACE("%s\n", debugstr_w(folder->Directory));
2215 /* split src and target dir */
2217 src_short = folder_split_path( p, ':' );
2219 /* split the long and short paths */
2220 tgt_long = folder_split_path( tgt_short, '|' );
2221 src_long = folder_split_path( src_short, '|' );
2223 /* check for no-op dirs */
2224 if (tgt_short && !strcmpW( szDot, tgt_short ))
2225 tgt_short = szEmpty;
2226 if (src_short && !strcmpW( szDot, src_short ))
2227 src_short = szEmpty;
2230 tgt_long = tgt_short;
2233 src_short = tgt_short;
2234 src_long = tgt_long;
2238 src_long = src_short;
2240 /* FIXME: use the target short path too */
2241 folder->TargetDefault = strdupW(tgt_long);
2242 folder->SourceShortPath = strdupW(src_short);
2243 folder->SourceLongPath = strdupW(src_long);
2246 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
2247 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
2248 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
2250 list_add_tail( &package->folders, &folder->entry );
2251 return ERROR_SUCCESS;
2254 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
2258 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
2260 list_add_tail( &parent->children, &fl->entry );
2261 return ERROR_SUCCESS;
2264 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
2266 MSIPACKAGE *package = param;
2267 MSIFOLDER *parent, *child;
2269 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
2270 return ERROR_FUNCTION_FAILED;
2272 if (!child->Parent) return ERROR_SUCCESS;
2274 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
2275 return ERROR_FUNCTION_FAILED;
2277 return add_folder_child( parent, child );
2280 static UINT load_all_folders( MSIPACKAGE *package )
2282 static const WCHAR query[] = {
2283 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
2284 '`','D','i','r','e','c','t','o','r','y','`',0 };
2288 if (!list_empty(&package->folders))
2289 return ERROR_SUCCESS;
2291 r = MSI_DatabaseOpenViewW( package->db, query, &view );
2292 if (r != ERROR_SUCCESS)
2295 r = MSI_IterateRecords( view, NULL, load_folder, package );
2296 if (r != ERROR_SUCCESS)
2298 msiobj_release( &view->hdr );
2301 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
2302 msiobj_release( &view->hdr );
2306 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
2308 msi_set_property( package->db, szCostingComplete, szZero );
2309 msi_set_property( package->db, szRootDrive, szCRoot );
2311 load_all_folders( package );
2312 load_all_components( package );
2313 load_all_features( package );
2314 load_all_files( package );
2315 load_all_patches( package );
2316 load_all_media( package );
2318 return ERROR_SUCCESS;
2321 static UINT execute_script(MSIPACKAGE *package, UINT script )
2324 UINT rc = ERROR_SUCCESS;
2326 TRACE("Executing Script %i\n",script);
2328 if (!package->script)
2330 ERR("no script!\n");
2331 return ERROR_FUNCTION_FAILED;
2334 for (i = 0; i < package->script->ActionCount[script]; i++)
2337 action = package->script->Actions[script][i];
2338 ui_actionstart(package, action);
2339 TRACE("Executing Action (%s)\n",debugstr_w(action));
2340 rc = ACTION_PerformAction(package, action, script);
2341 if (rc != ERROR_SUCCESS)
2344 msi_free_action_script(package, script);
2348 static UINT ACTION_FileCost(MSIPACKAGE *package)
2350 return ERROR_SUCCESS;
2353 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
2358 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
2360 if (!comp->ComponentId) continue;
2362 r = MsiQueryComponentStateW( package->ProductCode, NULL,
2363 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
2365 if (r != ERROR_SUCCESS)
2366 r = MsiQueryComponentStateW( package->ProductCode, NULL,
2367 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
2369 if (r != ERROR_SUCCESS)
2370 r = MsiQueryComponentStateW( package->ProductCode, NULL,
2371 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
2373 if (r != ERROR_SUCCESS)
2374 comp->Installed = INSTALLSTATE_ABSENT;
2378 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
2380 MSIFEATURE *feature;
2382 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2384 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
2386 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
2387 feature->Installed = INSTALLSTATE_ABSENT;
2389 feature->Installed = state;
2393 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
2395 return (feature->Level > 0 && feature->Level <= level);
2398 static BOOL process_state_property(MSIPACKAGE* package, int level,
2399 LPCWSTR property, INSTALLSTATE state)
2402 MSIFEATURE *feature;
2404 override = msi_dup_property( package->db, property );
2408 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2410 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
2413 if (!strcmpW(property, szReinstall)) state = feature->Installed;
2415 if (!strcmpiW( override, szAll ))
2417 if (feature->Installed != state)
2419 feature->Action = state;
2420 feature->ActionRequest = state;
2425 LPWSTR ptr = override;
2426 LPWSTR ptr2 = strchrW(override,',');
2430 int len = ptr2 - ptr;
2432 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
2433 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
2435 if (feature->Installed != state)
2437 feature->Action = state;
2438 feature->ActionRequest = state;
2445 ptr2 = strchrW(ptr,',');
2456 static BOOL process_overrides( MSIPACKAGE *package, int level )
2458 static const WCHAR szAddLocal[] =
2459 {'A','D','D','L','O','C','A','L',0};
2460 static const WCHAR szAddSource[] =
2461 {'A','D','D','S','O','U','R','C','E',0};
2462 static const WCHAR szAdvertise[] =
2463 {'A','D','V','E','R','T','I','S','E',0};
2466 /* all these activation/deactivation things happen in order and things
2467 * later on the list override things earlier on the list.
2469 * 0 INSTALLLEVEL processing
2482 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
2483 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
2484 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
2485 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
2486 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
2489 msi_set_property( package->db, szPreselected, szOne );
2494 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
2497 MSICOMPONENT* component;
2498 MSIFEATURE *feature;
2500 TRACE("Checking Install Level\n");
2502 level = msi_get_property_int(package->db, szInstallLevel, 1);
2504 if (!msi_get_property_int( package->db, szPreselected, 0 ))
2506 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2508 if (!is_feature_selected( feature, level )) continue;
2510 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
2512 if (feature->Attributes & msidbFeatureAttributesFavorSource)
2514 feature->Action = INSTALLSTATE_SOURCE;
2515 feature->ActionRequest = INSTALLSTATE_SOURCE;
2517 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
2519 feature->Action = INSTALLSTATE_ADVERTISED;
2520 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
2524 feature->Action = INSTALLSTATE_LOCAL;
2525 feature->ActionRequest = INSTALLSTATE_LOCAL;
2529 /* disable child features of unselected parent or follow parent */
2530 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2534 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
2536 if (!is_feature_selected( feature, level ))
2538 fl->feature->Action = INSTALLSTATE_UNKNOWN;
2539 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
2541 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
2543 fl->feature->Action = feature->Action;
2544 fl->feature->ActionRequest = feature->ActionRequest;
2549 else /* preselected */
2551 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2553 if (!is_feature_selected( feature, level )) continue;
2555 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
2557 if (feature->Installed == INSTALLSTATE_ABSENT)
2559 feature->Action = INSTALLSTATE_UNKNOWN;
2560 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
2564 feature->Action = feature->Installed;
2565 feature->ActionRequest = feature->Installed;
2569 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2573 if (!is_feature_selected( feature, level )) continue;
2575 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
2577 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
2579 fl->feature->Action = feature->Action;
2580 fl->feature->ActionRequest = feature->ActionRequest;
2586 /* now we want to set component state based based on feature state */
2587 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2591 TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
2592 debugstr_w(feature->Feature), feature->Level, feature->Installed,
2593 feature->ActionRequest, feature->Action);
2595 if (!is_feature_selected( feature, level )) continue;
2597 /* features with components that have compressed files are made local */
2598 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2600 if (cl->component->ForceLocalState &&
2601 feature->ActionRequest == INSTALLSTATE_SOURCE)
2603 feature->Action = INSTALLSTATE_LOCAL;
2604 feature->ActionRequest = INSTALLSTATE_LOCAL;
2609 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2611 component = cl->component;
2613 switch (feature->ActionRequest)
2615 case INSTALLSTATE_ABSENT:
2616 component->anyAbsent = 1;
2618 case INSTALLSTATE_ADVERTISED:
2619 component->hasAdvertiseFeature = 1;
2621 case INSTALLSTATE_SOURCE:
2622 component->hasSourceFeature = 1;
2624 case INSTALLSTATE_LOCAL:
2625 component->hasLocalFeature = 1;
2627 case INSTALLSTATE_DEFAULT:
2628 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
2629 component->hasAdvertiseFeature = 1;
2630 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
2631 component->hasSourceFeature = 1;
2633 component->hasLocalFeature = 1;
2641 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2643 /* check if it's local or source */
2644 if (!(component->Attributes & msidbComponentAttributesOptional) &&
2645 (component->hasLocalFeature || component->hasSourceFeature))
2647 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
2648 !component->ForceLocalState)
2650 component->Action = INSTALLSTATE_SOURCE;
2651 component->ActionRequest = INSTALLSTATE_SOURCE;
2655 component->Action = INSTALLSTATE_LOCAL;
2656 component->ActionRequest = INSTALLSTATE_LOCAL;
2661 /* if any feature is local, the component must be local too */
2662 if (component->hasLocalFeature)
2664 component->Action = INSTALLSTATE_LOCAL;
2665 component->ActionRequest = INSTALLSTATE_LOCAL;
2668 if (component->hasSourceFeature)
2670 component->Action = INSTALLSTATE_SOURCE;
2671 component->ActionRequest = INSTALLSTATE_SOURCE;
2674 if (component->hasAdvertiseFeature)
2676 component->Action = INSTALLSTATE_ADVERTISED;
2677 component->ActionRequest = INSTALLSTATE_ADVERTISED;
2680 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
2681 if (component->anyAbsent &&
2682 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
2684 component->Action = INSTALLSTATE_ABSENT;
2685 component->ActionRequest = INSTALLSTATE_ABSENT;
2689 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2691 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
2693 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
2694 component->Action = INSTALLSTATE_LOCAL;
2695 component->ActionRequest = INSTALLSTATE_LOCAL;
2698 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
2699 component->Installed == INSTALLSTATE_SOURCE &&
2700 component->hasSourceFeature)
2702 component->Action = INSTALLSTATE_UNKNOWN;
2703 component->ActionRequest = INSTALLSTATE_UNKNOWN;
2706 TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
2707 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
2710 return ERROR_SUCCESS;
2713 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2715 MSIPACKAGE *package = param;
2717 MSIFEATURE *feature;
2719 name = MSI_RecordGetString( row, 1 );
2721 feature = msi_get_loaded_feature( package, name );
2723 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2727 Condition = MSI_RecordGetString(row,3);
2729 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2731 int level = MSI_RecordGetInteger(row,2);
2732 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2733 feature->Level = level;
2736 return ERROR_SUCCESS;
2739 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2741 static const WCHAR name[] = {'\\',0};
2742 VS_FIXEDFILEINFO *ptr, *ret;
2744 DWORD versize, handle;
2747 TRACE("%s\n", debugstr_w(filename));
2749 versize = GetFileVersionInfoSizeW( filename, &handle );
2753 version = msi_alloc( versize );
2757 GetFileVersionInfoW( filename, 0, versize, version );
2759 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2761 msi_free( version );
2765 ret = msi_alloc( sz );
2766 memcpy( ret, ptr, sz );
2768 msi_free( version );
2772 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2776 msi_parse_version_string( version, &ms, &ls );
2778 if (fi->dwFileVersionMS > ms) return 1;
2779 else if (fi->dwFileVersionMS < ms) return -1;
2780 else if (fi->dwFileVersionLS > ls) return 1;
2781 else if (fi->dwFileVersionLS < ls) return -1;
2785 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2789 msi_parse_version_string( ver1, &ms1, NULL );
2790 msi_parse_version_string( ver2, &ms2, NULL );
2792 if (ms1 > ms2) return 1;
2793 else if (ms1 < ms2) return -1;
2797 DWORD msi_get_disk_file_size( LPCWSTR filename )
2802 TRACE("%s\n", debugstr_w(filename));
2804 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2805 if (file == INVALID_HANDLE_VALUE)
2806 return INVALID_FILE_SIZE;
2808 size = GetFileSize( file, NULL );
2809 CloseHandle( file );
2813 BOOL msi_file_hash_matches( MSIFILE *file )
2816 MSIFILEHASHINFO hash;
2818 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2819 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2820 if (r != ERROR_SUCCESS)
2823 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2826 static WCHAR *get_temp_dir( void )
2829 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2831 GetTempPathW( MAX_PATH, tmp );
2834 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2835 if (CreateDirectoryW( dir, NULL )) break;
2837 return strdupW( dir );
2841 * msi_build_directory_name()
2843 * This function is to save messing round with directory names
2844 * It handles adding backslashes between path segments,
2845 * and can add \ at the end of the directory name if told to.
2847 * It takes a variable number of arguments.
2848 * It always allocates a new string for the result, so make sure
2849 * to free the return value when finished with it.
2851 * The first arg is the number of path segments that follow.
2852 * The arguments following count are a list of path segments.
2853 * A path segment may be NULL.
2855 * Path segments will be added with a \ separating them.
2856 * A \ will not be added after the last segment, however if the
2857 * last segment is NULL, then the last character will be a \
2859 WCHAR *msi_build_directory_name( DWORD count, ... )
2865 va_start( va, count );
2866 for (i = 0; i < count; i++)
2868 const WCHAR *str = va_arg( va, const WCHAR * );
2869 if (str) sz += strlenW( str ) + 1;
2873 dir = msi_alloc( sz * sizeof(WCHAR) );
2876 va_start( va, count );
2877 for (i = 0; i < count; i++)
2879 const WCHAR *str = va_arg( va, const WCHAR * );
2881 strcatW( dir, str );
2882 if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2888 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2890 MSIASSEMBLY *assembly = file->Component->assembly;
2892 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2894 msi_free( file->TargetPath );
2895 if (assembly && !assembly->application)
2897 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2898 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2899 msi_track_tempfile( package, file->TargetPath );
2903 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2904 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2907 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2910 static UINT calculate_file_cost( MSIPACKAGE *package )
2912 VS_FIXEDFILEINFO *file_version;
2913 WCHAR *font_version;
2916 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2918 MSICOMPONENT *comp = file->Component;
2921 if (!comp->Enabled) continue;
2923 if (file->IsCompressed)
2924 comp->ForceLocalState = TRUE;
2926 set_target_path( package, file );
2928 if ((comp->assembly && !comp->assembly->installed) ||
2929 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2931 comp->Cost += file->FileSize;
2934 file_size = msi_get_disk_file_size( file->TargetPath );
2938 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2940 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2942 comp->Cost += file->FileSize - file_size;
2944 msi_free( file_version );
2947 else if ((font_version = font_version_from_file( file->TargetPath )))
2949 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2951 comp->Cost += file->FileSize - file_size;
2953 msi_free( font_version );
2957 if (file_size != file->FileSize)
2959 comp->Cost += file->FileSize - file_size;
2962 return ERROR_SUCCESS;
2965 void msi_clean_path( WCHAR *p )
2972 /* copy until the end of the string or a space */
2973 while (*p != ' ' && (*q = *p))
2976 /* reduce many backslashes to one */
2977 if (*p != '\\' || *q != '\\')
2981 /* quit at the end of the string */
2985 /* count the number of spaces */
2990 /* if it's leading or trailing space, skip it */
2991 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2993 else /* copy n spaces */
2994 while (n && (*q++ = *p++)) n--;
2998 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
3001 MSIFOLDER *folder, *parent, *child;
3004 TRACE("resolving %s\n", debugstr_w(name));
3006 if (!(folder = msi_get_loaded_folder( package, name ))) return;
3008 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
3010 if (!load_prop || !(path = msi_dup_property( package->db, szTargetDir )))
3012 path = msi_dup_property( package->db, szRootDrive );
3015 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
3017 parent = msi_get_loaded_folder( package, folder->Parent );
3018 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
3020 msi_clean_path( path );
3021 msi_set_property( package->db, folder->Directory, path );
3022 msi_free( folder->ResolvedTarget );
3023 folder->ResolvedTarget = path;
3025 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
3028 msi_resolve_target_folder( package, child->Directory, load_prop );
3030 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
3033 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
3035 static const WCHAR condition_query[] =
3036 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','C','o','n','d','i','t','i','o','n','`',0};
3037 static const WCHAR szOutOfDiskSpace[] =
3038 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
3040 UINT rc = ERROR_SUCCESS;
3044 TRACE("Building directory properties\n");
3045 msi_resolve_target_folder( package, szTargetDir, TRUE );
3047 TRACE("Evaluating component conditions\n");
3048 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3050 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
3052 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
3053 comp->Enabled = FALSE;
3056 comp->Enabled = TRUE;
3059 /* read components states from the registry */
3060 ACTION_GetComponentInstallStates(package);
3061 ACTION_GetFeatureInstallStates(package);
3063 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
3065 TRACE("Evaluating feature conditions\n");
3067 rc = MSI_DatabaseOpenViewW( package->db, condition_query, &view );
3068 if (rc == ERROR_SUCCESS)
3070 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
3071 msiobj_release( &view->hdr );
3075 TRACE("Calculating file cost\n");
3076 calculate_file_cost( package );
3078 msi_set_property( package->db, szCostingComplete, szOne );
3079 /* set default run level if not set */
3080 level = msi_dup_property( package->db, szInstallLevel );
3082 msi_set_property( package->db, szInstallLevel, szOne );
3085 /* FIXME: check volume disk space */
3086 msi_set_property( package->db, szOutOfDiskSpace, szZero );
3088 return MSI_SetFeatureStates(package);
3091 /* OK this value is "interpreted" and then formatted based on the
3092 first few characters */
3093 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
3098 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
3104 LPWSTR deformated = NULL;
3107 deformat_string(package, &value[2], &deformated);
3109 /* binary value type */
3113 *size = (strlenW(ptr)/2)+1;
3115 *size = strlenW(ptr)/2;
3117 data = msi_alloc(*size);
3123 /* if uneven pad with a zero in front */
3129 data[count] = (BYTE)strtol(byte,NULL,0);
3131 TRACE("Uneven byte count\n");
3139 data[count] = (BYTE)strtol(byte,NULL,0);
3142 msi_free(deformated);
3144 TRACE("Data %i bytes(%i)\n",*size,count);
3151 deformat_string(package, &value[1], &deformated);
3154 *size = sizeof(DWORD);
3155 data = msi_alloc(*size);
3161 if ( (*p < '0') || (*p > '9') )
3167 if (deformated[0] == '-')
3170 TRACE("DWORD %i\n",*(LPDWORD)data);
3172 msi_free(deformated);
3177 static const WCHAR szMulti[] = {'[','~',']',0};
3186 *type=REG_EXPAND_SZ;
3194 if (strstrW(value, szMulti))
3195 *type = REG_MULTI_SZ;
3197 /* remove initial delimiter */
3198 if (!strncmpW(value, szMulti, 3))
3201 *size = deformat_string(package, ptr,(LPWSTR*)&data);
3203 /* add double NULL terminator */
3204 if (*type == REG_MULTI_SZ)
3206 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
3207 data = msi_realloc_zero(data, *size);
3213 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
3220 if (msi_get_property_int( package->db, szAllUsers, 0 ))
3222 *root_key = HKEY_LOCAL_MACHINE;
3227 *root_key = HKEY_CURRENT_USER;
3232 *root_key = HKEY_CLASSES_ROOT;
3236 *root_key = HKEY_CURRENT_USER;
3240 *root_key = HKEY_LOCAL_MACHINE;
3244 *root_key = HKEY_USERS;
3248 ERR("Unknown root %i\n", root);
3255 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
3257 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
3258 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
3260 if (is_64bit && package->platform == PLATFORM_INTEL &&
3261 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
3266 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
3267 if (!(path_32node = msi_alloc( size ))) return NULL;
3269 memcpy( path_32node, path, len * sizeof(WCHAR) );
3270 strcpyW( path_32node + len, szWow6432Node );
3271 strcatW( path_32node, szBackSlash );
3272 strcatW( path_32node, path + len );
3276 return strdupW( path );
3279 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
3281 MSIPACKAGE *package = param;
3282 LPSTR value_data = NULL;
3283 HKEY root_key, hkey;
3285 LPWSTR deformated, uikey, keypath;
3286 LPCWSTR szRoot, component, name, key, value;
3290 BOOL check_first = FALSE;
3293 msi_ui_progress( package, 2, 0, 0, 0 );
3295 component = MSI_RecordGetString(row, 6);
3296 comp = msi_get_loaded_component(package,component);
3298 return ERROR_SUCCESS;
3302 TRACE("component is disabled\n");
3303 return ERROR_SUCCESS;
3306 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3308 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3309 comp->Action = comp->Installed;
3310 return ERROR_SUCCESS;
3312 comp->Action = INSTALLSTATE_LOCAL;
3314 name = MSI_RecordGetString(row, 4);
3315 if( MSI_RecordIsNull(row,5) && name )
3317 /* null values can have special meanings */
3318 if (name[0]=='-' && name[1] == 0)
3319 return ERROR_SUCCESS;
3320 else if ((name[0]=='+' && name[1] == 0) ||
3321 (name[0] == '*' && name[1] == 0))
3326 root = MSI_RecordGetInteger(row,2);
3327 key = MSI_RecordGetString(row, 3);
3329 szRoot = get_root_key( package, root, &root_key );
3331 return ERROR_SUCCESS;
3333 deformat_string(package, key , &deformated);
3334 size = strlenW(deformated) + strlenW(szRoot) + 1;
3335 uikey = msi_alloc(size*sizeof(WCHAR));
3336 strcpyW(uikey,szRoot);
3337 strcatW(uikey,deformated);
3339 keypath = get_keypath( package, root_key, deformated );
3340 msi_free( deformated );
3341 if (RegCreateKeyW( root_key, keypath, &hkey ))
3343 ERR("Could not create key %s\n", debugstr_w(keypath));
3346 return ERROR_SUCCESS;
3349 value = MSI_RecordGetString(row,5);
3351 value_data = parse_value(package, value, &type, &size);
3354 value_data = (LPSTR)strdupW(szEmpty);
3355 size = sizeof(szEmpty);
3359 deformat_string(package, name, &deformated);
3363 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
3365 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
3370 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
3371 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
3373 TRACE("value %s of %s checked already exists\n",
3374 debugstr_w(deformated), debugstr_w(uikey));
3378 TRACE("Checked and setting value %s of %s\n",
3379 debugstr_w(deformated), debugstr_w(uikey));
3380 if (deformated || size)
3381 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
3386 uirow = MSI_CreateRecord(3);
3387 MSI_RecordSetStringW(uirow,2,deformated);
3388 MSI_RecordSetStringW(uirow,1,uikey);
3389 if (type == REG_SZ || type == REG_EXPAND_SZ)
3390 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
3391 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
3392 msiobj_release( &uirow->hdr );
3394 msi_free(value_data);
3395 msi_free(deformated);
3399 return ERROR_SUCCESS;
3402 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
3406 static const WCHAR ExecSeqQuery[] =
3407 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3408 '`','R','e','g','i','s','t','r','y','`',0 };
3410 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3411 if (rc != ERROR_SUCCESS)
3412 return ERROR_SUCCESS;
3414 /* increment progress bar each time action data is sent */
3415 msi_ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
3417 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
3418 msiobj_release(&view->hdr);
3422 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
3426 DWORD num_subkeys, num_values;
3430 if ((res = RegDeleteTreeW( hkey_root, key )))
3432 TRACE("Failed to delete key %s (%d)\n", debugstr_w(key), res);
3437 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
3439 if ((res = RegDeleteValueW( hkey, value )))
3441 TRACE("Failed to delete value %s (%d)\n", debugstr_w(value), res);
3443 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3444 NULL, NULL, NULL, NULL );
3445 RegCloseKey( hkey );
3446 if (!res && !num_subkeys && !num_values)
3448 TRACE("Removing empty key %s\n", debugstr_w(key));
3449 RegDeleteKeyW( hkey_root, key );
3453 TRACE("Failed to open key %s (%d)\n", debugstr_w(key), res);
3457 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3459 MSIPACKAGE *package = param;
3460 LPCWSTR component, name, key_str, root_key_str;
3461 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3464 BOOL delete_key = FALSE;
3469 msi_ui_progress( package, 2, 0, 0, 0 );
3471 component = MSI_RecordGetString( row, 6 );
3472 comp = msi_get_loaded_component( package, component );
3474 return ERROR_SUCCESS;
3478 TRACE("component is disabled\n");
3479 return ERROR_SUCCESS;
3482 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3484 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
3485 comp->Action = comp->Installed;
3486 return ERROR_SUCCESS;
3488 comp->Action = INSTALLSTATE_ABSENT;
3490 name = MSI_RecordGetString( row, 4 );
3491 if (MSI_RecordIsNull( row, 5 ) && name )
3493 if (name[0] == '+' && !name[1])
3494 return ERROR_SUCCESS;
3495 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
3502 root = MSI_RecordGetInteger( row, 2 );
3503 key_str = MSI_RecordGetString( row, 3 );
3505 root_key_str = get_root_key( package, root, &hkey_root );
3507 return ERROR_SUCCESS;
3509 deformat_string( package, key_str, &deformated_key );
3510 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3511 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3512 strcpyW( ui_key_str, root_key_str );
3513 strcatW( ui_key_str, deformated_key );
3515 deformat_string( package, name, &deformated_name );
3517 keypath = get_keypath( package, hkey_root, deformated_key );
3518 msi_free( deformated_key );
3519 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
3520 msi_free( keypath );
3522 uirow = MSI_CreateRecord( 2 );
3523 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3524 MSI_RecordSetStringW( uirow, 2, deformated_name );
3526 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3527 msiobj_release( &uirow->hdr );
3529 msi_free( ui_key_str );
3530 msi_free( deformated_name );
3531 return ERROR_SUCCESS;
3534 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3536 MSIPACKAGE *package = param;
3537 LPCWSTR component, name, key_str, root_key_str;
3538 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3541 BOOL delete_key = FALSE;
3546 msi_ui_progress( package, 2, 0, 0, 0 );
3548 component = MSI_RecordGetString( row, 5 );
3549 comp = msi_get_loaded_component( package, component );
3551 return ERROR_SUCCESS;
3555 TRACE("component is disabled\n");
3556 return ERROR_SUCCESS;
3559 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3561 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3562 comp->Action = comp->Installed;
3563 return ERROR_SUCCESS;
3565 comp->Action = INSTALLSTATE_LOCAL;
3567 if ((name = MSI_RecordGetString( row, 4 )))
3569 if (name[0] == '-' && !name[1])
3576 root = MSI_RecordGetInteger( row, 2 );
3577 key_str = MSI_RecordGetString( row, 3 );
3579 root_key_str = get_root_key( package, root, &hkey_root );
3581 return ERROR_SUCCESS;
3583 deformat_string( package, key_str, &deformated_key );
3584 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3585 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3586 strcpyW( ui_key_str, root_key_str );
3587 strcatW( ui_key_str, deformated_key );
3589 deformat_string( package, name, &deformated_name );
3591 keypath = get_keypath( package, hkey_root, deformated_key );
3592 msi_free( deformated_key );
3593 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
3594 msi_free( keypath );
3596 uirow = MSI_CreateRecord( 2 );
3597 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3598 MSI_RecordSetStringW( uirow, 2, deformated_name );
3600 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3601 msiobj_release( &uirow->hdr );
3603 msi_free( ui_key_str );
3604 msi_free( deformated_name );
3605 return ERROR_SUCCESS;
3608 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3612 static const WCHAR registry_query[] =
3613 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3614 '`','R','e','g','i','s','t','r','y','`',0 };
3615 static const WCHAR remove_registry_query[] =
3616 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3617 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
3619 /* increment progress bar each time action data is sent */
3620 msi_ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
3622 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3623 if (rc == ERROR_SUCCESS)
3625 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3626 msiobj_release( &view->hdr );
3627 if (rc != ERROR_SUCCESS)
3631 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3632 if (rc == ERROR_SUCCESS)
3634 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3635 msiobj_release( &view->hdr );
3636 if (rc != ERROR_SUCCESS)
3640 return ERROR_SUCCESS;
3643 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3645 package->script->CurrentlyScripting = TRUE;
3647 return ERROR_SUCCESS;
3651 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3656 static const WCHAR q1[]=
3657 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3658 '`','R','e','g','i','s','t','r','y','`',0};
3661 MSIFEATURE *feature;
3664 TRACE("InstallValidate\n");
3666 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
3667 if (rc == ERROR_SUCCESS)
3669 MSI_IterateRecords( view, &progress, NULL, package );
3670 msiobj_release( &view->hdr );
3671 total += progress * REG_PROGRESS_VALUE;
3674 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3675 total += COMPONENT_PROGRESS_VALUE;
3677 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3678 total += file->FileSize;
3680 msi_ui_progress( package, 0, total, 0, 0 );
3682 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3684 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3685 debugstr_w(feature->Feature), feature->Installed,
3686 feature->ActionRequest, feature->Action);
3689 return ERROR_SUCCESS;
3692 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3694 MSIPACKAGE* package = param;
3695 LPCWSTR cond = NULL;
3696 LPCWSTR message = NULL;
3699 static const WCHAR title[]=
3700 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3702 cond = MSI_RecordGetString(row,1);
3704 r = MSI_EvaluateConditionW(package,cond);
3705 if (r == MSICONDITION_FALSE)
3707 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3710 message = MSI_RecordGetString(row,2);
3711 deformat_string(package,message,&deformated);
3712 MessageBoxW(NULL,deformated,title,MB_OK);
3713 msi_free(deformated);
3716 return ERROR_INSTALL_FAILURE;
3719 return ERROR_SUCCESS;
3722 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3725 MSIQUERY * view = NULL;
3726 static const WCHAR ExecSeqQuery[] =
3727 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3728 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3730 TRACE("Checking launch conditions\n");
3732 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3733 if (rc != ERROR_SUCCESS)
3734 return ERROR_SUCCESS;
3736 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3737 msiobj_release(&view->hdr);
3742 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3746 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3748 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3750 MSIRECORD * row = 0;
3752 LPWSTR deformated,buffer,deformated_name;
3754 static const WCHAR ExecSeqQuery[] =
3755 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3756 '`','R','e','g','i','s','t','r','y','`',' ',
3757 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
3758 ' ','=',' ' ,'\'','%','s','\'',0 };
3759 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
3760 static const WCHAR fmt2[]=
3761 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3763 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
3767 root = MSI_RecordGetInteger(row,2);
3768 key = MSI_RecordGetString(row, 3);
3769 name = MSI_RecordGetString(row, 4);
3770 deformat_string(package, key , &deformated);
3771 deformat_string(package, name, &deformated_name);
3773 len = strlenW(deformated) + 6;
3774 if (deformated_name)
3775 len+=strlenW(deformated_name);
3777 buffer = msi_alloc( len *sizeof(WCHAR));
3779 if (deformated_name)
3780 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3782 sprintfW(buffer,fmt,root,deformated);
3784 msi_free(deformated);
3785 msi_free(deformated_name);
3786 msiobj_release(&row->hdr);
3790 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3792 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3797 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3800 return strdupW( file->TargetPath );
3805 static HKEY openSharedDLLsKey(void)
3808 static const WCHAR path[] =
3809 {'S','o','f','t','w','a','r','e','\\',
3810 'M','i','c','r','o','s','o','f','t','\\',
3811 'W','i','n','d','o','w','s','\\',
3812 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3813 'S','h','a','r','e','d','D','L','L','s',0};
3815 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3819 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3824 DWORD sz = sizeof(count);
3827 hkey = openSharedDLLsKey();
3828 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3829 if (rc != ERROR_SUCCESS)
3835 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3839 hkey = openSharedDLLsKey();
3841 msi_reg_set_val_dword( hkey, path, count );
3843 RegDeleteValueW(hkey,path);
3848 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3850 MSIFEATURE *feature;
3854 /* only refcount DLLs */
3855 if (comp->KeyPath == NULL ||
3857 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3858 comp->Attributes & msidbComponentAttributesODBCDataSource)
3862 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3863 write = (count > 0);
3865 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3869 /* increment counts */
3870 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3874 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
3877 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3879 if ( cl->component == comp )
3884 /* decrement counts */
3885 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3889 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3892 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3894 if ( cl->component == comp )
3899 /* ref count all the files in the component */
3904 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3906 if (file->Component == comp)
3907 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3911 /* add a count for permanent */
3912 if (comp->Attributes & msidbComponentAttributesPermanent)
3915 comp->RefCount = count;
3918 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3921 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3923 WCHAR squished_pc[GUID_SIZE];
3924 WCHAR squished_cc[GUID_SIZE];
3931 squash_guid(package->ProductCode,squished_pc);
3932 msi_ui_progress( package, 1, COMPONENT_PROGRESS_VALUE, 1, 0 );
3934 msi_set_sourcedir_props(package, FALSE);
3936 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3940 msi_ui_progress( package, 2, 0, 0, 0 );
3941 if (!comp->ComponentId)
3944 squash_guid(comp->ComponentId,squished_cc);
3946 msi_free(comp->FullKeypath);
3949 const WCHAR prefixW[] = {'<','\\',0};
3950 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3952 comp->FullKeypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3953 if (comp->FullKeypath)
3955 strcpyW( comp->FullKeypath, prefixW );
3956 strcatW( comp->FullKeypath, comp->assembly->display_name );
3959 else comp->FullKeypath = resolve_keypath( package, comp );
3961 ACTION_RefCountComponent( package, comp );
3963 TRACE("Component %s (%s), Keypath=%s, RefCount=%i Request=%u\n",
3964 debugstr_w(comp->Component),
3965 debugstr_w(squished_cc),
3966 debugstr_w(comp->FullKeypath),
3968 comp->ActionRequest);
3970 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
3971 comp->ActionRequest == INSTALLSTATE_SOURCE)
3973 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3974 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3976 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3978 if (rc != ERROR_SUCCESS)
3981 if (comp->Attributes & msidbComponentAttributesPermanent)
3983 static const WCHAR szPermKey[] =
3984 { '0','0','0','0','0','0','0','0','0','0','0','0',
3985 '0','0','0','0','0','0','0','0','0','0','0','0',
3986 '0','0','0','0','0','0','0','0',0 };
3988 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3991 if (comp->ActionRequest == INSTALLSTATE_LOCAL)
3992 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3998 WCHAR source[MAX_PATH];
3999 WCHAR base[MAX_PATH];
4002 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
4003 static const WCHAR query[] = {
4004 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
4005 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
4006 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
4007 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
4008 '`','D','i','s','k','I','d','`',0};
4010 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
4013 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
4014 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
4015 ptr2 = strrchrW(source, '\\') + 1;
4016 msiobj_release(&row->hdr);
4018 lstrcpyW(base, package->PackagePath);
4019 ptr = strrchrW(base, '\\');
4022 sourcepath = msi_resolve_file_source(package, file);
4023 ptr = sourcepath + lstrlenW(base);
4024 lstrcpyW(ptr2, ptr);
4025 msi_free(sourcepath);
4027 msi_reg_set_val_str(hkey, squished_pc, source);
4031 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
4033 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4034 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
4036 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
4038 comp->Action = comp->ActionRequest;
4041 uirow = MSI_CreateRecord(3);
4042 MSI_RecordSetStringW(uirow,1,package->ProductCode);
4043 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
4044 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
4045 msi_ui_actiondata( package, szProcessComponents, uirow );
4046 msiobj_release( &uirow->hdr );
4049 return ERROR_SUCCESS;
4060 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
4061 LPWSTR lpszName, LONG_PTR lParam)
4064 typelib_struct *tl_struct = (typelib_struct*) lParam;
4065 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
4069 if (!IS_INTRESOURCE(lpszName))
4071 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
4075 sz = strlenW(tl_struct->source)+4;
4076 sz *= sizeof(WCHAR);
4078 if ((INT_PTR)lpszName == 1)
4079 tl_struct->path = strdupW(tl_struct->source);
4082 tl_struct->path = msi_alloc(sz);
4083 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
4086 TRACE("trying %s\n", debugstr_w(tl_struct->path));
4087 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
4090 msi_free(tl_struct->path);
4091 tl_struct->path = NULL;
4096 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
4097 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
4099 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
4103 msi_free(tl_struct->path);
4104 tl_struct->path = NULL;
4106 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
4107 ITypeLib_Release(tl_struct->ptLib);
4112 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
4114 MSIPACKAGE* package = param;
4118 typelib_struct tl_struct;
4123 component = MSI_RecordGetString(row,3);
4124 comp = msi_get_loaded_component(package,component);
4126 return ERROR_SUCCESS;
4130 TRACE("component is disabled\n");
4131 return ERROR_SUCCESS;
4134 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4136 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
4137 comp->Action = comp->Installed;
4138 return ERROR_SUCCESS;
4140 comp->Action = INSTALLSTATE_LOCAL;
4142 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
4144 TRACE("component has no key path\n");
4145 return ERROR_SUCCESS;
4147 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
4149 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
4153 guid = MSI_RecordGetString(row,1);
4154 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
4155 tl_struct.source = strdupW( file->TargetPath );
4156 tl_struct.path = NULL;
4158 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
4159 (LONG_PTR)&tl_struct);
4163 LPCWSTR helpid, help_path = NULL;
4166 helpid = MSI_RecordGetString(row,6);
4168 if (helpid) help_path = msi_get_target_folder( package, helpid );
4169 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
4172 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
4174 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
4176 ITypeLib_Release(tl_struct.ptLib);
4177 msi_free(tl_struct.path);
4179 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
4181 FreeLibrary(module);
4182 msi_free(tl_struct.source);
4186 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
4189 ERR("Failed to load type library: %08x\n", hr);
4190 return ERROR_INSTALL_FAILURE;
4193 ITypeLib_Release(tlib);
4196 return ERROR_SUCCESS;
4199 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
4202 * OK this is a bit confusing.. I am given a _Component key and I believe
4203 * that the file that is being registered as a type library is the "key file
4204 * of that component" which I interpret to mean "The file in the KeyPath of
4209 static const WCHAR Query[] =
4210 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4211 '`','T','y','p','e','L','i','b','`',0};
4213 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
4214 if (rc != ERROR_SUCCESS)
4215 return ERROR_SUCCESS;
4217 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
4218 msiobj_release(&view->hdr);
4222 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
4224 MSIPACKAGE *package = param;
4225 LPCWSTR component, guid;
4233 component = MSI_RecordGetString( row, 3 );
4234 comp = msi_get_loaded_component( package, component );
4236 return ERROR_SUCCESS;
4240 TRACE("component is disabled\n");
4241 return ERROR_SUCCESS;
4244 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4246 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4247 comp->Action = comp->Installed;
4248 return ERROR_SUCCESS;
4250 comp->Action = INSTALLSTATE_ABSENT;
4252 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
4254 guid = MSI_RecordGetString( row, 1 );
4255 CLSIDFromString( (LPCWSTR)guid, &libid );
4256 version = MSI_RecordGetInteger( row, 4 );
4257 language = MSI_RecordGetInteger( row, 2 );
4260 syskind = SYS_WIN64;
4262 syskind = SYS_WIN32;
4265 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
4268 WARN("Failed to unregister typelib: %08x\n", hr);
4271 return ERROR_SUCCESS;
4274 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
4278 static const WCHAR query[] =
4279 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4280 '`','T','y','p','e','L','i','b','`',0};
4282 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4283 if (rc != ERROR_SUCCESS)
4284 return ERROR_SUCCESS;
4286 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
4287 msiobj_release( &view->hdr );
4291 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
4293 static const WCHAR szlnk[] = {'.','l','n','k',0};
4294 LPCWSTR directory, extension, link_folder;
4295 LPWSTR link_file, filename;
4297 directory = MSI_RecordGetString( row, 2 );
4298 link_folder = msi_get_target_folder( package, directory );
4300 /* may be needed because of a bug somewhere else */
4301 msi_create_full_path( link_folder );
4303 filename = msi_dup_record_field( row, 3 );
4304 msi_reduce_to_long_filename( filename );
4306 extension = strchrW( filename, '.' );
4307 if (!extension || strcmpiW( extension, szlnk ))
4309 int len = strlenW( filename );
4310 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
4311 memcpy( filename + len, szlnk, sizeof(szlnk) );
4313 link_file = msi_build_directory_name( 2, link_folder, filename );
4314 msi_free( filename );
4319 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
4321 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
4322 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
4323 WCHAR *folder, *dest, *path;
4325 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4326 folder = msi_dup_property( package->db, szWindowsFolder );
4329 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
4330 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
4331 msi_free( appdata );
4333 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
4334 msi_create_full_path( dest );
4335 path = msi_build_directory_name( 2, dest, icon_name );
4341 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
4343 MSIPACKAGE *package = param;
4344 LPWSTR link_file, deformated, path;
4345 LPCWSTR component, target;
4347 IShellLinkW *sl = NULL;
4348 IPersistFile *pf = NULL;
4351 component = MSI_RecordGetString(row, 4);
4352 comp = msi_get_loaded_component(package, component);
4354 return ERROR_SUCCESS;
4358 TRACE("component is disabled\n");
4359 return ERROR_SUCCESS;
4362 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4364 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4365 comp->Action = comp->Installed;
4366 return ERROR_SUCCESS;
4368 comp->Action = INSTALLSTATE_LOCAL;
4370 msi_ui_actiondata( package, szCreateShortcuts, row );
4372 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
4373 &IID_IShellLinkW, (LPVOID *) &sl );
4377 ERR("CLSID_ShellLink not available\n");
4381 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
4384 ERR("QueryInterface(IID_IPersistFile) failed\n");
4388 target = MSI_RecordGetString(row, 5);
4389 if (strchrW(target, '['))
4391 deformat_string(package, target, &deformated);
4392 IShellLinkW_SetPath(sl,deformated);
4393 msi_free(deformated);
4397 FIXME("poorly handled shortcut format, advertised shortcut\n");
4398 IShellLinkW_SetPath(sl,comp->FullKeypath);
4401 if (!MSI_RecordIsNull(row,6))
4403 LPCWSTR arguments = MSI_RecordGetString(row, 6);
4404 deformat_string(package, arguments, &deformated);
4405 IShellLinkW_SetArguments(sl,deformated);
4406 msi_free(deformated);
4409 if (!MSI_RecordIsNull(row,7))
4411 LPCWSTR description = MSI_RecordGetString(row, 7);
4412 IShellLinkW_SetDescription(sl, description);
4415 if (!MSI_RecordIsNull(row,8))
4416 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
4418 if (!MSI_RecordIsNull(row,9))
4421 LPCWSTR icon = MSI_RecordGetString(row, 9);
4423 path = msi_build_icon_path(package, icon);
4424 index = MSI_RecordGetInteger(row,10);
4426 /* no value means 0 */
4427 if (index == MSI_NULL_INTEGER)
4430 IShellLinkW_SetIconLocation(sl, path, index);
4434 if (!MSI_RecordIsNull(row,11))
4435 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
4437 if (!MSI_RecordIsNull(row,12))
4439 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
4440 full_path = msi_get_target_folder( package, wkdir );
4441 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
4443 link_file = get_link_file(package, row);
4445 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
4446 IPersistFile_Save(pf, link_file, FALSE);
4447 msi_free(link_file);
4451 IPersistFile_Release( pf );
4453 IShellLinkW_Release( sl );
4455 return ERROR_SUCCESS;
4458 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4463 static const WCHAR Query[] =
4464 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4465 '`','S','h','o','r','t','c','u','t','`',0};
4467 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
4468 if (rc != ERROR_SUCCESS)
4469 return ERROR_SUCCESS;
4471 res = CoInitialize( NULL );
4473 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4474 msiobj_release(&view->hdr);
4482 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4484 MSIPACKAGE *package = param;
4489 component = MSI_RecordGetString( row, 4 );
4490 comp = msi_get_loaded_component( package, component );
4492 return ERROR_SUCCESS;
4496 TRACE("component is disabled\n");
4497 return ERROR_SUCCESS;
4500 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4502 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4503 comp->Action = comp->Installed;
4504 return ERROR_SUCCESS;
4506 comp->Action = INSTALLSTATE_ABSENT;
4508 msi_ui_actiondata( package, szRemoveShortcuts, row );
4510 link_file = get_link_file( package, row );
4512 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4513 if (!DeleteFileW( link_file ))
4515 WARN("Failed to remove shortcut file %u\n", GetLastError());
4517 msi_free( link_file );
4519 return ERROR_SUCCESS;
4522 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4526 static const WCHAR query[] =
4527 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4528 '`','S','h','o','r','t','c','u','t','`',0};
4530 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4531 if (rc != ERROR_SUCCESS)
4532 return ERROR_SUCCESS;
4534 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4535 msiobj_release( &view->hdr );
4540 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4542 MSIPACKAGE* package = param;
4550 FileName = MSI_RecordGetString(row,1);
4553 ERR("Unable to get FileName\n");
4554 return ERROR_SUCCESS;
4557 FilePath = msi_build_icon_path(package, FileName);
4559 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4561 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4562 FILE_ATTRIBUTE_NORMAL, NULL);
4564 if (the_file == INVALID_HANDLE_VALUE)
4566 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4568 return ERROR_SUCCESS;
4575 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4576 if (rc != ERROR_SUCCESS)
4578 ERR("Failed to get stream\n");
4579 CloseHandle(the_file);
4580 DeleteFileW(FilePath);
4583 WriteFile(the_file,buffer,sz,&write,NULL);
4584 } while (sz == 1024);
4587 CloseHandle(the_file);
4589 return ERROR_SUCCESS;
4592 static UINT msi_publish_icons(MSIPACKAGE *package)
4597 static const WCHAR query[]= {
4598 'S','E','L','E','C','T',' ','*',' ',
4599 'F','R','O','M',' ','`','I','c','o','n','`',0};
4601 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4602 if (r == ERROR_SUCCESS)
4604 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4605 msiobj_release(&view->hdr);
4608 return ERROR_SUCCESS;
4611 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4617 MSISOURCELISTINFO *info;
4619 r = RegCreateKeyW(hkey, szSourceList, &source);
4620 if (r != ERROR_SUCCESS)
4623 RegCloseKey(source);
4625 buffer = strrchrW(package->PackagePath, '\\') + 1;
4626 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4627 package->Context, MSICODE_PRODUCT,
4628 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4629 if (r != ERROR_SUCCESS)
4632 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4633 package->Context, MSICODE_PRODUCT,
4634 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4635 if (r != ERROR_SUCCESS)
4638 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4639 package->Context, MSICODE_PRODUCT,
4640 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4641 if (r != ERROR_SUCCESS)
4644 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4646 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4647 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4648 info->options, info->value);
4650 MsiSourceListSetInfoW(package->ProductCode, NULL,
4651 info->context, info->options,
4652 info->property, info->value);
4655 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4657 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4658 disk->context, disk->options,
4659 disk->disk_id, disk->volume_label, disk->disk_prompt);
4662 return ERROR_SUCCESS;
4665 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4667 MSIHANDLE hdb, suminfo;
4668 WCHAR guids[MAX_PATH];
4669 WCHAR packcode[SQUISH_GUID_SIZE];
4676 static const WCHAR szARPProductIcon[] =
4677 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4678 static const WCHAR szAssignment[] =
4679 {'A','s','s','i','g','n','m','e','n','t',0};
4680 static const WCHAR szAdvertiseFlags[] =
4681 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4682 static const WCHAR szClients[] =
4683 {'C','l','i','e','n','t','s',0};
4684 static const WCHAR szColon[] = {':',0};
4686 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4687 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4690 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4691 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4694 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4696 buffer = msi_dup_property(package->db, szARPProductIcon);
4699 LPWSTR path = msi_build_icon_path(package, buffer);
4700 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4705 buffer = msi_dup_property(package->db, szProductVersion);
4708 DWORD verdword = msi_version_str_to_dword(buffer);
4709 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4713 msi_reg_set_val_dword(hkey, szAssignment, 0);
4714 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4715 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4716 msi_reg_set_val_str(hkey, szClients, szColon);
4718 hdb = alloc_msihandle(&package->db->hdr);
4720 return ERROR_NOT_ENOUGH_MEMORY;
4722 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4723 MsiCloseHandle(hdb);
4724 if (r != ERROR_SUCCESS)
4728 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4729 NULL, guids, &size);
4730 if (r != ERROR_SUCCESS)
4733 ptr = strchrW(guids, ';');
4735 squash_guid(guids, packcode);
4736 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4739 MsiCloseHandle(suminfo);
4740 return ERROR_SUCCESS;
4743 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4748 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4750 upgrade = msi_dup_property(package->db, szUpgradeCode);
4752 return ERROR_SUCCESS;
4754 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4755 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4757 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4759 if (r != ERROR_SUCCESS)
4761 WARN("failed to open upgrade code key\n");
4763 return ERROR_SUCCESS;
4765 squash_guid(package->ProductCode, squashed_pc);
4766 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4769 return ERROR_SUCCESS;
4772 static BOOL msi_check_publish(MSIPACKAGE *package)
4774 MSIFEATURE *feature;
4776 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4778 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
4785 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4787 MSIFEATURE *feature;
4789 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4791 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
4798 static UINT msi_publish_patches( MSIPACKAGE *package )
4800 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4801 WCHAR patch_squashed[GUID_SIZE];
4802 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4804 MSIPATCHINFO *patch;
4806 WCHAR *p, *all_patches = NULL;
4809 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4810 if (r != ERROR_SUCCESS)
4811 return ERROR_FUNCTION_FAILED;
4813 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4814 if (res != ERROR_SUCCESS)
4816 r = ERROR_FUNCTION_FAILED;
4820 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4821 if (r != ERROR_SUCCESS)
4824 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4826 squash_guid( patch->patchcode, patch_squashed );
4827 len += strlenW( patch_squashed ) + 1;
4830 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4834 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4838 squash_guid( patch->patchcode, p );
4839 p += strlenW( p ) + 1;
4841 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4842 (const BYTE *)patch->transforms,
4843 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4844 if (res != ERROR_SUCCESS)
4847 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4848 if (r != ERROR_SUCCESS)
4851 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
4852 (const BYTE *)patch->localfile,
4853 (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
4854 RegCloseKey( patch_key );
4855 if (res != ERROR_SUCCESS)
4858 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4859 if (res != ERROR_SUCCESS)
4862 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4863 RegCloseKey( patch_key );
4864 if (res != ERROR_SUCCESS)
4868 all_patches[len] = 0;
4869 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4870 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4871 if (res != ERROR_SUCCESS)
4874 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4875 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4876 if (res != ERROR_SUCCESS)
4877 r = ERROR_FUNCTION_FAILED;
4880 RegCloseKey( product_patches_key );
4881 RegCloseKey( patches_key );
4882 RegCloseKey( product_key );
4883 msi_free( all_patches );
4888 * 99% of the work done here is only done for
4889 * advertised installs. However this is where the
4890 * Icon table is processed and written out
4891 * so that is what I am going to do here.
4893 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4896 HKEY hukey = NULL, hudkey = NULL;
4899 if (!list_empty(&package->patches))
4901 rc = msi_publish_patches(package);
4902 if (rc != ERROR_SUCCESS)
4906 /* FIXME: also need to publish if the product is in advertise mode */
4907 if (!msi_check_publish(package))
4908 return ERROR_SUCCESS;
4910 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4912 if (rc != ERROR_SUCCESS)
4915 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4916 NULL, &hudkey, TRUE);
4917 if (rc != ERROR_SUCCESS)
4920 rc = msi_publish_upgrade_code(package);
4921 if (rc != ERROR_SUCCESS)
4924 rc = msi_publish_product_properties(package, hukey);
4925 if (rc != ERROR_SUCCESS)
4928 rc = msi_publish_sourcelist(package, hukey);
4929 if (rc != ERROR_SUCCESS)
4932 rc = msi_publish_icons(package);
4935 uirow = MSI_CreateRecord( 1 );
4936 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4937 msi_ui_actiondata( package, szPublishProduct, uirow );
4938 msiobj_release( &uirow->hdr );
4941 RegCloseKey(hudkey);
4946 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4948 WCHAR *filename, *ptr, *folder, *ret;
4949 const WCHAR *dirprop;
4951 filename = msi_dup_record_field( row, 2 );
4952 if (filename && (ptr = strchrW( filename, '|' )))
4957 dirprop = MSI_RecordGetString( row, 3 );
4960 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4961 if (!folder) folder = msi_dup_property( package->db, dirprop );
4964 folder = msi_dup_property( package->db, szWindowsFolder );
4968 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4969 msi_free( filename );
4973 ret = msi_build_directory_name( 2, folder, ptr );
4975 msi_free( filename );
4980 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4982 MSIPACKAGE *package = param;
4983 LPCWSTR component, section, key, value, identifier;
4984 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4989 component = MSI_RecordGetString(row, 8);
4990 comp = msi_get_loaded_component(package,component);
4992 return ERROR_SUCCESS;
4996 TRACE("component is disabled\n");
4997 return ERROR_SUCCESS;
5000 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5002 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
5003 comp->Action = comp->Installed;
5004 return ERROR_SUCCESS;
5006 comp->Action = INSTALLSTATE_LOCAL;
5008 identifier = MSI_RecordGetString(row,1);
5009 section = MSI_RecordGetString(row,4);
5010 key = MSI_RecordGetString(row,5);
5011 value = MSI_RecordGetString(row,6);
5012 action = MSI_RecordGetInteger(row,7);
5014 deformat_string(package,section,&deformated_section);
5015 deformat_string(package,key,&deformated_key);
5016 deformat_string(package,value,&deformated_value);
5018 fullname = get_ini_file_name(package, row);
5022 TRACE("Adding value %s to section %s in %s\n",
5023 debugstr_w(deformated_key), debugstr_w(deformated_section),
5024 debugstr_w(fullname));
5025 WritePrivateProfileStringW(deformated_section, deformated_key,
5026 deformated_value, fullname);
5028 else if (action == 1)
5031 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
5032 returned, 10, fullname);
5033 if (returned[0] == 0)
5035 TRACE("Adding value %s to section %s in %s\n",
5036 debugstr_w(deformated_key), debugstr_w(deformated_section),
5037 debugstr_w(fullname));
5039 WritePrivateProfileStringW(deformated_section, deformated_key,
5040 deformated_value, fullname);
5043 else if (action == 3)
5044 FIXME("Append to existing section not yet implemented\n");
5046 uirow = MSI_CreateRecord(4);
5047 MSI_RecordSetStringW(uirow,1,identifier);
5048 MSI_RecordSetStringW(uirow,2,deformated_section);
5049 MSI_RecordSetStringW(uirow,3,deformated_key);
5050 MSI_RecordSetStringW(uirow,4,deformated_value);
5051 msi_ui_actiondata( package, szWriteIniValues, uirow );
5052 msiobj_release( &uirow->hdr );
5055 msi_free(deformated_key);
5056 msi_free(deformated_value);
5057 msi_free(deformated_section);
5058 return ERROR_SUCCESS;
5061 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
5065 static const WCHAR ExecSeqQuery[] =
5066 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5067 '`','I','n','i','F','i','l','e','`',0};
5069 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5070 if (rc != ERROR_SUCCESS)
5072 TRACE("no IniFile table\n");
5073 return ERROR_SUCCESS;
5076 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
5077 msiobj_release(&view->hdr);
5081 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
5083 MSIPACKAGE *package = param;
5084 LPCWSTR component, section, key, value, identifier;
5085 LPWSTR deformated_section, deformated_key, deformated_value, filename;
5090 component = MSI_RecordGetString( row, 8 );
5091 comp = msi_get_loaded_component( package, component );
5093 return ERROR_SUCCESS;
5097 TRACE("component is disabled\n");
5098 return ERROR_SUCCESS;
5101 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5103 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
5104 comp->Action = comp->Installed;
5105 return ERROR_SUCCESS;
5107 comp->Action = INSTALLSTATE_ABSENT;
5109 identifier = MSI_RecordGetString( row, 1 );
5110 section = MSI_RecordGetString( row, 4 );
5111 key = MSI_RecordGetString( row, 5 );
5112 value = MSI_RecordGetString( row, 6 );
5113 action = MSI_RecordGetInteger( row, 7 );
5115 deformat_string( package, section, &deformated_section );
5116 deformat_string( package, key, &deformated_key );
5117 deformat_string( package, value, &deformated_value );
5119 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
5121 filename = get_ini_file_name( package, row );
5123 TRACE("Removing key %s from section %s in %s\n",
5124 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
5126 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
5128 WARN("Unable to remove key %u\n", GetLastError());
5130 msi_free( filename );
5133 FIXME("Unsupported action %d\n", action);
5136 uirow = MSI_CreateRecord( 4 );
5137 MSI_RecordSetStringW( uirow, 1, identifier );
5138 MSI_RecordSetStringW( uirow, 2, deformated_section );
5139 MSI_RecordSetStringW( uirow, 3, deformated_key );
5140 MSI_RecordSetStringW( uirow, 4, deformated_value );
5141 msi_ui_actiondata( package, szRemoveIniValues, uirow );
5142 msiobj_release( &uirow->hdr );
5144 msi_free( deformated_key );
5145 msi_free( deformated_value );
5146 msi_free( deformated_section );
5147 return ERROR_SUCCESS;
5150 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
5152 MSIPACKAGE *package = param;
5153 LPCWSTR component, section, key, value, identifier;
5154 LPWSTR deformated_section, deformated_key, deformated_value, filename;
5159 component = MSI_RecordGetString( row, 8 );
5160 comp = msi_get_loaded_component( package, component );
5162 return ERROR_SUCCESS;
5166 TRACE("component is disabled\n");
5167 return ERROR_SUCCESS;
5170 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5172 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
5173 comp->Action = comp->Installed;
5174 return ERROR_SUCCESS;
5176 comp->Action = INSTALLSTATE_LOCAL;
5178 identifier = MSI_RecordGetString( row, 1 );
5179 section = MSI_RecordGetString( row, 4 );
5180 key = MSI_RecordGetString( row, 5 );
5181 value = MSI_RecordGetString( row, 6 );
5182 action = MSI_RecordGetInteger( row, 7 );
5184 deformat_string( package, section, &deformated_section );
5185 deformat_string( package, key, &deformated_key );
5186 deformat_string( package, value, &deformated_value );
5188 if (action == msidbIniFileActionRemoveLine)
5190 filename = get_ini_file_name( package, row );
5192 TRACE("Removing key %s from section %s in %s\n",
5193 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
5195 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
5197 WARN("Unable to remove key %u\n", GetLastError());
5199 msi_free( filename );
5202 FIXME("Unsupported action %d\n", action);
5204 uirow = MSI_CreateRecord( 4 );
5205 MSI_RecordSetStringW( uirow, 1, identifier );
5206 MSI_RecordSetStringW( uirow, 2, deformated_section );
5207 MSI_RecordSetStringW( uirow, 3, deformated_key );
5208 MSI_RecordSetStringW( uirow, 4, deformated_value );
5209 msi_ui_actiondata( package, szRemoveIniValues, uirow );
5210 msiobj_release( &uirow->hdr );
5212 msi_free( deformated_key );
5213 msi_free( deformated_value );
5214 msi_free( deformated_section );
5215 return ERROR_SUCCESS;
5218 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5222 static const WCHAR query[] =
5223 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5224 '`','I','n','i','F','i','l','e','`',0};
5225 static const WCHAR remove_query[] =
5226 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5227 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
5229 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5230 if (rc == ERROR_SUCCESS)
5232 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
5233 msiobj_release( &view->hdr );
5234 if (rc != ERROR_SUCCESS)
5238 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
5239 if (rc == ERROR_SUCCESS)
5241 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
5242 msiobj_release( &view->hdr );
5243 if (rc != ERROR_SUCCESS)
5247 return ERROR_SUCCESS;
5250 static void register_dll( const WCHAR *dll, BOOL unregister )
5254 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
5257 HRESULT (WINAPI *func_ptr)( void );
5258 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
5260 func_ptr = (void *)GetProcAddress( hmod, func );
5263 HRESULT hr = func_ptr();
5265 WARN("failed to register dll 0x%08x\n", hr);
5268 WARN("entry point %s not found\n", func);
5269 FreeLibrary( hmod );
5272 WARN("failed to load library %u\n", GetLastError());
5275 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
5277 MSIPACKAGE *package = param;
5282 filename = MSI_RecordGetString(row,1);
5283 file = msi_get_loaded_file( package, filename );
5287 ERR("Unable to find file id %s\n",debugstr_w(filename));
5288 return ERROR_SUCCESS;
5291 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
5293 register_dll( file->TargetPath, FALSE );
5295 uirow = MSI_CreateRecord( 2 );
5296 MSI_RecordSetStringW( uirow, 1, filename );
5297 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
5298 msi_ui_actiondata( package, szSelfRegModules, uirow );
5299 msiobj_release( &uirow->hdr );
5301 return ERROR_SUCCESS;
5304 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
5308 static const WCHAR ExecSeqQuery[] =
5309 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5310 '`','S','e','l','f','R','e','g','`',0};
5312 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5313 if (rc != ERROR_SUCCESS)
5315 TRACE("no SelfReg table\n");
5316 return ERROR_SUCCESS;
5319 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
5320 msiobj_release(&view->hdr);
5322 return ERROR_SUCCESS;
5325 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
5327 MSIPACKAGE *package = param;
5332 filename = MSI_RecordGetString( row, 1 );
5333 file = msi_get_loaded_file( package, filename );
5337 ERR("Unable to find file id %s\n", debugstr_w(filename));
5338 return ERROR_SUCCESS;
5341 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
5343 register_dll( file->TargetPath, TRUE );
5345 uirow = MSI_CreateRecord( 2 );
5346 MSI_RecordSetStringW( uirow, 1, filename );
5347 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
5348 msi_ui_actiondata( package, szSelfUnregModules, uirow );
5349 msiobj_release( &uirow->hdr );
5351 return ERROR_SUCCESS;
5354 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5358 static const WCHAR query[] =
5359 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5360 '`','S','e','l','f','R','e','g','`',0};
5362 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5363 if (rc != ERROR_SUCCESS)
5365 TRACE("no SelfReg table\n");
5366 return ERROR_SUCCESS;
5369 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
5370 msiobj_release( &view->hdr );
5372 return ERROR_SUCCESS;
5375 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
5377 MSIFEATURE *feature;
5379 HKEY hkey = NULL, userdata = NULL;
5381 if (!msi_check_publish(package))
5382 return ERROR_SUCCESS;
5384 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
5386 if (rc != ERROR_SUCCESS)
5389 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
5391 if (rc != ERROR_SUCCESS)
5394 /* here the guids are base 85 encoded */
5395 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5401 BOOL absent = FALSE;
5404 if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
5405 feature->ActionRequest != INSTALLSTATE_SOURCE &&
5406 feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
5409 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
5413 if (feature->Feature_Parent)
5414 size += strlenW( feature->Feature_Parent )+2;
5416 data = msi_alloc(size * sizeof(WCHAR));
5419 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
5421 MSICOMPONENT* component = cl->component;
5425 if (component->ComponentId)
5427 TRACE("From %s\n",debugstr_w(component->ComponentId));
5428 CLSIDFromString(component->ComponentId, &clsid);
5429 encode_base85_guid(&clsid,buf);
5430 TRACE("to %s\n",debugstr_w(buf));
5435 if (feature->Feature_Parent)
5437 static const WCHAR sep[] = {'\2',0};
5439 strcatW(data,feature->Feature_Parent);
5442 msi_reg_set_val_str( userdata, feature->Feature, data );
5446 if (feature->Feature_Parent)
5447 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
5450 size += sizeof(WCHAR);
5451 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5452 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
5456 size += 2*sizeof(WCHAR);
5457 data = msi_alloc(size);
5460 if (feature->Feature_Parent)
5461 strcpyW( &data[1], feature->Feature_Parent );
5462 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5468 uirow = MSI_CreateRecord( 1 );
5469 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5470 msi_ui_actiondata( package, szPublishFeatures, uirow );
5471 msiobj_release( &uirow->hdr );
5472 /* FIXME: call msi_ui_progress? */
5477 RegCloseKey(userdata);
5481 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
5487 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
5489 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
5491 if (r == ERROR_SUCCESS)
5493 RegDeleteValueW(hkey, feature->Feature);
5497 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
5499 if (r == ERROR_SUCCESS)
5501 RegDeleteValueW(hkey, feature->Feature);
5505 uirow = MSI_CreateRecord( 1 );
5506 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5507 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
5508 msiobj_release( &uirow->hdr );
5510 return ERROR_SUCCESS;
5513 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5515 MSIFEATURE *feature;
5517 if (!msi_check_unpublish(package))
5518 return ERROR_SUCCESS;
5520 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5522 msi_unpublish_feature(package, feature);
5525 return ERROR_SUCCESS;
5528 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5532 WCHAR date[9], *val, *buffer;
5533 const WCHAR *prop, *key;
5535 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5536 static const WCHAR modpath_fmt[] =
5537 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5538 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5539 static const WCHAR szModifyPath[] =
5540 {'M','o','d','i','f','y','P','a','t','h',0};
5541 static const WCHAR szUninstallString[] =
5542 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5543 static const WCHAR szEstimatedSize[] =
5544 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5545 static const WCHAR szDisplayVersion[] =
5546 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5547 static const WCHAR szInstallSource[] =
5548 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5549 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5550 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5551 static const WCHAR szAuthorizedCDFPrefix[] =
5552 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5553 static const WCHAR szARPCONTACT[] =
5554 {'A','R','P','C','O','N','T','A','C','T',0};
5555 static const WCHAR szContact[] =
5556 {'C','o','n','t','a','c','t',0};
5557 static const WCHAR szARPCOMMENTS[] =
5558 {'A','R','P','C','O','M','M','E','N','T','S',0};
5559 static const WCHAR szComments[] =
5560 {'C','o','m','m','e','n','t','s',0};
5561 static const WCHAR szProductName[] =
5562 {'P','r','o','d','u','c','t','N','a','m','e',0};
5563 static const WCHAR szDisplayName[] =
5564 {'D','i','s','p','l','a','y','N','a','m','e',0};
5565 static const WCHAR szARPHELPLINK[] =
5566 {'A','R','P','H','E','L','P','L','I','N','K',0};
5567 static const WCHAR szHelpLink[] =
5568 {'H','e','l','p','L','i','n','k',0};
5569 static const WCHAR szARPHELPTELEPHONE[] =
5570 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5571 static const WCHAR szHelpTelephone[] =
5572 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5573 static const WCHAR szARPINSTALLLOCATION[] =
5574 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5575 static const WCHAR szInstallLocation[] =
5576 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
5577 static const WCHAR szManufacturer[] =
5578 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5579 static const WCHAR szPublisher[] =
5580 {'P','u','b','l','i','s','h','e','r',0};
5581 static const WCHAR szARPREADME[] =
5582 {'A','R','P','R','E','A','D','M','E',0};
5583 static const WCHAR szReadme[] =
5584 {'R','e','a','d','M','e',0};
5585 static const WCHAR szARPSIZE[] =
5586 {'A','R','P','S','I','Z','E',0};
5587 static const WCHAR szSize[] =
5588 {'S','i','z','e',0};
5589 static const WCHAR szARPURLINFOABOUT[] =
5590 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5591 static const WCHAR szURLInfoAbout[] =
5592 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5593 static const WCHAR szARPURLUPDATEINFO[] =
5594 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5595 static const WCHAR szURLUpdateInfo[] =
5596 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5598 static const WCHAR *propval[] = {
5599 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5600 szARPCONTACT, szContact,
5601 szARPCOMMENTS, szComments,
5602 szProductName, szDisplayName,
5603 szARPHELPLINK, szHelpLink,
5604 szARPHELPTELEPHONE, szHelpTelephone,
5605 szARPINSTALLLOCATION, szInstallLocation,
5606 szSourceDir, szInstallSource,
5607 szManufacturer, szPublisher,
5608 szARPREADME, szReadme,
5610 szARPURLINFOABOUT, szURLInfoAbout,
5611 szARPURLUPDATEINFO, szURLUpdateInfo,
5614 const WCHAR **p = propval;
5620 val = msi_dup_property(package->db, prop);
5621 msi_reg_set_val_str(hkey, key, val);
5625 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5627 size = deformat_string(package, modpath_fmt, &buffer);
5628 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5629 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5632 /* FIXME: Write real Estimated Size when we have it */
5633 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5635 GetLocalTime(&systime);
5636 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5637 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5639 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5640 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5642 buffer = msi_dup_property(package->db, szProductVersion);
5643 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5646 DWORD verdword = msi_version_str_to_dword(buffer);
5648 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5649 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5650 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5654 return ERROR_SUCCESS;
5657 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5659 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5661 LPWSTR upgrade_code;
5662 HKEY hkey, props, upgrade_key;
5665 /* FIXME: also need to publish if the product is in advertise mode */
5666 if (!msi_check_publish(package))
5667 return ERROR_SUCCESS;
5669 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5670 if (rc != ERROR_SUCCESS)
5673 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5674 NULL, &props, TRUE);
5675 if (rc != ERROR_SUCCESS)
5678 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
5679 msi_free( package->db->localfile );
5680 package->db->localfile = NULL;
5682 rc = msi_publish_install_properties(package, hkey);
5683 if (rc != ERROR_SUCCESS)
5686 rc = msi_publish_install_properties(package, props);
5687 if (rc != ERROR_SUCCESS)
5690 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5693 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5694 if (rc == ERROR_SUCCESS)
5696 squash_guid( package->ProductCode, squashed_pc );
5697 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5698 RegCloseKey( upgrade_key );
5700 msi_free( upgrade_code );
5704 uirow = MSI_CreateRecord( 1 );
5705 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5706 msi_ui_actiondata( package, szRegisterProduct, uirow );
5707 msiobj_release( &uirow->hdr );
5710 return ERROR_SUCCESS;
5713 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5715 return execute_script(package,INSTALL_SCRIPT);
5718 static UINT msi_unpublish_product(MSIPACKAGE *package, WCHAR *remove)
5720 WCHAR *upgrade, **features;
5721 BOOL full_uninstall = TRUE;
5722 MSIFEATURE *feature;
5723 MSIPATCHINFO *patch;
5725 static const WCHAR szUpgradeCode[] =
5726 {'U','p','g','r','a','d','e','C','o','d','e',0};
5728 features = msi_split_string(remove, ',');
5731 ERR("REMOVE feature list is empty!\n");
5732 return ERROR_FUNCTION_FAILED;
5735 if (!strcmpW( features[0], szAll ))
5736 full_uninstall = TRUE;
5739 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5741 if (feature->Action != INSTALLSTATE_ABSENT)
5742 full_uninstall = FALSE;
5747 if (!full_uninstall)
5748 return ERROR_SUCCESS;
5750 MSIREG_DeleteProductKey(package->ProductCode);
5751 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5752 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5754 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5755 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5756 MSIREG_DeleteUserProductKey(package->ProductCode);
5757 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5759 upgrade = msi_dup_property(package->db, szUpgradeCode);
5762 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5763 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5767 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5769 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5772 return ERROR_SUCCESS;
5775 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5780 /* turn off scheduling */
5781 package->script->CurrentlyScripting= FALSE;
5783 /* first do the same as an InstallExecute */
5784 rc = ACTION_InstallExecute(package);
5785 if (rc != ERROR_SUCCESS)
5788 /* then handle Commit Actions */
5789 rc = execute_script(package,COMMIT_SCRIPT);
5790 if (rc != ERROR_SUCCESS)
5793 remove = msi_dup_property(package->db, szRemove);
5795 rc = msi_unpublish_product(package, remove);
5801 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5803 static const WCHAR RunOnce[] = {
5804 'S','o','f','t','w','a','r','e','\\',
5805 'M','i','c','r','o','s','o','f','t','\\',
5806 'W','i','n','d','o','w','s','\\',
5807 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5808 'R','u','n','O','n','c','e',0};
5809 static const WCHAR InstallRunOnce[] = {
5810 'S','o','f','t','w','a','r','e','\\',
5811 'M','i','c','r','o','s','o','f','t','\\',
5812 'W','i','n','d','o','w','s','\\',
5813 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5814 'I','n','s','t','a','l','l','e','r','\\',
5815 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5817 static const WCHAR msiexec_fmt[] = {
5819 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5820 '\"','%','s','\"',0};
5821 static const WCHAR install_fmt[] = {
5822 '/','I',' ','\"','%','s','\"',' ',
5823 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5824 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5825 WCHAR buffer[256], sysdir[MAX_PATH];
5827 WCHAR squished_pc[100];
5829 squash_guid(package->ProductCode,squished_pc);
5831 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5832 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5833 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5836 msi_reg_set_val_str( hkey, squished_pc, buffer );
5839 TRACE("Reboot command %s\n",debugstr_w(buffer));
5841 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5842 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5844 msi_reg_set_val_str( hkey, squished_pc, buffer );
5847 return ERROR_INSTALL_SUSPEND;
5850 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5852 static const WCHAR query[] =
5853 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5854 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5855 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5856 MSIRECORD *rec, *row;
5862 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5864 rec = MSI_CreateRecord( count + 2 );
5865 str = MSI_RecordGetString( row, 1 );
5866 MSI_RecordSetStringW( rec, 0, str );
5867 msiobj_release( &row->hdr );
5868 MSI_RecordSetInteger( rec, 1, error );
5870 va_start( va, count );
5871 for (i = 0; i < count; i++)
5873 str = va_arg( va, const WCHAR *);
5874 MSI_RecordSetStringW( rec, i + 2, str );
5878 MSI_FormatRecordW( package, rec, NULL, &size );
5880 data = msi_alloc( size * sizeof(WCHAR) );
5881 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5883 msiobj_release( &rec->hdr );
5887 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5893 * We are currently doing what should be done here in the top level Install
5894 * however for Administrative and uninstalls this step will be needed
5896 if (!package->PackagePath)
5897 return ERROR_SUCCESS;
5899 msi_set_sourcedir_props(package, TRUE);
5901 attrib = GetFileAttributesW(package->db->path);
5902 if (attrib == INVALID_FILE_ATTRIBUTES)
5908 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5909 package->Context, MSICODE_PRODUCT,
5910 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5911 if (rc == ERROR_MORE_DATA)
5913 prompt = msi_alloc(size * sizeof(WCHAR));
5914 MsiSourceListGetInfoW(package->ProductCode, NULL,
5915 package->Context, MSICODE_PRODUCT,
5916 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5919 prompt = strdupW(package->db->path);
5921 msg = msi_build_error_string(package, 1302, 1, prompt);
5922 while(attrib == INVALID_FILE_ATTRIBUTES)
5924 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
5927 rc = ERROR_INSTALL_USEREXIT;
5930 attrib = GetFileAttributesW(package->db->path);
5936 return ERROR_SUCCESS;
5941 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5944 LPWSTR buffer, productid = NULL;
5945 UINT i, rc = ERROR_SUCCESS;
5948 static const WCHAR szPropKeys[][80] =
5950 {'P','r','o','d','u','c','t','I','D',0},
5951 {'U','S','E','R','N','A','M','E',0},
5952 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5956 static const WCHAR szRegKeys[][80] =
5958 {'P','r','o','d','u','c','t','I','D',0},
5959 {'R','e','g','O','w','n','e','r',0},
5960 {'R','e','g','C','o','m','p','a','n','y',0},
5964 if (msi_check_unpublish(package))
5966 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5970 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5974 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5976 if (rc != ERROR_SUCCESS)
5979 for( i = 0; szPropKeys[i][0]; i++ )
5981 buffer = msi_dup_property( package->db, szPropKeys[i] );
5982 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5987 uirow = MSI_CreateRecord( 1 );
5988 MSI_RecordSetStringW( uirow, 1, productid );
5989 msi_ui_actiondata( package, szRegisterUser, uirow );
5990 msiobj_release( &uirow->hdr );
5992 msi_free(productid);
5998 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
6002 package->script->InWhatSequence |= SEQUENCE_EXEC;
6003 rc = ACTION_ProcessExecSequence(package,FALSE);
6007 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
6009 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
6010 WCHAR productid_85[21], component_85[21], *ret;
6014 /* > is used if there is a component GUID and < if not. */
6016 productid_85[0] = 0;
6017 component_85[0] = 0;
6018 CLSIDFromString( package->ProductCode, &clsid );
6020 encode_base85_guid( &clsid, productid_85 );
6023 CLSIDFromString( component->ComponentId, &clsid );
6024 encode_base85_guid( &clsid, component_85 );
6027 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
6028 debugstr_w(component_85));
6030 sz = 20 + strlenW( feature ) + 20 + 3;
6031 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
6032 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
6036 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
6038 MSIPACKAGE *package = param;
6039 LPCWSTR compgroupid, component, feature, qualifier, text;
6040 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
6049 feature = MSI_RecordGetString(rec, 5);
6050 feat = msi_get_loaded_feature(package, feature);
6052 return ERROR_SUCCESS;
6054 if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
6055 feat->ActionRequest != INSTALLSTATE_SOURCE &&
6056 feat->ActionRequest != INSTALLSTATE_ADVERTISED)
6058 TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
6059 feat->Action = feat->Installed;
6060 return ERROR_SUCCESS;
6063 component = MSI_RecordGetString(rec, 3);
6064 comp = msi_get_loaded_component(package, component);
6066 return ERROR_SUCCESS;
6068 compgroupid = MSI_RecordGetString(rec,1);
6069 qualifier = MSI_RecordGetString(rec,2);
6071 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
6072 if (rc != ERROR_SUCCESS)
6075 advertise = msi_create_component_advertise_string( package, comp, feature );
6076 text = MSI_RecordGetString( rec, 4 );
6079 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
6080 strcpyW( p, advertise );
6082 msi_free( advertise );
6085 existing = msi_reg_get_val_str( hkey, qualifier );
6087 sz = strlenW( advertise ) + 1;
6090 for (p = existing; *p; p += len)
6092 len = strlenW( p ) + 1;
6093 if (strcmpW( advertise, p )) sz += len;
6096 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
6098 rc = ERROR_OUTOFMEMORY;
6104 for (p = existing; *p; p += len)
6106 len = strlenW( p ) + 1;
6107 if (strcmpW( advertise, p ))
6109 memcpy( q, p, len * sizeof(WCHAR) );
6114 strcpyW( q, advertise );
6115 q[strlenW( q ) + 1] = 0;
6117 msi_reg_set_val_multi_str( hkey, qualifier, output );
6122 msi_free( advertise );
6123 msi_free( existing );
6126 uirow = MSI_CreateRecord( 2 );
6127 MSI_RecordSetStringW( uirow, 1, compgroupid );
6128 MSI_RecordSetStringW( uirow, 2, qualifier);
6129 msi_ui_actiondata( package, szPublishComponents, uirow );
6130 msiobj_release( &uirow->hdr );
6131 /* FIXME: call ui_progress? */
6137 * At present I am ignorning the advertised components part of this and only
6138 * focusing on the qualified component sets
6140 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
6144 static const WCHAR ExecSeqQuery[] =
6145 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6146 '`','P','u','b','l','i','s','h',
6147 'C','o','m','p','o','n','e','n','t','`',0};
6149 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6150 if (rc != ERROR_SUCCESS)
6151 return ERROR_SUCCESS;
6153 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
6154 msiobj_release(&view->hdr);
6159 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
6161 static const WCHAR szInstallerComponents[] = {
6162 'S','o','f','t','w','a','r','e','\\',
6163 'M','i','c','r','o','s','o','f','t','\\',
6164 'I','n','s','t','a','l','l','e','r','\\',
6165 'C','o','m','p','o','n','e','n','t','s','\\',0};
6167 MSIPACKAGE *package = param;
6168 LPCWSTR compgroupid, component, feature, qualifier;
6172 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
6175 feature = MSI_RecordGetString( rec, 5 );
6176 feat = msi_get_loaded_feature( package, feature );
6178 return ERROR_SUCCESS;
6180 if (feat->ActionRequest != INSTALLSTATE_ABSENT)
6182 TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
6183 feat->Action = feat->Installed;
6184 return ERROR_SUCCESS;
6187 component = MSI_RecordGetString( rec, 3 );
6188 comp = msi_get_loaded_component( package, component );
6190 return ERROR_SUCCESS;
6192 compgroupid = MSI_RecordGetString( rec, 1 );
6193 qualifier = MSI_RecordGetString( rec, 2 );
6195 squash_guid( compgroupid, squashed );
6196 strcpyW( keypath, szInstallerComponents );
6197 strcatW( keypath, squashed );
6199 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
6200 if (res != ERROR_SUCCESS)
6202 WARN("Unable to delete component key %d\n", res);
6205 uirow = MSI_CreateRecord( 2 );
6206 MSI_RecordSetStringW( uirow, 1, compgroupid );
6207 MSI_RecordSetStringW( uirow, 2, qualifier );
6208 msi_ui_actiondata( package, szUnpublishComponents, uirow );
6209 msiobj_release( &uirow->hdr );
6211 return ERROR_SUCCESS;
6214 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6218 static const WCHAR query[] =
6219 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6220 '`','P','u','b','l','i','s','h',
6221 'C','o','m','p','o','n','e','n','t','`',0};
6223 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6224 if (rc != ERROR_SUCCESS)
6225 return ERROR_SUCCESS;
6227 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
6228 msiobj_release( &view->hdr );
6233 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
6235 static const WCHAR query[] =
6236 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6237 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
6238 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
6239 MSIPACKAGE *package = param;
6240 MSICOMPONENT *component;
6243 SC_HANDLE hscm = NULL, service = NULL;
6245 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
6246 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
6247 DWORD serv_type, start_type, err_control;
6248 SERVICE_DESCRIPTIONW sd = {NULL};
6250 comp = MSI_RecordGetString( rec, 12 );
6251 component = msi_get_loaded_component( package, comp );
6254 WARN("service component not found\n");
6257 if (!component->Enabled)
6259 TRACE("service component disabled\n");
6262 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
6265 ERR("Failed to open the SC Manager!\n");
6269 start_type = MSI_RecordGetInteger(rec, 5);
6270 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
6273 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
6274 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
6275 serv_type = MSI_RecordGetInteger(rec, 4);
6276 err_control = MSI_RecordGetInteger(rec, 6);
6277 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
6278 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
6279 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
6280 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
6281 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
6282 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
6284 /* fetch the service path */
6285 row = MSI_QueryGetRecord(package->db, query, comp);
6288 ERR("Query failed\n");
6291 key = MSI_RecordGetString(row, 6);
6292 file = msi_get_loaded_file(package, key);
6293 msiobj_release(&row->hdr);
6296 ERR("Failed to load the service file\n");
6300 if (!args || !args[0]) image_path = file->TargetPath;
6303 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
6304 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
6305 return ERROR_OUTOFMEMORY;
6307 strcpyW(image_path, file->TargetPath);
6308 strcatW(image_path, szSpace);
6309 strcatW(image_path, args);
6311 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
6312 start_type, err_control, image_path, load_order,
6313 NULL, depends, serv_name, pass);
6317 if (GetLastError() != ERROR_SERVICE_EXISTS)
6318 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
6320 else if (sd.lpDescription)
6322 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
6323 WARN("failed to set service description %u\n", GetLastError());
6326 if (image_path != file->TargetPath) msi_free(image_path);
6328 CloseServiceHandle(service);
6329 CloseServiceHandle(hscm);
6332 msi_free(sd.lpDescription);
6333 msi_free(load_order);
6334 msi_free(serv_name);
6339 return ERROR_SUCCESS;
6342 static UINT ACTION_InstallServices( MSIPACKAGE *package )
6346 static const WCHAR ExecSeqQuery[] =
6347 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6348 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
6350 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6351 if (rc != ERROR_SUCCESS)
6352 return ERROR_SUCCESS;
6354 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
6355 msiobj_release(&view->hdr);
6360 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
6361 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
6363 LPCWSTR *vector, *temp_vector;
6367 static const WCHAR separator[] = {'[','~',']',0};
6370 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
6375 vector = msi_alloc(sizeof(LPWSTR));
6383 vector[*numargs - 1] = p;
6385 if ((q = strstrW(p, separator)))
6389 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
6395 vector = temp_vector;
6404 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
6406 MSIPACKAGE *package = param;
6409 SC_HANDLE scm = NULL, service = NULL;
6410 LPCWSTR component, *vector = NULL;
6411 LPWSTR name, args, display_name = NULL;
6412 DWORD event, numargs, len;
6413 UINT r = ERROR_FUNCTION_FAILED;
6415 component = MSI_RecordGetString(rec, 6);
6416 comp = msi_get_loaded_component(package, component);
6418 return ERROR_SUCCESS;
6422 TRACE("component is disabled\n");
6423 return ERROR_SUCCESS;
6426 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
6428 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6429 comp->Action = comp->Installed;
6430 return ERROR_SUCCESS;
6432 comp->Action = INSTALLSTATE_LOCAL;
6434 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
6435 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6436 event = MSI_RecordGetInteger(rec, 3);
6438 if (!(event & msidbServiceControlEventStart))
6444 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
6447 ERR("Failed to open the service control manager\n");
6452 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6453 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6455 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6456 GetServiceDisplayNameW( scm, name, display_name, &len );
6459 service = OpenServiceW(scm, name, SERVICE_START);
6462 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6466 vector = msi_service_args_to_vector(args, &numargs);
6468 if (!StartServiceW(service, numargs, vector) &&
6469 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6471 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6478 uirow = MSI_CreateRecord( 2 );
6479 MSI_RecordSetStringW( uirow, 1, display_name );
6480 MSI_RecordSetStringW( uirow, 2, name );
6481 msi_ui_actiondata( package, szStartServices, uirow );
6482 msiobj_release( &uirow->hdr );
6484 CloseServiceHandle(service);
6485 CloseServiceHandle(scm);
6490 msi_free(display_name);
6494 static UINT ACTION_StartServices( MSIPACKAGE *package )
6499 static const WCHAR query[] = {
6500 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6501 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6503 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6504 if (rc != ERROR_SUCCESS)
6505 return ERROR_SUCCESS;
6507 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6508 msiobj_release(&view->hdr);
6513 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6515 DWORD i, needed, count;
6516 ENUM_SERVICE_STATUSW *dependencies;
6520 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6521 0, &needed, &count))
6524 if (GetLastError() != ERROR_MORE_DATA)
6527 dependencies = msi_alloc(needed);
6531 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6532 needed, &needed, &count))
6535 for (i = 0; i < count; i++)
6537 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6538 SERVICE_STOP | SERVICE_QUERY_STATUS);
6542 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
6549 msi_free(dependencies);
6553 static UINT stop_service( LPCWSTR name )
6555 SC_HANDLE scm = NULL, service = NULL;
6556 SERVICE_STATUS status;
6557 SERVICE_STATUS_PROCESS ssp;
6560 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6563 WARN("Failed to open the SCM: %d\n", GetLastError());
6567 service = OpenServiceW(scm, name,
6569 SERVICE_QUERY_STATUS |
6570 SERVICE_ENUMERATE_DEPENDENTS);
6573 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6577 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6578 sizeof(SERVICE_STATUS_PROCESS), &needed))
6580 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6584 if (ssp.dwCurrentState == SERVICE_STOPPED)
6587 stop_service_dependents(scm, service);
6589 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6590 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6593 CloseServiceHandle(service);
6594 CloseServiceHandle(scm);
6596 return ERROR_SUCCESS;
6599 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6601 MSIPACKAGE *package = param;
6605 LPWSTR name = NULL, display_name = NULL;
6609 event = MSI_RecordGetInteger( rec, 3 );
6610 if (!(event & msidbServiceControlEventStop))
6611 return ERROR_SUCCESS;
6613 component = MSI_RecordGetString( rec, 6 );
6614 comp = msi_get_loaded_component( package, component );
6616 return ERROR_SUCCESS;
6620 TRACE("component is disabled\n");
6621 return ERROR_SUCCESS;
6624 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6626 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6627 comp->Action = comp->Installed;
6628 return ERROR_SUCCESS;
6630 comp->Action = INSTALLSTATE_ABSENT;
6632 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6635 ERR("Failed to open the service control manager\n");
6640 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6641 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6643 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6644 GetServiceDisplayNameW( scm, name, display_name, &len );
6646 CloseServiceHandle( scm );
6648 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6649 stop_service( name );
6652 uirow = MSI_CreateRecord( 2 );
6653 MSI_RecordSetStringW( uirow, 1, display_name );
6654 MSI_RecordSetStringW( uirow, 2, name );
6655 msi_ui_actiondata( package, szStopServices, uirow );
6656 msiobj_release( &uirow->hdr );
6659 msi_free( display_name );
6660 return ERROR_SUCCESS;
6663 static UINT ACTION_StopServices( MSIPACKAGE *package )
6668 static const WCHAR query[] = {
6669 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6670 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6672 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6673 if (rc != ERROR_SUCCESS)
6674 return ERROR_SUCCESS;
6676 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6677 msiobj_release(&view->hdr);
6682 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6684 MSIPACKAGE *package = param;
6688 LPWSTR name = NULL, display_name = NULL;
6690 SC_HANDLE scm = NULL, service = NULL;
6692 event = MSI_RecordGetInteger( rec, 3 );
6693 if (!(event & msidbServiceControlEventDelete))
6694 return ERROR_SUCCESS;
6696 component = MSI_RecordGetString(rec, 6);
6697 comp = msi_get_loaded_component(package, component);
6699 return ERROR_SUCCESS;
6703 TRACE("component is disabled\n");
6704 return ERROR_SUCCESS;
6707 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6709 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6710 comp->Action = comp->Installed;
6711 return ERROR_SUCCESS;
6713 comp->Action = INSTALLSTATE_ABSENT;
6715 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6716 stop_service( name );
6718 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6721 WARN("Failed to open the SCM: %d\n", GetLastError());
6726 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6727 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6729 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6730 GetServiceDisplayNameW( scm, name, display_name, &len );
6733 service = OpenServiceW( scm, name, DELETE );
6736 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6740 if (!DeleteService( service ))
6741 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6744 uirow = MSI_CreateRecord( 2 );
6745 MSI_RecordSetStringW( uirow, 1, display_name );
6746 MSI_RecordSetStringW( uirow, 2, name );
6747 msi_ui_actiondata( package, szDeleteServices, uirow );
6748 msiobj_release( &uirow->hdr );
6750 CloseServiceHandle( service );
6751 CloseServiceHandle( scm );
6753 msi_free( display_name );
6755 return ERROR_SUCCESS;
6758 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6763 static const WCHAR query[] = {
6764 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6765 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6767 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6768 if (rc != ERROR_SUCCESS)
6769 return ERROR_SUCCESS;
6771 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6772 msiobj_release( &view->hdr );
6777 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6779 MSIPACKAGE *package = param;
6780 LPWSTR driver, driver_path, ptr;
6781 WCHAR outpath[MAX_PATH];
6782 MSIFILE *driver_file = NULL, *setup_file = NULL;
6785 LPCWSTR desc, file_key, component;
6787 UINT r = ERROR_SUCCESS;
6789 static const WCHAR driver_fmt[] = {
6790 'D','r','i','v','e','r','=','%','s',0};
6791 static const WCHAR setup_fmt[] = {
6792 'S','e','t','u','p','=','%','s',0};
6793 static const WCHAR usage_fmt[] = {
6794 'F','i','l','e','U','s','a','g','e','=','1',0};
6796 component = MSI_RecordGetString( rec, 2 );
6797 comp = msi_get_loaded_component( package, component );
6799 return ERROR_SUCCESS;
6803 TRACE("component is disabled\n");
6804 return ERROR_SUCCESS;
6807 desc = MSI_RecordGetString(rec, 3);
6809 file_key = MSI_RecordGetString( rec, 4 );
6810 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6812 file_key = MSI_RecordGetString( rec, 5 );
6813 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6817 ERR("ODBC Driver entry not found!\n");
6818 return ERROR_FUNCTION_FAILED;
6821 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6823 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6824 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6826 driver = msi_alloc(len * sizeof(WCHAR));
6828 return ERROR_OUTOFMEMORY;
6831 lstrcpyW(ptr, desc);
6832 ptr += lstrlenW(ptr) + 1;
6834 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6839 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6843 lstrcpyW(ptr, usage_fmt);
6844 ptr += lstrlenW(ptr) + 1;
6847 driver_path = strdupW(driver_file->TargetPath);
6848 ptr = strrchrW(driver_path, '\\');
6849 if (ptr) *ptr = '\0';
6851 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6852 NULL, ODBC_INSTALL_COMPLETE, &usage))
6854 ERR("Failed to install SQL driver!\n");
6855 r = ERROR_FUNCTION_FAILED;
6858 uirow = MSI_CreateRecord( 5 );
6859 MSI_RecordSetStringW( uirow, 1, desc );
6860 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6861 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6862 msi_ui_actiondata( package, szInstallODBC, uirow );
6863 msiobj_release( &uirow->hdr );
6866 msi_free(driver_path);
6871 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6873 MSIPACKAGE *package = param;
6874 LPWSTR translator, translator_path, ptr;
6875 WCHAR outpath[MAX_PATH];
6876 MSIFILE *translator_file = NULL, *setup_file = NULL;
6879 LPCWSTR desc, file_key, component;
6881 UINT r = ERROR_SUCCESS;
6883 static const WCHAR translator_fmt[] = {
6884 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6885 static const WCHAR setup_fmt[] = {
6886 'S','e','t','u','p','=','%','s',0};
6888 component = MSI_RecordGetString( rec, 2 );
6889 comp = msi_get_loaded_component( package, component );
6891 return ERROR_SUCCESS;
6895 TRACE("component is disabled\n");
6896 return ERROR_SUCCESS;
6899 desc = MSI_RecordGetString(rec, 3);
6901 file_key = MSI_RecordGetString( rec, 4 );
6902 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6904 file_key = MSI_RecordGetString( rec, 5 );
6905 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6907 if (!translator_file)
6909 ERR("ODBC Translator entry not found!\n");
6910 return ERROR_FUNCTION_FAILED;
6913 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6915 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6917 translator = msi_alloc(len * sizeof(WCHAR));
6919 return ERROR_OUTOFMEMORY;
6922 lstrcpyW(ptr, desc);
6923 ptr += lstrlenW(ptr) + 1;
6925 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6930 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6935 translator_path = strdupW(translator_file->TargetPath);
6936 ptr = strrchrW(translator_path, '\\');
6937 if (ptr) *ptr = '\0';
6939 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6940 NULL, ODBC_INSTALL_COMPLETE, &usage))
6942 ERR("Failed to install SQL translator!\n");
6943 r = ERROR_FUNCTION_FAILED;
6946 uirow = MSI_CreateRecord( 5 );
6947 MSI_RecordSetStringW( uirow, 1, desc );
6948 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6949 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6950 msi_ui_actiondata( package, szInstallODBC, uirow );
6951 msiobj_release( &uirow->hdr );
6953 msi_free(translator);
6954 msi_free(translator_path);
6959 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6961 MSIPACKAGE *package = param;
6964 LPCWSTR desc, driver, component;
6965 WORD request = ODBC_ADD_SYS_DSN;
6968 UINT r = ERROR_SUCCESS;
6971 static const WCHAR attrs_fmt[] = {
6972 'D','S','N','=','%','s',0 };
6974 component = MSI_RecordGetString( rec, 2 );
6975 comp = msi_get_loaded_component( package, component );
6977 return ERROR_SUCCESS;
6981 TRACE("component is disabled\n");
6982 return ERROR_SUCCESS;
6985 desc = MSI_RecordGetString(rec, 3);
6986 driver = MSI_RecordGetString(rec, 4);
6987 registration = MSI_RecordGetInteger(rec, 5);
6989 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6990 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6992 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6993 attrs = msi_alloc(len * sizeof(WCHAR));
6995 return ERROR_OUTOFMEMORY;
6997 len = sprintfW(attrs, attrs_fmt, desc);
7000 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
7002 ERR("Failed to install SQL data source!\n");
7003 r = ERROR_FUNCTION_FAILED;
7006 uirow = MSI_CreateRecord( 5 );
7007 MSI_RecordSetStringW( uirow, 1, desc );
7008 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
7009 MSI_RecordSetInteger( uirow, 3, request );
7010 msi_ui_actiondata( package, szInstallODBC, uirow );
7011 msiobj_release( &uirow->hdr );
7018 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
7023 static const WCHAR driver_query[] = {
7024 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7025 'O','D','B','C','D','r','i','v','e','r',0 };
7027 static const WCHAR translator_query[] = {
7028 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7029 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
7031 static const WCHAR source_query[] = {
7032 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7033 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
7035 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
7036 if (rc != ERROR_SUCCESS)
7037 return ERROR_SUCCESS;
7039 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
7040 msiobj_release(&view->hdr);
7042 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
7043 if (rc != ERROR_SUCCESS)
7044 return ERROR_SUCCESS;
7046 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
7047 msiobj_release(&view->hdr);
7049 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
7050 if (rc != ERROR_SUCCESS)
7051 return ERROR_SUCCESS;
7053 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
7054 msiobj_release(&view->hdr);
7059 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
7061 MSIPACKAGE *package = param;
7065 LPCWSTR desc, component;
7067 component = MSI_RecordGetString( rec, 2 );
7068 comp = msi_get_loaded_component( package, component );
7070 return ERROR_SUCCESS;
7074 TRACE("component is disabled\n");
7075 return ERROR_SUCCESS;
7078 desc = MSI_RecordGetString( rec, 3 );
7079 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
7081 WARN("Failed to remove ODBC driver\n");
7085 FIXME("Usage count reached 0\n");
7088 uirow = MSI_CreateRecord( 2 );
7089 MSI_RecordSetStringW( uirow, 1, desc );
7090 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
7091 msi_ui_actiondata( package, szRemoveODBC, uirow );
7092 msiobj_release( &uirow->hdr );
7094 return ERROR_SUCCESS;
7097 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
7099 MSIPACKAGE *package = param;
7103 LPCWSTR desc, component;
7105 component = MSI_RecordGetString( rec, 2 );
7106 comp = msi_get_loaded_component( package, component );
7108 return ERROR_SUCCESS;
7112 TRACE("component is disabled\n");
7113 return ERROR_SUCCESS;
7116 desc = MSI_RecordGetString( rec, 3 );
7117 if (!SQLRemoveTranslatorW( desc, &usage ))
7119 WARN("Failed to remove ODBC translator\n");
7123 FIXME("Usage count reached 0\n");
7126 uirow = MSI_CreateRecord( 2 );
7127 MSI_RecordSetStringW( uirow, 1, desc );
7128 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
7129 msi_ui_actiondata( package, szRemoveODBC, uirow );
7130 msiobj_release( &uirow->hdr );
7132 return ERROR_SUCCESS;
7135 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
7137 MSIPACKAGE *package = param;
7141 LPCWSTR desc, driver, component;
7142 WORD request = ODBC_REMOVE_SYS_DSN;
7146 static const WCHAR attrs_fmt[] = {
7147 'D','S','N','=','%','s',0 };
7149 component = MSI_RecordGetString( rec, 2 );
7150 comp = msi_get_loaded_component( package, component );
7152 return ERROR_SUCCESS;
7156 TRACE("component is disabled\n");
7157 return ERROR_SUCCESS;
7160 desc = MSI_RecordGetString( rec, 3 );
7161 driver = MSI_RecordGetString( rec, 4 );
7162 registration = MSI_RecordGetInteger( rec, 5 );
7164 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
7165 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
7167 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
7168 attrs = msi_alloc( len * sizeof(WCHAR) );
7170 return ERROR_OUTOFMEMORY;
7172 FIXME("Use ODBCSourceAttribute table\n");
7174 len = sprintfW( attrs, attrs_fmt, desc );
7177 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
7179 WARN("Failed to remove ODBC data source\n");
7183 uirow = MSI_CreateRecord( 3 );
7184 MSI_RecordSetStringW( uirow, 1, desc );
7185 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
7186 MSI_RecordSetInteger( uirow, 3, request );
7187 msi_ui_actiondata( package, szRemoveODBC, uirow );
7188 msiobj_release( &uirow->hdr );
7190 return ERROR_SUCCESS;
7193 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
7198 static const WCHAR driver_query[] = {
7199 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7200 'O','D','B','C','D','r','i','v','e','r',0 };
7202 static const WCHAR translator_query[] = {
7203 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7204 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
7206 static const WCHAR source_query[] = {
7207 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7208 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
7210 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7211 if (rc != ERROR_SUCCESS)
7212 return ERROR_SUCCESS;
7214 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
7215 msiobj_release( &view->hdr );
7217 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7218 if (rc != ERROR_SUCCESS)
7219 return ERROR_SUCCESS;
7221 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
7222 msiobj_release( &view->hdr );
7224 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
7225 if (rc != ERROR_SUCCESS)
7226 return ERROR_SUCCESS;
7228 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
7229 msiobj_release( &view->hdr );
7234 #define ENV_ACT_SETALWAYS 0x1
7235 #define ENV_ACT_SETABSENT 0x2
7236 #define ENV_ACT_REMOVE 0x4
7237 #define ENV_ACT_REMOVEMATCH 0x8
7239 #define ENV_MOD_MACHINE 0x20000000
7240 #define ENV_MOD_APPEND 0x40000000
7241 #define ENV_MOD_PREFIX 0x80000000
7242 #define ENV_MOD_MASK 0xC0000000
7244 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
7246 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
7248 LPCWSTR cptr = *name;
7250 static const WCHAR prefix[] = {'[','~',']',0};
7251 static const int prefix_len = 3;
7257 *flags |= ENV_ACT_SETALWAYS;
7258 else if (*cptr == '+')
7259 *flags |= ENV_ACT_SETABSENT;
7260 else if (*cptr == '-')
7261 *flags |= ENV_ACT_REMOVE;
7262 else if (*cptr == '!')
7263 *flags |= ENV_ACT_REMOVEMATCH;
7264 else if (*cptr == '*')
7265 *flags |= ENV_MOD_MACHINE;
7275 ERR("Missing environment variable\n");
7276 return ERROR_FUNCTION_FAILED;
7281 LPCWSTR ptr = *value;
7282 if (!strncmpW(ptr, prefix, prefix_len))
7284 if (ptr[prefix_len] == szSemiColon[0])
7286 *flags |= ENV_MOD_APPEND;
7287 *value += lstrlenW(prefix);
7294 else if (lstrlenW(*value) >= prefix_len)
7296 ptr += lstrlenW(ptr) - prefix_len;
7297 if (!strcmpW( ptr, prefix ))
7299 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
7301 *flags |= ENV_MOD_PREFIX;
7302 /* the "[~]" will be removed by deformat_string */;
7312 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
7313 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
7314 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
7315 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
7317 ERR("Invalid flags: %08x\n", *flags);
7318 return ERROR_FUNCTION_FAILED;
7322 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
7324 return ERROR_SUCCESS;
7327 static UINT open_env_key( DWORD flags, HKEY *key )
7329 static const WCHAR user_env[] =
7330 {'E','n','v','i','r','o','n','m','e','n','t',0};
7331 static const WCHAR machine_env[] =
7332 {'S','y','s','t','e','m','\\',
7333 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
7334 'C','o','n','t','r','o','l','\\',
7335 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
7336 'E','n','v','i','r','o','n','m','e','n','t',0};
7341 if (flags & ENV_MOD_MACHINE)
7344 root = HKEY_LOCAL_MACHINE;
7349 root = HKEY_CURRENT_USER;
7352 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
7353 if (res != ERROR_SUCCESS)
7355 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
7356 return ERROR_FUNCTION_FAILED;
7359 return ERROR_SUCCESS;
7362 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
7364 MSIPACKAGE *package = param;
7365 LPCWSTR name, value, component;
7366 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
7367 DWORD flags, type, size;
7374 component = MSI_RecordGetString(rec, 4);
7375 comp = msi_get_loaded_component(package, component);
7377 return ERROR_SUCCESS;
7381 TRACE("component is disabled\n");
7382 return ERROR_SUCCESS;
7385 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
7387 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
7388 comp->Action = comp->Installed;
7389 return ERROR_SUCCESS;
7391 comp->Action = INSTALLSTATE_LOCAL;
7393 name = MSI_RecordGetString(rec, 2);
7394 value = MSI_RecordGetString(rec, 3);
7396 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7398 res = env_parse_flags(&name, &value, &flags);
7399 if (res != ERROR_SUCCESS || !value)
7402 if (value && !deformat_string(package, value, &deformatted))
7404 res = ERROR_OUTOFMEMORY;
7408 value = deformatted;
7410 res = open_env_key( flags, &env );
7411 if (res != ERROR_SUCCESS)
7414 if (flags & ENV_MOD_MACHINE)
7415 action |= 0x20000000;
7419 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
7420 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7421 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7424 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
7428 /* Nothing to do. */
7431 res = ERROR_SUCCESS;
7435 /* If we are appending but the string was empty, strip ; */
7436 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
7438 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
7439 newval = strdupW(value);
7442 res = ERROR_OUTOFMEMORY;
7450 /* Contrary to MSDN, +-variable to [~];path works */
7451 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7453 res = ERROR_SUCCESS;
7457 data = msi_alloc(size);
7461 return ERROR_OUTOFMEMORY;
7464 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
7465 if (res != ERROR_SUCCESS)
7468 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7471 res = RegDeleteValueW(env, name);
7472 if (res != ERROR_SUCCESS)
7473 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7477 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
7478 if (flags & ENV_MOD_MASK)
7482 if (flags & ENV_MOD_APPEND) multiplier++;
7483 if (flags & ENV_MOD_PREFIX) multiplier++;
7484 mod_size = lstrlenW(value) * multiplier;
7485 size += mod_size * sizeof(WCHAR);
7488 newval = msi_alloc(size);
7492 res = ERROR_OUTOFMEMORY;
7496 if (flags & ENV_MOD_PREFIX)
7498 lstrcpyW(newval, value);
7499 ptr = newval + lstrlenW(value);
7500 action |= 0x80000000;
7503 lstrcpyW(ptr, data);
7505 if (flags & ENV_MOD_APPEND)
7507 lstrcatW(newval, value);
7508 action |= 0x40000000;
7511 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7512 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7515 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7519 uirow = MSI_CreateRecord( 3 );
7520 MSI_RecordSetStringW( uirow, 1, name );
7521 MSI_RecordSetStringW( uirow, 2, newval );
7522 MSI_RecordSetInteger( uirow, 3, action );
7523 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7524 msiobj_release( &uirow->hdr );
7526 if (env) RegCloseKey(env);
7527 msi_free(deformatted);
7533 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7537 static const WCHAR ExecSeqQuery[] =
7538 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7539 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7540 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
7541 if (rc != ERROR_SUCCESS)
7542 return ERROR_SUCCESS;
7544 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7545 msiobj_release(&view->hdr);
7550 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7552 MSIPACKAGE *package = param;
7553 LPCWSTR name, value, component;
7554 LPWSTR deformatted = NULL;
7563 component = MSI_RecordGetString( rec, 4 );
7564 comp = msi_get_loaded_component( package, component );
7566 return ERROR_SUCCESS;
7570 TRACE("component is disabled\n");
7571 return ERROR_SUCCESS;
7574 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
7576 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
7577 comp->Action = comp->Installed;
7578 return ERROR_SUCCESS;
7580 comp->Action = INSTALLSTATE_ABSENT;
7582 name = MSI_RecordGetString( rec, 2 );
7583 value = MSI_RecordGetString( rec, 3 );
7585 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7587 r = env_parse_flags( &name, &value, &flags );
7588 if (r != ERROR_SUCCESS)
7591 if (!(flags & ENV_ACT_REMOVE))
7593 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7594 return ERROR_SUCCESS;
7597 if (value && !deformat_string( package, value, &deformatted ))
7598 return ERROR_OUTOFMEMORY;
7600 value = deformatted;
7602 r = open_env_key( flags, &env );
7603 if (r != ERROR_SUCCESS)
7609 if (flags & ENV_MOD_MACHINE)
7610 action |= 0x20000000;
7612 TRACE("Removing %s\n", debugstr_w(name));
7614 res = RegDeleteValueW( env, name );
7615 if (res != ERROR_SUCCESS)
7617 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7622 uirow = MSI_CreateRecord( 3 );
7623 MSI_RecordSetStringW( uirow, 1, name );
7624 MSI_RecordSetStringW( uirow, 2, value );
7625 MSI_RecordSetInteger( uirow, 3, action );
7626 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7627 msiobj_release( &uirow->hdr );
7629 if (env) RegCloseKey( env );
7630 msi_free( deformatted );
7634 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7638 static const WCHAR query[] =
7639 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7640 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7642 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7643 if (rc != ERROR_SUCCESS)
7644 return ERROR_SUCCESS;
7646 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7647 msiobj_release( &view->hdr );
7652 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7654 LPWSTR key, template, id;
7655 UINT r = ERROR_SUCCESS;
7657 id = msi_dup_property( package->db, szProductID );
7661 return ERROR_SUCCESS;
7663 template = msi_dup_property( package->db, szPIDTemplate );
7664 key = msi_dup_property( package->db, szPIDKEY );
7666 if (key && template)
7668 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7669 r = msi_set_property( package->db, szProductID, key );
7671 msi_free( template );
7676 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7679 package->need_reboot = 1;
7680 return ERROR_SUCCESS;
7683 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7685 static const WCHAR szAvailableFreeReg[] =
7686 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7688 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7690 TRACE("%p %d kilobytes\n", package, space);
7692 uirow = MSI_CreateRecord( 1 );
7693 MSI_RecordSetInteger( uirow, 1, space );
7694 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7695 msiobj_release( &uirow->hdr );
7697 return ERROR_SUCCESS;
7700 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7702 FIXME("%p\n", package);
7703 return ERROR_SUCCESS;
7706 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7708 FIXME("%p\n", package);
7709 return ERROR_SUCCESS;
7712 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7717 static const WCHAR driver_query[] = {
7718 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7719 'O','D','B','C','D','r','i','v','e','r',0 };
7721 static const WCHAR translator_query[] = {
7722 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7723 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
7725 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7726 if (r == ERROR_SUCCESS)
7729 r = MSI_IterateRecords( view, &count, NULL, package );
7730 msiobj_release( &view->hdr );
7731 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7734 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7735 if (r == ERROR_SUCCESS)
7738 r = MSI_IterateRecords( view, &count, NULL, package );
7739 msiobj_release( &view->hdr );
7740 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7743 return ERROR_SUCCESS;
7746 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7748 MSIPACKAGE *package = param;
7749 const WCHAR *property = MSI_RecordGetString( rec, 1 );
7752 if ((value = msi_dup_property( package->db, property )))
7754 FIXME("remove %s\n", debugstr_w(value));
7757 return ERROR_SUCCESS;
7760 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7765 static const WCHAR query[] =
7766 {'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',
7767 ' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7769 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7770 if (r == ERROR_SUCCESS)
7772 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7773 msiobj_release( &view->hdr );
7775 return ERROR_SUCCESS;
7778 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7780 MSIPACKAGE *package = param;
7781 int attributes = MSI_RecordGetInteger( rec, 5 );
7783 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7785 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7786 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7787 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7788 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7792 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7794 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7795 if (r != ERROR_SUCCESS)
7796 return ERROR_SUCCESS;
7800 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7801 if (r != ERROR_SUCCESS)
7802 return ERROR_SUCCESS;
7804 RegCloseKey( hkey );
7806 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7807 debugstr_w(upgrade_code), debugstr_w(version_min),
7808 debugstr_w(version_max), debugstr_w(language));
7810 return ERROR_SUCCESS;
7813 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7818 static const WCHAR query[] =
7819 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7821 if (msi_get_property_int( package->db, szInstalled, 0 ))
7823 TRACE("product is installed, skipping action\n");
7824 return ERROR_SUCCESS;
7826 if (msi_get_property_int( package->db, szPreselected, 0 ))
7828 TRACE("Preselected property is set, not migrating feature states\n");
7829 return ERROR_SUCCESS;
7832 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7833 if (r == ERROR_SUCCESS)
7835 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7836 msiobj_release( &view->hdr );
7838 return ERROR_SUCCESS;
7841 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7842 LPCSTR action, LPCWSTR table )
7844 static const WCHAR query[] = {
7845 'S','E','L','E','C','T',' ','*',' ',
7846 'F','R','O','M',' ','`','%','s','`',0 };
7847 MSIQUERY *view = NULL;
7851 r = MSI_OpenQuery( package->db, &view, query, table );
7852 if (r == ERROR_SUCCESS)
7854 r = MSI_IterateRecords(view, &count, NULL, package);
7855 msiobj_release(&view->hdr);
7859 FIXME("%s -> %u ignored %s table values\n",
7860 action, count, debugstr_w(table));
7862 return ERROR_SUCCESS;
7865 static UINT ACTION_BindImage( MSIPACKAGE *package )
7867 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
7868 return msi_unimplemented_action_stub( package, "BindImage", table );
7871 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7873 static const WCHAR table[] = {
7874 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7875 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7878 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7880 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7881 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7884 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7886 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7887 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7890 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7892 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7893 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7896 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7898 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7899 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7902 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
7906 const WCHAR *action;
7907 UINT (*handler)(MSIPACKAGE *);
7911 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
7912 { szAppSearch, ACTION_AppSearch },
7913 { szBindImage, ACTION_BindImage },
7914 { szCCPSearch, ACTION_CCPSearch },
7915 { szCostFinalize, ACTION_CostFinalize },
7916 { szCostInitialize, ACTION_CostInitialize },
7917 { szCreateFolders, ACTION_CreateFolders },
7918 { szCreateShortcuts, ACTION_CreateShortcuts },
7919 { szDeleteServices, ACTION_DeleteServices },
7920 { szDisableRollback, ACTION_DisableRollback },
7921 { szDuplicateFiles, ACTION_DuplicateFiles },
7922 { szExecuteAction, ACTION_ExecuteAction },
7923 { szFileCost, ACTION_FileCost },
7924 { szFindRelatedProducts, ACTION_FindRelatedProducts },
7925 { szForceReboot, ACTION_ForceReboot },
7926 { szInstallAdminPackage, ACTION_InstallAdminPackage },
7927 { szInstallExecute, ACTION_InstallExecute },
7928 { szInstallExecuteAgain, ACTION_InstallExecute },
7929 { szInstallFiles, ACTION_InstallFiles},
7930 { szInstallFinalize, ACTION_InstallFinalize },
7931 { szInstallInitialize, ACTION_InstallInitialize },
7932 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
7933 { szInstallValidate, ACTION_InstallValidate },
7934 { szIsolateComponents, ACTION_IsolateComponents },
7935 { szLaunchConditions, ACTION_LaunchConditions },
7936 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
7937 { szMoveFiles, ACTION_MoveFiles },
7938 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
7939 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
7940 { szInstallODBC, ACTION_InstallODBC },
7941 { szInstallServices, ACTION_InstallServices },
7942 { szPatchFiles, ACTION_PatchFiles },
7943 { szProcessComponents, ACTION_ProcessComponents },
7944 { szPublishComponents, ACTION_PublishComponents },
7945 { szPublishFeatures, ACTION_PublishFeatures },
7946 { szPublishProduct, ACTION_PublishProduct },
7947 { szRegisterClassInfo, ACTION_RegisterClassInfo },
7948 { szRegisterComPlus, ACTION_RegisterComPlus},
7949 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
7950 { szRegisterFonts, ACTION_RegisterFonts },
7951 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
7952 { szRegisterProduct, ACTION_RegisterProduct },
7953 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
7954 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
7955 { szRegisterUser, ACTION_RegisterUser },
7956 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
7957 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
7958 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
7959 { szRemoveFiles, ACTION_RemoveFiles },
7960 { szRemoveFolders, ACTION_RemoveFolders },
7961 { szRemoveIniValues, ACTION_RemoveIniValues },
7962 { szRemoveODBC, ACTION_RemoveODBC },
7963 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
7964 { szRemoveShortcuts, ACTION_RemoveShortcuts },
7965 { szResolveSource, ACTION_ResolveSource },
7966 { szRMCCPSearch, ACTION_RMCCPSearch },
7967 { szScheduleReboot, ACTION_ScheduleReboot },
7968 { szSelfRegModules, ACTION_SelfRegModules },
7969 { szSelfUnregModules, ACTION_SelfUnregModules },
7970 { szSetODBCFolders, ACTION_SetODBCFolders },
7971 { szStartServices, ACTION_StartServices },
7972 { szStopServices, ACTION_StopServices },
7973 { szUnpublishComponents, ACTION_UnpublishComponents },
7974 { szUnpublishFeatures, ACTION_UnpublishFeatures },
7975 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
7976 { szUnregisterComPlus, ACTION_UnregisterComPlus },
7977 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
7978 { szUnregisterFonts, ACTION_UnregisterFonts },
7979 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
7980 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
7981 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
7982 { szValidateProductID, ACTION_ValidateProductID },
7983 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
7984 { szWriteIniValues, ACTION_WriteIniValues },
7985 { szWriteRegistryValues, ACTION_WriteRegistryValues },
7989 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7995 while (StandardActions[i].action != NULL)
7997 if (!strcmpW( StandardActions[i].action, action ))
7999 ui_actionstart( package, action );
8000 if (StandardActions[i].handler)
8002 ui_actioninfo( package, action, TRUE, 0 );
8003 *rc = StandardActions[i].handler( package );
8004 ui_actioninfo( package, action, FALSE, *rc );
8008 FIXME("unhandled standard action %s\n", debugstr_w(action));
8009 *rc = ERROR_SUCCESS;
8019 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
8021 UINT rc = ERROR_SUCCESS;
8024 TRACE("Performing action (%s)\n", debugstr_w(action));
8026 handled = ACTION_HandleStandardAction(package, action, &rc);
8029 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
8033 WARN("unhandled msi action %s\n", debugstr_w(action));
8034 rc = ERROR_FUNCTION_NOT_CALLED;
8040 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
8042 UINT rc = ERROR_SUCCESS;
8043 BOOL handled = FALSE;
8045 TRACE("Performing action (%s)\n", debugstr_w(action));
8047 handled = ACTION_HandleStandardAction(package, action, &rc);
8050 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
8052 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
8057 WARN("unhandled msi action %s\n", debugstr_w(action));
8058 rc = ERROR_FUNCTION_NOT_CALLED;
8064 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
8066 UINT rc = ERROR_SUCCESS;
8069 static const WCHAR ExecSeqQuery[] =
8070 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
8071 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
8072 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
8073 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
8074 static const WCHAR UISeqQuery[] =
8075 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
8076 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
8077 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
8078 ' ', '=',' ','%','i',0};
8080 if (needs_ui_sequence(package))
8081 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
8083 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
8087 LPCWSTR action, cond;
8089 TRACE("Running the actions\n");
8091 /* check conditions */
8092 cond = MSI_RecordGetString(row, 2);
8094 /* this is a hack to skip errors in the condition code */
8095 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
8097 msiobj_release(&row->hdr);
8098 return ERROR_SUCCESS;
8101 action = MSI_RecordGetString(row, 1);
8104 ERR("failed to fetch action\n");
8105 msiobj_release(&row->hdr);
8106 return ERROR_FUNCTION_FAILED;
8109 if (needs_ui_sequence(package))
8110 rc = ACTION_PerformUIAction(package, action, -1);
8112 rc = ACTION_PerformAction(package, action, -1);
8114 msiobj_release(&row->hdr);
8120 /****************************************************
8121 * TOP level entry points
8122 *****************************************************/
8124 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
8125 LPCWSTR szCommandLine )
8130 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
8131 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
8133 msi_set_property( package->db, szAction, szInstall );
8135 package->script->InWhatSequence = SEQUENCE_INSTALL;
8142 dir = strdupW(szPackagePath);
8143 p = strrchrW(dir, '\\');
8147 file = szPackagePath + (p - dir);
8152 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
8153 GetCurrentDirectoryW(MAX_PATH, dir);
8154 lstrcatW(dir, szBackSlash);
8155 file = szPackagePath;
8158 msi_free( package->PackagePath );
8159 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
8160 if (!package->PackagePath)
8163 return ERROR_OUTOFMEMORY;
8166 lstrcpyW(package->PackagePath, dir);
8167 lstrcatW(package->PackagePath, file);
8170 msi_set_sourcedir_props(package, FALSE);
8173 rc = msi_parse_command_line( package, szCommandLine, FALSE );
8174 if (rc != ERROR_SUCCESS)
8177 msi_apply_transforms( package );
8178 msi_apply_patches( package );
8180 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
8182 TRACE("setting reinstall property\n");
8183 msi_set_property( package->db, szReinstall, szAll );
8186 /* properties may have been added by a transform */
8187 msi_clone_properties( package );
8189 msi_parse_command_line( package, szCommandLine, FALSE );
8190 msi_adjust_privilege_properties( package );
8191 msi_set_context( package );
8193 if (needs_ui_sequence( package))
8195 package->script->InWhatSequence |= SEQUENCE_UI;
8196 rc = ACTION_ProcessUISequence(package);
8197 ui_exists = ui_sequence_exists(package);
8198 if (rc == ERROR_SUCCESS || !ui_exists)
8200 package->script->InWhatSequence |= SEQUENCE_EXEC;
8201 rc = ACTION_ProcessExecSequence(package, ui_exists);
8205 rc = ACTION_ProcessExecSequence(package, FALSE);
8207 package->script->CurrentlyScripting = FALSE;
8209 /* process the ending type action */
8210 if (rc == ERROR_SUCCESS)
8211 ACTION_PerformActionSequence(package, -1);
8212 else if (rc == ERROR_INSTALL_USEREXIT)
8213 ACTION_PerformActionSequence(package, -2);
8214 else if (rc == ERROR_INSTALL_SUSPEND)
8215 ACTION_PerformActionSequence(package, -4);
8217 ACTION_PerformActionSequence(package, -3);
8219 /* finish up running custom actions */
8220 ACTION_FinishCustomActions(package);
8222 if (rc == ERROR_SUCCESS && package->need_reboot)
8223 return ERROR_SUCCESS_REBOOT_REQUIRED;