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"
40 #include "wine/unicode.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
49 * consts and values used
51 static const WCHAR c_colon[] = {'C',':','\\',0};
53 static const WCHAR szCreateFolders[] =
54 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
55 static const WCHAR szCostFinalize[] =
56 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
57 static const WCHAR szWriteRegistryValues[] =
58 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
59 static const WCHAR szCostInitialize[] =
60 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
61 static const WCHAR szFileCost[] =
62 {'F','i','l','e','C','o','s','t',0};
63 static const WCHAR szInstallInitialize[] =
64 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
65 static const WCHAR szInstallValidate[] =
66 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
67 static const WCHAR szLaunchConditions[] =
68 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
69 static const WCHAR szProcessComponents[] =
70 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
71 static const WCHAR szRegisterTypeLibraries[] =
72 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
73 static const WCHAR szCreateShortcuts[] =
74 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
75 static const WCHAR szPublishProduct[] =
76 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
77 static const WCHAR szWriteIniValues[] =
78 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
79 static const WCHAR szSelfRegModules[] =
80 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
81 static const WCHAR szPublishFeatures[] =
82 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
83 static const WCHAR szRegisterProduct[] =
84 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
85 static const WCHAR szInstallExecute[] =
86 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
87 static const WCHAR szInstallExecuteAgain[] =
88 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
89 static const WCHAR szInstallFinalize[] =
90 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
91 static const WCHAR szForceReboot[] =
92 {'F','o','r','c','e','R','e','b','o','o','t',0};
93 static const WCHAR szResolveSource[] =
94 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
95 static const WCHAR szAppSearch[] =
96 {'A','p','p','S','e','a','r','c','h',0};
97 static const WCHAR szAllocateRegistrySpace[] =
98 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
99 static const WCHAR szBindImage[] =
100 {'B','i','n','d','I','m','a','g','e',0};
101 static const WCHAR szCCPSearch[] =
102 {'C','C','P','S','e','a','r','c','h',0};
103 static const WCHAR szDeleteServices[] =
104 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
105 static const WCHAR szDisableRollback[] =
106 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
107 static const WCHAR szExecuteAction[] =
108 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
109 static const WCHAR szInstallAdminPackage[] =
110 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
111 static const WCHAR szInstallSFPCatalogFile[] =
112 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
113 static const WCHAR szIsolateComponents[] =
114 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
115 static const WCHAR szMigrateFeatureStates[] =
116 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
117 static const WCHAR szMoveFiles[] =
118 {'M','o','v','e','F','i','l','e','s',0};
119 static const WCHAR szMsiPublishAssemblies[] =
120 {'M','s','i','P','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
121 static const WCHAR szMsiUnpublishAssemblies[] =
122 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
123 static const WCHAR szInstallODBC[] =
124 {'I','n','s','t','a','l','l','O','D','B','C',0};
125 static const WCHAR szInstallServices[] =
126 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
127 static const WCHAR szPatchFiles[] =
128 {'P','a','t','c','h','F','i','l','e','s',0};
129 static const WCHAR szPublishComponents[] =
130 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
131 static const WCHAR szRegisterComPlus[] =
132 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
133 static const WCHAR szRegisterFonts[] =
134 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
135 static const WCHAR szRegisterUser[] =
136 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
137 static const WCHAR szRemoveDuplicateFiles[] =
138 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
139 static const WCHAR szRemoveEnvironmentStrings[] =
140 {'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};
141 static const WCHAR szRemoveExistingProducts[] =
142 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
143 static const WCHAR szRemoveFolders[] =
144 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
145 static const WCHAR szRemoveIniValues[] =
146 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
147 static const WCHAR szRemoveODBC[] =
148 {'R','e','m','o','v','e','O','D','B','C',0};
149 static const WCHAR szRemoveRegistryValues[] =
150 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
151 static const WCHAR szRemoveShortcuts[] =
152 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
153 static const WCHAR szRMCCPSearch[] =
154 {'R','M','C','C','P','S','e','a','r','c','h',0};
155 static const WCHAR szScheduleReboot[] =
156 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
157 static const WCHAR szSelfUnregModules[] =
158 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
159 static const WCHAR szSetODBCFolders[] =
160 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
161 static const WCHAR szStartServices[] =
162 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
163 static const WCHAR szStopServices[] =
164 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
165 static const WCHAR szUnpublishComponents[] =
166 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
167 static const WCHAR szUnpublishFeatures[] =
168 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
169 static const WCHAR szUnregisterClassInfo[] =
170 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
171 static const WCHAR szUnregisterComPlus[] =
172 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
173 static const WCHAR szUnregisterExtensionInfo[] =
174 {'U','n','r','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n','I','n','f','o',0};
175 static const WCHAR szUnregisterFonts[] =
176 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
177 static const WCHAR szUnregisterMIMEInfo[] =
178 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
179 static const WCHAR szUnregisterProgIdInfo[] =
180 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
181 static const WCHAR szUnregisterTypeLibraries[] =
182 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
183 static const WCHAR szValidateProductID[] =
184 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
185 static const WCHAR szWriteEnvironmentStrings[] =
186 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
188 /********************************************************
190 ********************************************************/
192 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
194 static const WCHAR Query_t[] =
195 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
196 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
197 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
198 ' ','\'','%','s','\'',0};
201 row = MSI_QueryGetRecord( package->db, Query_t, action );
204 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
205 msiobj_release(&row->hdr);
208 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
212 static const WCHAR template_s[]=
213 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
215 static const WCHAR template_e[]=
216 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
217 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
219 static const WCHAR format[] =
220 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
224 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
226 sprintfW(message,template_s,timet,action);
228 sprintfW(message,template_e,timet,action,rc);
230 row = MSI_CreateRecord(1);
231 MSI_RecordSetStringW(row,1,message);
233 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
234 msiobj_release(&row->hdr);
237 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
243 LPWSTR prop = NULL, val = NULL;
246 return ERROR_SUCCESS;
258 TRACE("Looking at %s\n",debugstr_w(ptr));
260 ptr2 = strchrW(ptr,'=');
263 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
270 prop = msi_alloc((len+1)*sizeof(WCHAR));
271 memcpy(prop,ptr,len*sizeof(WCHAR));
281 while (*ptr && (quote || (!quote && *ptr!=' ')))
294 val = msi_alloc((len+1)*sizeof(WCHAR));
295 memcpy(val,ptr2,len*sizeof(WCHAR));
298 if (lstrlenW(prop) > 0)
300 TRACE("Found commandline property (%s) = (%s)\n",
301 debugstr_w(prop), debugstr_w(val));
302 MSI_SetPropertyW(package,prop,val);
308 return ERROR_SUCCESS;
312 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
315 LPWSTR p, *ret = NULL;
321 /* count the number of substrings */
322 for ( pc = str, count = 0; pc; count++ )
324 pc = strchrW( pc, sep );
329 /* allocate space for an array of substring pointers and the substrings */
330 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
331 (lstrlenW(str)+1) * sizeof(WCHAR) );
335 /* copy the string and set the pointers */
336 p = (LPWSTR) &ret[count+1];
338 for( count = 0; (ret[count] = p); count++ )
340 p = strchrW( p, sep );
348 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
350 static const WCHAR szSystemLanguageID[] =
351 { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
353 LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
354 UINT ret = ERROR_FUNCTION_FAILED;
356 prod_code = msi_dup_property( package, szProductCode );
357 patch_product = msi_get_suminfo_product( patch );
359 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
361 if ( strstrW( patch_product, prod_code ) )
366 si = MSI_GetSummaryInformationW( patch, 0 );
369 ERR("no summary information!\n");
373 template = msi_suminfo_dup_string( si, PID_TEMPLATE );
376 ERR("no template property!\n");
377 msiobj_release( &si->hdr );
384 msiobj_release( &si->hdr );
388 langid = msi_dup_property( package, szSystemLanguageID );
391 msiobj_release( &si->hdr );
395 p = strchrW( template, ';' );
396 if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
398 TRACE("applicable transform\n");
402 /* FIXME: check platform */
404 msiobj_release( &si->hdr );
408 msi_free( patch_product );
409 msi_free( prod_code );
410 msi_free( template );
416 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
417 MSIDATABASE *patch_db, LPCWSTR name )
419 UINT ret = ERROR_FUNCTION_FAILED;
420 IStorage *stg = NULL;
423 TRACE("%p %s\n", package, debugstr_w(name) );
427 ERR("expected a colon in %s\n", debugstr_w(name));
428 return ERROR_FUNCTION_FAILED;
431 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
434 ret = msi_check_transform_applicable( package, stg );
435 if (ret == ERROR_SUCCESS)
436 msi_table_apply_transform( package->db, stg );
438 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
439 IStorage_Release( stg );
442 ERR("failed to open substorage %s\n", debugstr_w(name));
444 return ERROR_SUCCESS;
447 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
449 LPWSTR guid_list, *guids, product_code;
450 UINT i, ret = ERROR_FUNCTION_FAILED;
452 product_code = msi_dup_property( package, szProductCode );
455 /* FIXME: the property ProductCode should be written into the DB somewhere */
456 ERR("no product code to check\n");
457 return ERROR_SUCCESS;
460 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
461 guids = msi_split_string( guid_list, ';' );
462 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
464 if (!lstrcmpW( guids[i], product_code ))
468 msi_free( guid_list );
469 msi_free( product_code );
474 static UINT msi_set_media_source_prop(MSIPACKAGE *package)
477 MSIRECORD *rec = NULL;
482 static const WCHAR query[] = {'S','E','L','E','C','T',' ',
483 '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
484 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
485 '`','S','o','u','r','c','e','`',' ','I','S',' ',
486 'N','O','T',' ','N','U','L','L',0};
488 r = MSI_DatabaseOpenViewW(package->db, query, &view);
489 if (r != ERROR_SUCCESS)
492 r = MSI_ViewExecute(view, 0);
493 if (r != ERROR_SUCCESS)
496 if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
498 prop = MSI_RecordGetString(rec, 1);
499 patch = msi_dup_property(package, szPatch);
500 MSI_SetPropertyW(package, prop, patch);
505 if (rec) msiobj_release(&rec->hdr);
506 msiobj_release(&view->hdr);
511 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
514 LPWSTR str, *substorage;
515 UINT i, r = ERROR_SUCCESS;
517 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
519 return ERROR_FUNCTION_FAILED;
521 if (msi_check_patch_applicable( package, si ) != ERROR_SUCCESS)
523 TRACE("Patch not applicable\n");
524 return ERROR_SUCCESS;
527 package->patch = msi_alloc(sizeof(MSIPATCHINFO));
529 return ERROR_OUTOFMEMORY;
531 package->patch->patchcode = msi_suminfo_dup_string(si, PID_REVNUMBER);
532 if (!package->patch->patchcode)
533 return ERROR_OUTOFMEMORY;
535 /* enumerate the substorage */
536 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
537 package->patch->transforms = str;
539 substorage = msi_split_string( str, ';' );
540 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
541 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
543 msi_free( substorage );
544 msiobj_release( &si->hdr );
546 msi_set_media_source_prop(package);
551 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
553 MSIDATABASE *patch_db = NULL;
556 TRACE("%p %s\n", package, debugstr_w( file ) );
559 * We probably want to make sure we only open a patch collection here.
560 * Patch collections (.msp) and databases (.msi) have different GUIDs
561 * but currently MSI_OpenDatabaseW will accept both.
563 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
564 if ( r != ERROR_SUCCESS )
566 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
570 msi_parse_patch_summary( package, patch_db );
573 * There might be a CAB file in the patch package,
574 * so append it to the list of storage to search for streams.
576 append_storage_to_db( package->db, patch_db->storage );
578 msiobj_release( &patch_db->hdr );
580 return ERROR_SUCCESS;
583 /* get the PATCH property, and apply all the patches it specifies */
584 static UINT msi_apply_patches( MSIPACKAGE *package )
586 LPWSTR patch_list, *patches;
587 UINT i, r = ERROR_SUCCESS;
589 patch_list = msi_dup_property( package, szPatch );
591 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
593 patches = msi_split_string( patch_list, ';' );
594 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
595 r = msi_apply_patch_package( package, patches[i] );
598 msi_free( patch_list );
603 static UINT msi_apply_transforms( MSIPACKAGE *package )
605 static const WCHAR szTransforms[] = {
606 'T','R','A','N','S','F','O','R','M','S',0 };
607 LPWSTR xform_list, *xforms;
608 UINT i, r = ERROR_SUCCESS;
610 xform_list = msi_dup_property( package, szTransforms );
611 xforms = msi_split_string( xform_list, ';' );
613 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
615 if (xforms[i][0] == ':')
616 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
618 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
622 msi_free( xform_list );
627 static BOOL ui_sequence_exists( MSIPACKAGE *package )
632 static const WCHAR ExecSeqQuery [] =
633 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
634 '`','I','n','s','t','a','l','l',
635 'U','I','S','e','q','u','e','n','c','e','`',
636 ' ','W','H','E','R','E',' ',
637 '`','S','e','q','u','e','n','c','e','`',' ',
638 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
639 '`','S','e','q','u','e','n','c','e','`',0};
641 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
642 if (rc == ERROR_SUCCESS)
644 msiobj_release(&view->hdr);
651 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
654 LPWSTR source, check;
657 static const WCHAR szOriginalDatabase[] =
658 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
660 db = msi_dup_property( package, szOriginalDatabase );
662 return ERROR_OUTOFMEMORY;
664 p = strrchrW( db, '\\' );
667 p = strrchrW( db, '/' );
671 return ERROR_SUCCESS;
676 source = msi_alloc( len * sizeof(WCHAR) );
677 lstrcpynW( source, db, len );
679 check = msi_dup_property( package, cszSourceDir );
680 if (!check || replace)
681 MSI_SetPropertyW( package, cszSourceDir, source );
685 check = msi_dup_property( package, cszSOURCEDIR );
686 if (!check || replace)
687 MSI_SetPropertyW( package, cszSOURCEDIR, source );
693 return ERROR_SUCCESS;
696 static BOOL needs_ui_sequence(MSIPACKAGE *package)
698 INT level = msi_get_property_int(package, szUILevel, 0);
699 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
702 static UINT msi_set_context(MSIPACKAGE *package)
709 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
711 r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
712 if (r == ERROR_SUCCESS)
715 if (num == 1 || num == 2)
716 package->Context = MSIINSTALLCONTEXT_MACHINE;
719 return ERROR_SUCCESS;
722 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
725 LPCWSTR cond, action;
726 MSIPACKAGE *package = param;
728 action = MSI_RecordGetString(row,1);
731 ERR("Error is retrieving action name\n");
732 return ERROR_FUNCTION_FAILED;
735 /* check conditions */
736 cond = MSI_RecordGetString(row,2);
738 /* this is a hack to skip errors in the condition code */
739 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
741 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
742 return ERROR_SUCCESS;
745 if (needs_ui_sequence(package))
746 rc = ACTION_PerformUIAction(package, action, -1);
748 rc = ACTION_PerformAction(package, action, -1, FALSE);
750 msi_dialog_check_messages( NULL );
752 if (package->CurrentInstallState != ERROR_SUCCESS)
753 rc = package->CurrentInstallState;
755 if (rc == ERROR_FUNCTION_NOT_CALLED)
758 if (rc != ERROR_SUCCESS)
759 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
764 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
768 static const WCHAR query[] =
769 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
771 ' ','W','H','E','R','E',' ',
772 '`','S','e','q','u','e','n','c','e','`',' ',
773 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
774 '`','S','e','q','u','e','n','c','e','`',0};
776 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
778 r = MSI_OpenQuery( package->db, &view, query, szTable );
779 if (r == ERROR_SUCCESS)
781 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
782 msiobj_release(&view->hdr);
788 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
792 static const WCHAR ExecSeqQuery[] =
793 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
794 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
795 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
796 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
797 'O','R','D','E','R',' ', 'B','Y',' ',
798 '`','S','e','q','u','e','n','c','e','`',0 };
799 static const WCHAR IVQuery[] =
800 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
801 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
802 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
803 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
804 ' ','\'', 'I','n','s','t','a','l','l',
805 'V','a','l','i','d','a','t','e','\'', 0};
808 if (package->script->ExecuteSequenceRun)
810 TRACE("Execute Sequence already Run\n");
811 return ERROR_SUCCESS;
814 package->script->ExecuteSequenceRun = TRUE;
816 /* get the sequence number */
819 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
821 return ERROR_FUNCTION_FAILED;
822 seq = MSI_RecordGetInteger(row,1);
823 msiobj_release(&row->hdr);
826 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
827 if (rc == ERROR_SUCCESS)
829 TRACE("Running the actions\n");
831 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
832 msiobj_release(&view->hdr);
838 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
842 static const WCHAR ExecSeqQuery [] =
843 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
844 '`','I','n','s','t','a','l','l',
845 'U','I','S','e','q','u','e','n','c','e','`',
846 ' ','W','H','E','R','E',' ',
847 '`','S','e','q','u','e','n','c','e','`',' ',
848 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
849 '`','S','e','q','u','e','n','c','e','`',0};
851 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
852 if (rc == ERROR_SUCCESS)
854 TRACE("Running the actions\n");
856 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
857 msiobj_release(&view->hdr);
863 /********************************************************
864 * ACTION helper functions and functions that perform the actions
865 *******************************************************/
866 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
867 UINT* rc, UINT script, BOOL force )
872 arc = ACTION_CustomAction(package, action, script, force);
874 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
883 * Actual Action Handlers
886 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
888 MSIPACKAGE *package = param;
894 dir = MSI_RecordGetString(row,1);
897 ERR("Unable to get folder id\n");
898 return ERROR_SUCCESS;
901 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
904 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
905 return ERROR_SUCCESS;
908 TRACE("Folder is %s\n",debugstr_w(full_path));
911 uirow = MSI_CreateRecord(1);
912 MSI_RecordSetStringW(uirow,1,full_path);
913 ui_actiondata(package,szCreateFolders,uirow);
914 msiobj_release( &uirow->hdr );
916 if (folder->State == 0)
917 create_full_pathW(full_path);
922 return ERROR_SUCCESS;
925 /* FIXME: probably should merge this with the above function */
926 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
928 UINT rc = ERROR_SUCCESS;
932 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
934 return ERROR_FUNCTION_FAILED;
936 /* create the path */
937 if (folder->State == 0)
939 create_full_pathW(install_path);
942 msi_free(install_path);
947 UINT msi_create_component_directories( MSIPACKAGE *package )
951 /* create all the folders required by the components are going to install */
952 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
954 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
956 msi_create_directory( package, comp->Directory );
959 return ERROR_SUCCESS;
963 * Also we cannot enable/disable components either, so for now I am just going
964 * to do all the directories for all the components.
966 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
968 static const WCHAR ExecSeqQuery[] =
969 {'S','E','L','E','C','T',' ',
970 '`','D','i','r','e','c','t','o','r','y','_','`',
971 ' ','F','R','O','M',' ',
972 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
976 /* create all the empty folders specified in the CreateFolder table */
977 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
978 if (rc != ERROR_SUCCESS)
979 return ERROR_SUCCESS;
981 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
982 msiobj_release(&view->hdr);
984 msi_create_component_directories( package );
989 static UINT load_component( MSIRECORD *row, LPVOID param )
991 MSIPACKAGE *package = param;
994 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
996 return ERROR_FUNCTION_FAILED;
998 list_add_tail( &package->components, &comp->entry );
1000 /* fill in the data */
1001 comp->Component = msi_dup_record_field( row, 1 );
1003 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1005 comp->ComponentId = msi_dup_record_field( row, 2 );
1006 comp->Directory = msi_dup_record_field( row, 3 );
1007 comp->Attributes = MSI_RecordGetInteger(row,4);
1008 comp->Condition = msi_dup_record_field( row, 5 );
1009 comp->KeyPath = msi_dup_record_field( row, 6 );
1011 comp->Installed = INSTALLSTATE_UNKNOWN;
1012 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1014 return ERROR_SUCCESS;
1017 static UINT load_all_components( MSIPACKAGE *package )
1019 static const WCHAR query[] = {
1020 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1021 '`','C','o','m','p','o','n','e','n','t','`',0 };
1025 if (!list_empty(&package->components))
1026 return ERROR_SUCCESS;
1028 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1029 if (r != ERROR_SUCCESS)
1032 r = MSI_IterateRecords(view, NULL, load_component, package);
1033 msiobj_release(&view->hdr);
1038 MSIPACKAGE *package;
1039 MSIFEATURE *feature;
1042 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1046 cl = msi_alloc( sizeof (*cl) );
1048 return ERROR_NOT_ENOUGH_MEMORY;
1049 cl->component = comp;
1050 list_add_tail( &feature->Components, &cl->entry );
1052 return ERROR_SUCCESS;
1055 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1059 fl = msi_alloc( sizeof(*fl) );
1061 return ERROR_NOT_ENOUGH_MEMORY;
1062 fl->feature = child;
1063 list_add_tail( &parent->Children, &fl->entry );
1065 return ERROR_SUCCESS;
1068 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1070 _ilfs* ilfs = param;
1074 component = MSI_RecordGetString(row,1);
1076 /* check to see if the component is already loaded */
1077 comp = get_loaded_component( ilfs->package, component );
1080 ERR("unknown component %s\n", debugstr_w(component));
1081 return ERROR_FUNCTION_FAILED;
1084 add_feature_component( ilfs->feature, comp );
1085 comp->Enabled = TRUE;
1087 return ERROR_SUCCESS;
1090 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1092 MSIFEATURE *feature;
1097 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1099 if ( !lstrcmpW( feature->Feature, name ) )
1106 static UINT load_feature(MSIRECORD * row, LPVOID param)
1108 MSIPACKAGE* package = param;
1109 MSIFEATURE* feature;
1110 static const WCHAR Query1[] =
1111 {'S','E','L','E','C','T',' ',
1112 '`','C','o','m','p','o','n','e','n','t','_','`',
1113 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1114 'C','o','m','p','o','n','e','n','t','s','`',' ',
1115 'W','H','E','R','E',' ',
1116 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1121 /* fill in the data */
1123 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1125 return ERROR_NOT_ENOUGH_MEMORY;
1127 list_init( &feature->Children );
1128 list_init( &feature->Components );
1130 feature->Feature = msi_dup_record_field( row, 1 );
1132 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1134 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1135 feature->Title = msi_dup_record_field( row, 3 );
1136 feature->Description = msi_dup_record_field( row, 4 );
1138 if (!MSI_RecordIsNull(row,5))
1139 feature->Display = MSI_RecordGetInteger(row,5);
1141 feature->Level= MSI_RecordGetInteger(row,6);
1142 feature->Directory = msi_dup_record_field( row, 7 );
1143 feature->Attributes = MSI_RecordGetInteger(row,8);
1145 feature->Installed = INSTALLSTATE_UNKNOWN;
1146 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1148 list_add_tail( &package->features, &feature->entry );
1150 /* load feature components */
1152 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1153 if (rc != ERROR_SUCCESS)
1154 return ERROR_SUCCESS;
1156 ilfs.package = package;
1157 ilfs.feature = feature;
1159 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1160 msiobj_release(&view->hdr);
1162 return ERROR_SUCCESS;
1165 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1167 MSIPACKAGE* package = param;
1168 MSIFEATURE *parent, *child;
1170 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1172 return ERROR_FUNCTION_FAILED;
1174 if (!child->Feature_Parent)
1175 return ERROR_SUCCESS;
1177 parent = find_feature_by_name( package, child->Feature_Parent );
1179 return ERROR_FUNCTION_FAILED;
1181 add_feature_child( parent, child );
1182 return ERROR_SUCCESS;
1185 static UINT load_all_features( MSIPACKAGE *package )
1187 static const WCHAR query[] = {
1188 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1189 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1190 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1194 if (!list_empty(&package->features))
1195 return ERROR_SUCCESS;
1197 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1198 if (r != ERROR_SUCCESS)
1201 r = MSI_IterateRecords( view, NULL, load_feature, package );
1202 if (r != ERROR_SUCCESS)
1205 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1206 msiobj_release( &view->hdr );
1211 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1222 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1224 static const WCHAR query[] = {
1225 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1226 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1227 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1228 MSIQUERY *view = NULL;
1229 MSIRECORD *row = NULL;
1232 TRACE("%s\n", debugstr_w(file->File));
1234 r = MSI_OpenQuery(package->db, &view, query, file->File);
1235 if (r != ERROR_SUCCESS)
1238 r = MSI_ViewExecute(view, NULL);
1239 if (r != ERROR_SUCCESS)
1242 r = MSI_ViewFetch(view, &row);
1243 if (r != ERROR_SUCCESS)
1246 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1247 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1248 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1249 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1250 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1253 if (view) msiobj_release(&view->hdr);
1254 if (row) msiobj_release(&row->hdr);
1258 static UINT load_file(MSIRECORD *row, LPVOID param)
1260 MSIPACKAGE* package = param;
1264 /* fill in the data */
1266 file = msi_alloc_zero( sizeof (MSIFILE) );
1268 return ERROR_NOT_ENOUGH_MEMORY;
1270 file->File = msi_dup_record_field( row, 1 );
1272 component = MSI_RecordGetString( row, 2 );
1273 file->Component = get_loaded_component( package, component );
1275 if (!file->Component)
1277 WARN("Component not found: %s\n", debugstr_w(component));
1278 msi_free(file->File);
1280 return ERROR_SUCCESS;
1283 file->FileName = msi_dup_record_field( row, 3 );
1284 reduce_to_longfilename( file->FileName );
1286 file->ShortName = msi_dup_record_field( row, 3 );
1287 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1289 file->FileSize = MSI_RecordGetInteger( row, 4 );
1290 file->Version = msi_dup_record_field( row, 5 );
1291 file->Language = msi_dup_record_field( row, 6 );
1292 file->Attributes = MSI_RecordGetInteger( row, 7 );
1293 file->Sequence = MSI_RecordGetInteger( row, 8 );
1295 file->state = msifs_invalid;
1297 /* if the compressed bits are not set in the file attributes,
1298 * then read the information from the package word count property
1300 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1302 file->IsCompressed = FALSE;
1304 else if (file->Attributes &
1305 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1307 file->IsCompressed = TRUE;
1309 else if (file->Attributes & msidbFileAttributesNoncompressed)
1311 file->IsCompressed = FALSE;
1315 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1318 load_file_hash(package, file);
1320 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1322 list_add_tail( &package->files, &file->entry );
1324 return ERROR_SUCCESS;
1327 static UINT load_all_files(MSIPACKAGE *package)
1331 static const WCHAR Query[] =
1332 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1333 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1334 '`','S','e','q','u','e','n','c','e','`', 0};
1336 if (!list_empty(&package->files))
1337 return ERROR_SUCCESS;
1339 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1340 if (rc != ERROR_SUCCESS)
1341 return ERROR_SUCCESS;
1343 rc = MSI_IterateRecords(view, NULL, load_file, package);
1344 msiobj_release(&view->hdr);
1346 return ERROR_SUCCESS;
1349 static UINT load_folder( MSIRECORD *row, LPVOID param )
1351 MSIPACKAGE *package = param;
1352 static WCHAR szEmpty[] = { 0 };
1353 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1356 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1358 return ERROR_NOT_ENOUGH_MEMORY;
1360 folder->Directory = msi_dup_record_field( row, 1 );
1362 TRACE("%s\n", debugstr_w(folder->Directory));
1364 p = msi_dup_record_field(row, 3);
1366 /* split src and target dir */
1368 src_short = folder_split_path( p, ':' );
1370 /* split the long and short paths */
1371 tgt_long = folder_split_path( tgt_short, '|' );
1372 src_long = folder_split_path( src_short, '|' );
1374 /* check for no-op dirs */
1375 if (!lstrcmpW(szDot, tgt_short))
1376 tgt_short = szEmpty;
1377 if (!lstrcmpW(szDot, src_short))
1378 src_short = szEmpty;
1381 tgt_long = tgt_short;
1384 src_short = tgt_short;
1385 src_long = tgt_long;
1389 src_long = src_short;
1391 /* FIXME: use the target short path too */
1392 folder->TargetDefault = strdupW(tgt_long);
1393 folder->SourceShortPath = strdupW(src_short);
1394 folder->SourceLongPath = strdupW(src_long);
1397 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1398 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1399 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1401 folder->Parent = msi_dup_record_field( row, 2 );
1403 folder->Property = msi_dup_property( package, folder->Directory );
1405 list_add_tail( &package->folders, &folder->entry );
1407 TRACE("returning %p\n", folder);
1409 return ERROR_SUCCESS;
1412 static UINT load_all_folders( MSIPACKAGE *package )
1414 static const WCHAR query[] = {
1415 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1416 '`','D','i','r','e','c','t','o','r','y','`',0 };
1420 if (!list_empty(&package->folders))
1421 return ERROR_SUCCESS;
1423 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1424 if (r != ERROR_SUCCESS)
1427 r = MSI_IterateRecords(view, NULL, load_folder, package);
1428 msiobj_release(&view->hdr);
1433 * I am not doing any of the costing functionality yet.
1434 * Mostly looking at doing the Component and Feature loading
1436 * The native MSI does A LOT of modification to tables here. Mostly adding
1437 * a lot of temporary columns to the Feature and Component tables.
1439 * note: Native msi also tracks the short filename. But I am only going to
1440 * track the long ones. Also looking at this directory table
1441 * it appears that the directory table does not get the parents
1442 * resolved base on property only based on their entries in the
1445 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1447 static const WCHAR szCosting[] =
1448 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1450 MSI_SetPropertyW(package, szCosting, szZero);
1451 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1453 load_all_folders( package );
1454 load_all_components( package );
1455 load_all_features( package );
1456 load_all_files( package );
1458 return ERROR_SUCCESS;
1461 static UINT execute_script(MSIPACKAGE *package, UINT script )
1464 UINT rc = ERROR_SUCCESS;
1466 TRACE("Executing Script %i\n",script);
1468 if (!package->script)
1470 ERR("no script!\n");
1471 return ERROR_FUNCTION_FAILED;
1474 for (i = 0; i < package->script->ActionCount[script]; i++)
1477 action = package->script->Actions[script][i];
1478 ui_actionstart(package, action);
1479 TRACE("Executing Action (%s)\n",debugstr_w(action));
1480 rc = ACTION_PerformAction(package, action, script, TRUE);
1481 if (rc != ERROR_SUCCESS)
1484 msi_free_action_script(package, script);
1488 static UINT ACTION_FileCost(MSIPACKAGE *package)
1490 return ERROR_SUCCESS;
1493 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1499 state = MsiQueryProductStateW(package->ProductCode);
1501 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1503 if (!comp->ComponentId)
1506 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1507 comp->Installed = INSTALLSTATE_ABSENT;
1510 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1511 package->Context, comp->ComponentId,
1513 if (r != ERROR_SUCCESS)
1514 comp->Installed = INSTALLSTATE_ABSENT;
1519 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1521 MSIFEATURE *feature;
1524 state = MsiQueryProductStateW(package->ProductCode);
1526 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1528 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1529 feature->Installed = INSTALLSTATE_ABSENT;
1532 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1538 static BOOL process_state_property(MSIPACKAGE* package, int level,
1539 LPCWSTR property, INSTALLSTATE state)
1542 MSIFEATURE *feature;
1544 override = msi_dup_property( package, property );
1548 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1550 if (lstrcmpW(property, szRemove) &&
1551 (feature->Level <= 0 || feature->Level > level))
1554 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1556 if (strcmpiW(override, szAll)==0)
1557 msi_feature_set_state(package, feature, state);
1560 LPWSTR ptr = override;
1561 LPWSTR ptr2 = strchrW(override,',');
1565 int len = ptr2 - ptr;
1567 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1568 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1570 msi_feature_set_state(package, feature, state);
1576 ptr2 = strchrW(ptr,',');
1588 static BOOL process_overrides( MSIPACKAGE *package, int level )
1590 static const WCHAR szAddLocal[] =
1591 {'A','D','D','L','O','C','A','L',0};
1592 static const WCHAR szAddSource[] =
1593 {'A','D','D','S','O','U','R','C','E',0};
1594 static const WCHAR szAdvertise[] =
1595 {'A','D','V','E','R','T','I','S','E',0};
1598 /* all these activation/deactivation things happen in order and things
1599 * later on the list override things earlier on the list.
1601 * 0 INSTALLLEVEL processing
1614 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1615 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1616 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1617 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1618 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1621 MSI_SetPropertyW( package, szPreselected, szOne );
1626 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1629 static const WCHAR szlevel[] =
1630 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1631 MSICOMPONENT* component;
1632 MSIFEATURE *feature;
1634 TRACE("Checking Install Level\n");
1636 level = msi_get_property_int(package, szlevel, 1);
1638 if (!msi_get_property_int( package, szPreselected, 0 ))
1640 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1642 BOOL feature_state = ((feature->Level > 0) &&
1643 (feature->Level <= level));
1645 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1647 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1648 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1649 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1650 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1652 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1656 /* disable child features of unselected parent features */
1657 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1661 if (feature->Level > 0 && feature->Level <= level)
1664 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1665 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1670 * now we want to enable or disable components base on feature
1673 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1677 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1678 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1680 if (!feature->Level)
1683 /* features with components that have compressed files are made local */
1684 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1686 if (cl->component->Enabled &&
1687 cl->component->ForceLocalState &&
1688 feature->Action == INSTALLSTATE_SOURCE)
1690 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1695 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1697 component = cl->component;
1699 if (!component->Enabled)
1702 switch (feature->Action)
1704 case INSTALLSTATE_ABSENT:
1705 component->anyAbsent = 1;
1707 case INSTALLSTATE_ADVERTISED:
1708 component->hasAdvertiseFeature = 1;
1710 case INSTALLSTATE_SOURCE:
1711 component->hasSourceFeature = 1;
1713 case INSTALLSTATE_LOCAL:
1714 component->hasLocalFeature = 1;
1716 case INSTALLSTATE_DEFAULT:
1717 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1718 component->hasAdvertiseFeature = 1;
1719 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1720 component->hasSourceFeature = 1;
1722 component->hasLocalFeature = 1;
1730 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1732 /* if the component isn't enabled, leave it alone */
1733 if (!component->Enabled)
1736 /* check if it's local or source */
1737 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1738 (component->hasLocalFeature || component->hasSourceFeature))
1740 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1741 !component->ForceLocalState)
1742 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1744 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1748 /* if any feature is local, the component must be local too */
1749 if (component->hasLocalFeature)
1751 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1755 if (component->hasSourceFeature)
1757 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1761 if (component->hasAdvertiseFeature)
1763 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1767 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1768 if (component->anyAbsent)
1769 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1772 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1774 if (component->Action == INSTALLSTATE_DEFAULT)
1776 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1777 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1780 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1781 debugstr_w(component->Component), component->Installed, component->Action);
1785 return ERROR_SUCCESS;
1788 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1790 MSIPACKAGE *package = param;
1795 name = MSI_RecordGetString(row,1);
1797 f = get_loaded_folder(package, name);
1798 if (!f) return ERROR_SUCCESS;
1800 /* reset the ResolvedTarget */
1801 msi_free(f->ResolvedTarget);
1802 f->ResolvedTarget = NULL;
1804 /* This helper function now does ALL the work */
1805 TRACE("Dir %s ...\n",debugstr_w(name));
1806 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1807 TRACE("resolves to %s\n",debugstr_w(path));
1810 return ERROR_SUCCESS;
1813 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1815 MSIPACKAGE *package = param;
1817 MSIFEATURE *feature;
1819 name = MSI_RecordGetString( row, 1 );
1821 feature = get_loaded_feature( package, name );
1823 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1827 Condition = MSI_RecordGetString(row,3);
1829 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1831 int level = MSI_RecordGetInteger(row,2);
1832 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1833 feature->Level = level;
1836 return ERROR_SUCCESS;
1839 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1841 static const WCHAR name_fmt[] =
1842 {'%','u','.','%','u','.','%','u','.','%','u',0};
1843 static const WCHAR name[] = {'\\',0};
1844 VS_FIXEDFILEINFO *lpVer;
1845 WCHAR filever[0x100];
1851 TRACE("%s\n", debugstr_w(filename));
1853 versize = GetFileVersionInfoSizeW( filename, &handle );
1857 version = msi_alloc( versize );
1858 GetFileVersionInfoW( filename, 0, versize, version );
1860 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
1862 msi_free( version );
1866 sprintfW( filever, name_fmt,
1867 HIWORD(lpVer->dwFileVersionMS),
1868 LOWORD(lpVer->dwFileVersionMS),
1869 HIWORD(lpVer->dwFileVersionLS),
1870 LOWORD(lpVer->dwFileVersionLS));
1872 msi_free( version );
1874 return strdupW( filever );
1877 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1879 LPWSTR file_version;
1882 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1884 MSICOMPONENT* comp = file->Component;
1890 if (file->IsCompressed)
1891 comp->ForceLocalState = TRUE;
1893 /* calculate target */
1894 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
1896 msi_free(file->TargetPath);
1898 TRACE("file %s is named %s\n",
1899 debugstr_w(file->File), debugstr_w(file->FileName));
1901 file->TargetPath = build_directory_name(2, p, file->FileName);
1905 TRACE("file %s resolves to %s\n",
1906 debugstr_w(file->File), debugstr_w(file->TargetPath));
1908 /* don't check files of components that aren't installed */
1909 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
1910 comp->Installed == INSTALLSTATE_ABSENT)
1912 file->state = msifs_missing; /* assume files are missing */
1916 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1918 file->state = msifs_missing;
1919 comp->Cost += file->FileSize;
1923 if (file->Version &&
1924 (file_version = msi_get_disk_file_version( file->TargetPath )))
1926 TRACE("new %s old %s\n", debugstr_w(file->Version),
1927 debugstr_w(file_version));
1928 /* FIXME: seems like a bad way to compare version numbers */
1929 if (lstrcmpiW(file_version, file->Version)<0)
1931 file->state = msifs_overwrite;
1932 comp->Cost += file->FileSize;
1935 file->state = msifs_present;
1936 msi_free( file_version );
1939 file->state = msifs_present;
1942 return ERROR_SUCCESS;
1946 * A lot is done in this function aside from just the costing.
1947 * The costing needs to be implemented at some point but for now I am going
1948 * to focus on the directory building
1951 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
1953 static const WCHAR ExecSeqQuery[] =
1954 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1955 '`','D','i','r','e','c','t','o','r','y','`',0};
1956 static const WCHAR ConditionQuery[] =
1957 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1958 '`','C','o','n','d','i','t','i','o','n','`',0};
1959 static const WCHAR szCosting[] =
1960 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1961 static const WCHAR szlevel[] =
1962 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1963 static const WCHAR szOutOfDiskSpace[] =
1964 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
1966 UINT rc = ERROR_SUCCESS;
1970 TRACE("Building Directory properties\n");
1972 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1973 if (rc == ERROR_SUCCESS)
1975 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
1977 msiobj_release(&view->hdr);
1980 /* read components states from the registry */
1981 ACTION_GetComponentInstallStates(package);
1982 ACTION_GetFeatureInstallStates(package);
1984 TRACE("File calculations\n");
1985 msi_check_file_install_states( package );
1987 if (!process_overrides( package, msi_get_property_int( package, szlevel, 1 ) ))
1989 TRACE("Evaluating Condition Table\n");
1991 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
1992 if (rc == ERROR_SUCCESS)
1994 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
1995 msiobj_release( &view->hdr );
1998 TRACE("Enabling or Disabling Components\n");
1999 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2001 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2003 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2004 comp->Enabled = FALSE;
2007 comp->Enabled = TRUE;
2011 MSI_SetPropertyW(package,szCosting,szOne);
2012 /* set default run level if not set */
2013 level = msi_dup_property( package, szlevel );
2015 MSI_SetPropertyW(package,szlevel, szOne);
2018 /* FIXME: check volume disk space */
2019 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2021 return MSI_SetFeatureStates(package);
2024 /* OK this value is "interpreted" and then formatted based on the
2025 first few characters */
2026 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2031 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2037 LPWSTR deformated = NULL;
2040 deformat_string(package, &value[2], &deformated);
2042 /* binary value type */
2046 *size = (strlenW(ptr)/2)+1;
2048 *size = strlenW(ptr)/2;
2050 data = msi_alloc(*size);
2056 /* if uneven pad with a zero in front */
2062 data[count] = (BYTE)strtol(byte,NULL,0);
2064 TRACE("Uneven byte count\n");
2072 data[count] = (BYTE)strtol(byte,NULL,0);
2075 msi_free(deformated);
2077 TRACE("Data %i bytes(%i)\n",*size,count);
2084 deformat_string(package, &value[1], &deformated);
2087 *size = sizeof(DWORD);
2088 data = msi_alloc(*size);
2094 if ( (*p < '0') || (*p > '9') )
2100 if (deformated[0] == '-')
2103 TRACE("DWORD %i\n",*(LPDWORD)data);
2105 msi_free(deformated);
2110 static const WCHAR szMulti[] = {'[','~',']',0};
2119 *type=REG_EXPAND_SZ;
2127 if (strstrW(value,szMulti))
2128 *type = REG_MULTI_SZ;
2130 /* remove initial delimiter */
2131 if (!strncmpW(value, szMulti, 3))
2134 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2136 /* add double NULL terminator */
2137 if (*type == REG_MULTI_SZ)
2139 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2140 data = msi_realloc_zero(data, *size);
2146 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2148 MSIPACKAGE *package = param;
2149 static const WCHAR szHCR[] =
2150 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2151 'R','O','O','T','\\',0};
2152 static const WCHAR szHCU[] =
2153 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2154 'U','S','E','R','\\',0};
2155 static const WCHAR szHLM[] =
2156 {'H','K','E','Y','_','L','O','C','A','L','_',
2157 'M','A','C','H','I','N','E','\\',0};
2158 static const WCHAR szHU[] =
2159 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2161 LPSTR value_data = NULL;
2162 HKEY root_key, hkey;
2165 LPCWSTR szRoot, component, name, key, value;
2170 BOOL check_first = FALSE;
2173 ui_progress(package,2,0,0,0);
2180 component = MSI_RecordGetString(row, 6);
2181 comp = get_loaded_component(package,component);
2183 return ERROR_SUCCESS;
2185 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2187 TRACE("Skipping write due to disabled component %s\n",
2188 debugstr_w(component));
2190 comp->Action = comp->Installed;
2192 return ERROR_SUCCESS;
2195 comp->Action = INSTALLSTATE_LOCAL;
2197 name = MSI_RecordGetString(row, 4);
2198 if( MSI_RecordIsNull(row,5) && name )
2200 /* null values can have special meanings */
2201 if (name[0]=='-' && name[1] == 0)
2202 return ERROR_SUCCESS;
2203 else if ((name[0]=='+' && name[1] == 0) ||
2204 (name[0] == '*' && name[1] == 0))
2209 root = MSI_RecordGetInteger(row,2);
2210 key = MSI_RecordGetString(row, 3);
2212 /* get the root key */
2217 LPWSTR all_users = msi_dup_property( package, szAllUsers );
2218 if (all_users && all_users[0] == '1')
2220 root_key = HKEY_LOCAL_MACHINE;
2225 root_key = HKEY_CURRENT_USER;
2228 msi_free(all_users);
2231 case 0: root_key = HKEY_CLASSES_ROOT;
2234 case 1: root_key = HKEY_CURRENT_USER;
2237 case 2: root_key = HKEY_LOCAL_MACHINE;
2240 case 3: root_key = HKEY_USERS;
2244 ERR("Unknown root %i\n",root);
2250 return ERROR_SUCCESS;
2252 deformat_string(package, key , &deformated);
2253 size = strlenW(deformated) + strlenW(szRoot) + 1;
2254 uikey = msi_alloc(size*sizeof(WCHAR));
2255 strcpyW(uikey,szRoot);
2256 strcatW(uikey,deformated);
2258 if (RegCreateKeyW( root_key, deformated, &hkey))
2260 ERR("Could not create key %s\n",debugstr_w(deformated));
2261 msi_free(deformated);
2263 return ERROR_SUCCESS;
2265 msi_free(deformated);
2267 value = MSI_RecordGetString(row,5);
2269 value_data = parse_value(package, value, &type, &size);
2272 value_data = (LPSTR)strdupW(szEmpty);
2273 size = sizeof(szEmpty);
2277 deformat_string(package, name, &deformated);
2281 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2283 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2288 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2289 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2291 TRACE("value %s of %s checked already exists\n",
2292 debugstr_w(deformated), debugstr_w(uikey));
2296 TRACE("Checked and setting value %s of %s\n",
2297 debugstr_w(deformated), debugstr_w(uikey));
2298 if (deformated || size)
2299 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2304 uirow = MSI_CreateRecord(3);
2305 MSI_RecordSetStringW(uirow,2,deformated);
2306 MSI_RecordSetStringW(uirow,1,uikey);
2309 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2311 MSI_RecordSetStringW(uirow,3,value);
2313 ui_actiondata(package,szWriteRegistryValues,uirow);
2314 msiobj_release( &uirow->hdr );
2316 msi_free(value_data);
2317 msi_free(deformated);
2320 return ERROR_SUCCESS;
2323 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2327 static const WCHAR ExecSeqQuery[] =
2328 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2329 '`','R','e','g','i','s','t','r','y','`',0 };
2331 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2332 if (rc != ERROR_SUCCESS)
2333 return ERROR_SUCCESS;
2335 /* increment progress bar each time action data is sent */
2336 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2338 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2340 msiobj_release(&view->hdr);
2344 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2346 package->script->CurrentlyScripting = TRUE;
2348 return ERROR_SUCCESS;
2352 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2357 static const WCHAR q1[]=
2358 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2359 '`','R','e','g','i','s','t','r','y','`',0};
2362 MSIFEATURE *feature;
2365 TRACE("InstallValidate\n");
2367 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2368 if (rc == ERROR_SUCCESS)
2370 MSI_IterateRecords( view, &progress, NULL, package );
2371 msiobj_release( &view->hdr );
2372 total += progress * REG_PROGRESS_VALUE;
2375 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2376 total += COMPONENT_PROGRESS_VALUE;
2378 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2379 total += file->FileSize;
2381 ui_progress(package,0,total,0,0);
2383 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2385 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2386 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2387 feature->ActionRequest);
2390 return ERROR_SUCCESS;
2393 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2395 MSIPACKAGE* package = param;
2396 LPCWSTR cond = NULL;
2397 LPCWSTR message = NULL;
2400 static const WCHAR title[]=
2401 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2403 cond = MSI_RecordGetString(row,1);
2405 r = MSI_EvaluateConditionW(package,cond);
2406 if (r == MSICONDITION_FALSE)
2408 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2411 message = MSI_RecordGetString(row,2);
2412 deformat_string(package,message,&deformated);
2413 MessageBoxW(NULL,deformated,title,MB_OK);
2414 msi_free(deformated);
2417 return ERROR_INSTALL_FAILURE;
2420 return ERROR_SUCCESS;
2423 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2426 MSIQUERY * view = NULL;
2427 static const WCHAR ExecSeqQuery[] =
2428 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2429 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2431 TRACE("Checking launch conditions\n");
2433 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2434 if (rc != ERROR_SUCCESS)
2435 return ERROR_SUCCESS;
2437 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2438 msiobj_release(&view->hdr);
2443 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2447 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2449 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2451 MSIRECORD * row = 0;
2453 LPWSTR deformated,buffer,deformated_name;
2455 static const WCHAR ExecSeqQuery[] =
2456 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2457 '`','R','e','g','i','s','t','r','y','`',' ',
2458 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2459 ' ','=',' ' ,'\'','%','s','\'',0 };
2460 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2461 static const WCHAR fmt2[]=
2462 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2464 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2468 root = MSI_RecordGetInteger(row,2);
2469 key = MSI_RecordGetString(row, 3);
2470 name = MSI_RecordGetString(row, 4);
2471 deformat_string(package, key , &deformated);
2472 deformat_string(package, name, &deformated_name);
2474 len = strlenW(deformated) + 6;
2475 if (deformated_name)
2476 len+=strlenW(deformated_name);
2478 buffer = msi_alloc( len *sizeof(WCHAR));
2480 if (deformated_name)
2481 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2483 sprintfW(buffer,fmt,root,deformated);
2485 msi_free(deformated);
2486 msi_free(deformated_name);
2487 msiobj_release(&row->hdr);
2491 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2493 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2498 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2501 return strdupW( file->TargetPath );
2506 static HKEY openSharedDLLsKey(void)
2509 static const WCHAR path[] =
2510 {'S','o','f','t','w','a','r','e','\\',
2511 'M','i','c','r','o','s','o','f','t','\\',
2512 'W','i','n','d','o','w','s','\\',
2513 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2514 'S','h','a','r','e','d','D','L','L','s',0};
2516 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2520 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2525 DWORD sz = sizeof(count);
2528 hkey = openSharedDLLsKey();
2529 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2530 if (rc != ERROR_SUCCESS)
2536 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2540 hkey = openSharedDLLsKey();
2542 msi_reg_set_val_dword( hkey, path, count );
2544 RegDeleteValueW(hkey,path);
2550 * Return TRUE if the count should be written out and FALSE if not
2552 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2554 MSIFEATURE *feature;
2558 /* only refcount DLLs */
2559 if (comp->KeyPath == NULL ||
2560 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2561 comp->Attributes & msidbComponentAttributesODBCDataSource)
2565 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2566 write = (count > 0);
2568 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2572 /* increment counts */
2573 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2577 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2580 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2582 if ( cl->component == comp )
2587 /* decrement counts */
2588 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2592 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2595 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2597 if ( cl->component == comp )
2602 /* ref count all the files in the component */
2607 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2609 if (file->Component == comp)
2610 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2614 /* add a count for permanent */
2615 if (comp->Attributes & msidbComponentAttributesPermanent)
2618 comp->RefCount = count;
2621 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2624 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2626 WCHAR squished_pc[GUID_SIZE];
2627 WCHAR squished_cc[GUID_SIZE];
2634 squash_guid(package->ProductCode,squished_pc);
2635 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2637 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2641 ui_progress(package,2,0,0,0);
2642 if (!comp->ComponentId)
2645 squash_guid(comp->ComponentId,squished_cc);
2647 msi_free(comp->FullKeypath);
2648 comp->FullKeypath = resolve_keypath( package, comp );
2650 ACTION_RefCountComponent( package, comp );
2652 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2653 debugstr_w(comp->Component),
2654 debugstr_w(squished_cc),
2655 debugstr_w(comp->FullKeypath),
2658 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2659 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2661 if (!comp->FullKeypath)
2664 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2665 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2668 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2671 if (rc != ERROR_SUCCESS)
2674 if (comp->Attributes & msidbComponentAttributesPermanent)
2676 static const WCHAR szPermKey[] =
2677 { '0','0','0','0','0','0','0','0','0','0','0','0',
2678 '0','0','0','0','0','0','0','0','0','0','0','0',
2679 '0','0','0','0','0','0','0','0',0 };
2681 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2684 if (comp->Action == INSTALLSTATE_LOCAL)
2685 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2691 WCHAR source[MAX_PATH];
2692 WCHAR base[MAX_PATH];
2695 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2696 static const WCHAR query[] = {
2697 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2698 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2699 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2700 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2701 '`','D','i','s','k','I','d','`',0};
2703 file = get_loaded_file(package, comp->KeyPath);
2707 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2708 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2709 ptr2 = strrchrW(source, '\\') + 1;
2710 msiobj_release(&row->hdr);
2712 lstrcpyW(base, package->PackagePath);
2713 ptr = strrchrW(base, '\\');
2716 sourcepath = resolve_file_source(package, file);
2717 ptr = sourcepath + lstrlenW(base);
2718 lstrcpyW(ptr2, ptr);
2719 msi_free(sourcepath);
2721 msi_reg_set_val_str(hkey, squished_pc, source);
2725 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2727 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2728 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
2730 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
2734 uirow = MSI_CreateRecord(3);
2735 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2736 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2737 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2738 ui_actiondata(package,szProcessComponents,uirow);
2739 msiobj_release( &uirow->hdr );
2742 return ERROR_SUCCESS;
2753 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2754 LPWSTR lpszName, LONG_PTR lParam)
2757 typelib_struct *tl_struct = (typelib_struct*) lParam;
2758 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2762 if (!IS_INTRESOURCE(lpszName))
2764 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2768 sz = strlenW(tl_struct->source)+4;
2769 sz *= sizeof(WCHAR);
2771 if ((INT_PTR)lpszName == 1)
2772 tl_struct->path = strdupW(tl_struct->source);
2775 tl_struct->path = msi_alloc(sz);
2776 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2779 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2780 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2783 msi_free(tl_struct->path);
2784 tl_struct->path = NULL;
2789 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2790 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2792 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2796 msi_free(tl_struct->path);
2797 tl_struct->path = NULL;
2799 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2800 ITypeLib_Release(tl_struct->ptLib);
2805 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2807 MSIPACKAGE* package = param;
2811 typelib_struct tl_struct;
2816 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2818 component = MSI_RecordGetString(row,3);
2819 comp = get_loaded_component(package,component);
2821 return ERROR_SUCCESS;
2823 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2825 TRACE("Skipping typelib reg due to disabled component\n");
2827 comp->Action = comp->Installed;
2829 return ERROR_SUCCESS;
2832 comp->Action = INSTALLSTATE_LOCAL;
2834 file = get_loaded_file( package, comp->KeyPath );
2836 return ERROR_SUCCESS;
2838 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2842 guid = MSI_RecordGetString(row,1);
2843 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2844 tl_struct.source = strdupW( file->TargetPath );
2845 tl_struct.path = NULL;
2847 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2848 (LONG_PTR)&tl_struct);
2856 helpid = MSI_RecordGetString(row,6);
2859 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2860 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2864 ERR("Failed to register type library %s\n",
2865 debugstr_w(tl_struct.path));
2868 ui_actiondata(package,szRegisterTypeLibraries,row);
2870 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2873 ITypeLib_Release(tl_struct.ptLib);
2874 msi_free(tl_struct.path);
2877 ERR("Failed to load type library %s\n",
2878 debugstr_w(tl_struct.source));
2880 FreeLibrary(module);
2881 msi_free(tl_struct.source);
2885 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
2888 ERR("Failed to load type library: %08x\n", hr);
2889 return ERROR_FUNCTION_FAILED;
2892 ITypeLib_Release(tlib);
2895 return ERROR_SUCCESS;
2898 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2901 * OK this is a bit confusing.. I am given a _Component key and I believe
2902 * that the file that is being registered as a type library is the "key file
2903 * of that component" which I interpret to mean "The file in the KeyPath of
2908 static const WCHAR Query[] =
2909 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2910 '`','T','y','p','e','L','i','b','`',0};
2912 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2913 if (rc != ERROR_SUCCESS)
2914 return ERROR_SUCCESS;
2916 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2917 msiobj_release(&view->hdr);
2921 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
2923 MSIPACKAGE *package = param;
2924 LPWSTR target_file, target_folder, filename;
2925 LPCWSTR buffer, extension;
2927 static const WCHAR szlnk[]={'.','l','n','k',0};
2928 IShellLinkW *sl = NULL;
2929 IPersistFile *pf = NULL;
2932 buffer = MSI_RecordGetString(row,4);
2933 comp = get_loaded_component(package,buffer);
2935 return ERROR_SUCCESS;
2937 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
2939 TRACE("Skipping shortcut creation due to disabled component\n");
2941 comp->Action = comp->Installed;
2943 return ERROR_SUCCESS;
2946 comp->Action = INSTALLSTATE_LOCAL;
2948 ui_actiondata(package,szCreateShortcuts,row);
2950 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
2951 &IID_IShellLinkW, (LPVOID *) &sl );
2955 ERR("CLSID_ShellLink not available\n");
2959 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
2962 ERR("QueryInterface(IID_IPersistFile) failed\n");
2966 buffer = MSI_RecordGetString(row,2);
2967 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
2969 /* may be needed because of a bug somewhere else */
2970 create_full_pathW(target_folder);
2972 filename = msi_dup_record_field( row, 3 );
2973 reduce_to_longfilename(filename);
2975 extension = strchrW(filename,'.');
2976 if (!extension || strcmpiW(extension,szlnk))
2978 int len = strlenW(filename);
2979 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
2980 memcpy(filename + len, szlnk, sizeof(szlnk));
2982 target_file = build_directory_name(2, target_folder, filename);
2983 msi_free(target_folder);
2986 buffer = MSI_RecordGetString(row,5);
2987 if (strchrW(buffer,'['))
2990 deformat_string(package,buffer,&deformated);
2991 IShellLinkW_SetPath(sl,deformated);
2992 msi_free(deformated);
2996 FIXME("poorly handled shortcut format, advertised shortcut\n");
2997 IShellLinkW_SetPath(sl,comp->FullKeypath);
3000 if (!MSI_RecordIsNull(row,6))
3003 buffer = MSI_RecordGetString(row,6);
3004 deformat_string(package,buffer,&deformated);
3005 IShellLinkW_SetArguments(sl,deformated);
3006 msi_free(deformated);
3009 if (!MSI_RecordIsNull(row,7))
3011 buffer = MSI_RecordGetString(row,7);
3012 IShellLinkW_SetDescription(sl,buffer);
3015 if (!MSI_RecordIsNull(row,8))
3016 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3018 if (!MSI_RecordIsNull(row,9))
3023 buffer = MSI_RecordGetString(row,9);
3025 Path = build_icon_path(package,buffer);
3026 index = MSI_RecordGetInteger(row,10);
3028 /* no value means 0 */
3029 if (index == MSI_NULL_INTEGER)
3032 IShellLinkW_SetIconLocation(sl,Path,index);
3036 if (!MSI_RecordIsNull(row,11))
3037 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3039 if (!MSI_RecordIsNull(row,12))
3042 buffer = MSI_RecordGetString(row,12);
3043 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3045 IShellLinkW_SetWorkingDirectory(sl,Path);
3049 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3050 IPersistFile_Save(pf,target_file,FALSE);
3052 msi_free(target_file);
3056 IPersistFile_Release( pf );
3058 IShellLinkW_Release( sl );
3060 return ERROR_SUCCESS;
3063 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3068 static const WCHAR Query[] =
3069 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3070 '`','S','h','o','r','t','c','u','t','`',0};
3072 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3073 if (rc != ERROR_SUCCESS)
3074 return ERROR_SUCCESS;
3076 res = CoInitialize( NULL );
3078 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3079 msiobj_release(&view->hdr);
3087 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3089 MSIPACKAGE* package = param;
3098 FileName = MSI_RecordGetString(row,1);
3101 ERR("Unable to get FileName\n");
3102 return ERROR_SUCCESS;
3105 FilePath = build_icon_path(package,FileName);
3107 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3109 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3110 FILE_ATTRIBUTE_NORMAL, NULL);
3112 if (the_file == INVALID_HANDLE_VALUE)
3114 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3116 return ERROR_SUCCESS;
3123 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3124 if (rc != ERROR_SUCCESS)
3126 ERR("Failed to get stream\n");
3127 CloseHandle(the_file);
3128 DeleteFileW(FilePath);
3131 WriteFile(the_file,buffer,sz,&write,NULL);
3132 } while (sz == 1024);
3136 CloseHandle(the_file);
3138 uirow = MSI_CreateRecord(1);
3139 MSI_RecordSetStringW(uirow,1,FileName);
3140 ui_actiondata(package,szPublishProduct,uirow);
3141 msiobj_release( &uirow->hdr );
3143 return ERROR_SUCCESS;
3146 static UINT msi_publish_icons(MSIPACKAGE *package)
3151 static const WCHAR query[]= {
3152 'S','E','L','E','C','T',' ','*',' ',
3153 'F','R','O','M',' ','`','I','c','o','n','`',0};
3155 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3156 if (r == ERROR_SUCCESS)
3158 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3159 msiobj_release(&view->hdr);
3162 return ERROR_SUCCESS;
3165 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3171 MSISOURCELISTINFO *info;
3173 r = RegCreateKeyW(hkey, szSourceList, &source);
3174 if (r != ERROR_SUCCESS)
3177 RegCloseKey(source);
3179 buffer = strrchrW(package->PackagePath, '\\') + 1;
3180 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3181 package->Context, MSICODE_PRODUCT,
3182 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3183 if (r != ERROR_SUCCESS)
3186 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3187 package->Context, MSICODE_PRODUCT,
3188 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3189 if (r != ERROR_SUCCESS)
3192 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3193 package->Context, MSICODE_PRODUCT,
3194 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3195 if (r != ERROR_SUCCESS)
3198 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3200 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3201 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3202 info->options, info->value);
3204 MsiSourceListSetInfoW(package->ProductCode, NULL,
3205 info->context, info->options,
3206 info->property, info->value);
3209 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3211 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3212 disk->context, disk->options,
3213 disk->disk_id, disk->volume_label, disk->disk_prompt);
3216 return ERROR_SUCCESS;
3219 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3221 MSIHANDLE hdb, suminfo;
3222 WCHAR guids[MAX_PATH];
3223 WCHAR packcode[SQUISH_GUID_SIZE];
3230 static const WCHAR szProductLanguage[] =
3231 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3232 static const WCHAR szARPProductIcon[] =
3233 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3234 static const WCHAR szProductVersion[] =
3235 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3236 static const WCHAR szAssignment[] =
3237 {'A','s','s','i','g','n','m','e','n','t',0};
3238 static const WCHAR szAdvertiseFlags[] =
3239 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3240 static const WCHAR szClients[] =
3241 {'C','l','i','e','n','t','s',0};
3242 static const WCHAR szColon[] = {':',0};
3244 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3245 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3248 langid = msi_get_property_int(package, szProductLanguage, 0);
3249 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3252 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3254 buffer = msi_dup_property(package, szARPProductIcon);
3257 LPWSTR path = build_icon_path(package,buffer);
3258 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3263 buffer = msi_dup_property(package, szProductVersion);
3266 DWORD verdword = msi_version_str_to_dword(buffer);
3267 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3271 msi_reg_set_val_dword(hkey, szAssignment, 0);
3272 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3273 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3274 msi_reg_set_val_str(hkey, szClients, szColon);
3276 hdb = alloc_msihandle(&package->db->hdr);
3278 return ERROR_NOT_ENOUGH_MEMORY;
3280 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3281 MsiCloseHandle(hdb);
3282 if (r != ERROR_SUCCESS)
3286 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3287 NULL, guids, &size);
3288 if (r != ERROR_SUCCESS)
3291 ptr = strchrW(guids, ';');
3293 squash_guid(guids, packcode);
3294 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3297 MsiCloseHandle(suminfo);
3298 return ERROR_SUCCESS;
3301 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3306 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3308 static const WCHAR szUpgradeCode[] =
3309 {'U','p','g','r','a','d','e','C','o','d','e',0};
3311 upgrade = msi_dup_property(package, szUpgradeCode);
3313 return ERROR_SUCCESS;
3315 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3317 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3318 if (r != ERROR_SUCCESS)
3323 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3324 if (r != ERROR_SUCCESS)
3328 squash_guid(package->ProductCode, squashed_pc);
3329 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3338 static BOOL msi_check_publish(MSIPACKAGE *package)
3340 MSIFEATURE *feature;
3342 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3344 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3351 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3353 MSIFEATURE *feature;
3355 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3357 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3364 static UINT msi_publish_patch(MSIPACKAGE *package, HKEY prodkey, HKEY hudkey)
3366 WCHAR patch_squashed[GUID_SIZE];
3369 UINT r = ERROR_FUNCTION_FAILED;
3371 res = RegCreateKeyExW(prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
3373 if (res != ERROR_SUCCESS)
3374 return ERROR_FUNCTION_FAILED;
3376 squash_guid(package->patch->patchcode, patch_squashed);
3378 res = RegSetValueExW(patches, szPatches, 0, REG_MULTI_SZ,
3379 (const BYTE *)patch_squashed,
3380 (lstrlenW(patch_squashed) + 1) * sizeof(WCHAR));
3381 if (res != ERROR_SUCCESS)
3384 res = RegSetValueExW(patches, patch_squashed, 0, REG_SZ,
3385 (const BYTE *)package->patch->transforms,
3386 (lstrlenW(package->patch->transforms) + 1) * sizeof(WCHAR));
3387 if (res == ERROR_SUCCESS)
3391 RegCloseKey(patches);
3396 * 99% of the work done here is only done for
3397 * advertised installs. However this is where the
3398 * Icon table is processed and written out
3399 * so that is what I am going to do here.
3401 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3407 /* FIXME: also need to publish if the product is in advertise mode */
3408 if (!msi_check_publish(package))
3409 return ERROR_SUCCESS;
3411 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3413 if (rc != ERROR_SUCCESS)
3416 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3417 NULL, &hudkey, TRUE);
3418 if (rc != ERROR_SUCCESS)
3421 rc = msi_publish_upgrade_code(package);
3422 if (rc != ERROR_SUCCESS)
3427 rc = msi_publish_patch(package, hukey, hudkey);
3428 if (rc != ERROR_SUCCESS)
3432 rc = msi_publish_product_properties(package, hukey);
3433 if (rc != ERROR_SUCCESS)
3436 rc = msi_publish_sourcelist(package, hukey);
3437 if (rc != ERROR_SUCCESS)
3440 rc = msi_publish_icons(package);
3444 RegCloseKey(hudkey);
3449 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3451 MSIPACKAGE *package = param;
3452 LPCWSTR component, section, key, value, identifier, dirproperty;
3453 LPWSTR deformated_section, deformated_key, deformated_value;
3454 LPWSTR folder, filename, fullname = NULL;
3455 LPCWSTR filenameptr;
3459 static const WCHAR szWindowsFolder[] =
3460 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3462 component = MSI_RecordGetString(row, 8);
3463 comp = get_loaded_component(package,component);
3465 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3467 TRACE("Skipping ini file due to disabled component %s\n",
3468 debugstr_w(component));
3470 comp->Action = comp->Installed;
3472 return ERROR_SUCCESS;
3475 comp->Action = INSTALLSTATE_LOCAL;
3477 identifier = MSI_RecordGetString(row,1);
3478 dirproperty = MSI_RecordGetString(row,3);
3479 section = MSI_RecordGetString(row,4);
3480 key = MSI_RecordGetString(row,5);
3481 value = MSI_RecordGetString(row,6);
3482 action = MSI_RecordGetInteger(row,7);
3484 deformat_string(package,section,&deformated_section);
3485 deformat_string(package,key,&deformated_key);
3486 deformat_string(package,value,&deformated_value);
3488 filename = msi_dup_record_field(row, 2);
3489 if (filename && (filenameptr = strchrW(filename, '|')))
3492 filenameptr = filename;
3496 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3498 folder = msi_dup_property( package, dirproperty );
3501 folder = msi_dup_property( package, szWindowsFolder );
3505 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3509 fullname = build_directory_name(2, folder, filenameptr);
3513 TRACE("Adding value %s to section %s in %s\n",
3514 debugstr_w(deformated_key), debugstr_w(deformated_section),
3515 debugstr_w(fullname));
3516 WritePrivateProfileStringW(deformated_section, deformated_key,
3517 deformated_value, fullname);
3519 else if (action == 1)
3522 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3523 returned, 10, fullname);
3524 if (returned[0] == 0)
3526 TRACE("Adding value %s to section %s in %s\n",
3527 debugstr_w(deformated_key), debugstr_w(deformated_section),
3528 debugstr_w(fullname));
3530 WritePrivateProfileStringW(deformated_section, deformated_key,
3531 deformated_value, fullname);
3534 else if (action == 3)
3535 FIXME("Append to existing section not yet implemented\n");
3537 uirow = MSI_CreateRecord(4);
3538 MSI_RecordSetStringW(uirow,1,identifier);
3539 MSI_RecordSetStringW(uirow,2,deformated_section);
3540 MSI_RecordSetStringW(uirow,3,deformated_key);
3541 MSI_RecordSetStringW(uirow,4,deformated_value);
3542 ui_actiondata(package,szWriteIniValues,uirow);
3543 msiobj_release( &uirow->hdr );
3549 msi_free(deformated_key);
3550 msi_free(deformated_value);
3551 msi_free(deformated_section);
3552 return ERROR_SUCCESS;
3555 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3559 static const WCHAR ExecSeqQuery[] =
3560 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3561 '`','I','n','i','F','i','l','e','`',0};
3563 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3564 if (rc != ERROR_SUCCESS)
3566 TRACE("no IniFile table\n");
3567 return ERROR_SUCCESS;
3570 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3571 msiobj_release(&view->hdr);
3575 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3577 MSIPACKAGE *package = param;
3582 static const WCHAR ExeStr[] =
3583 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3584 static const WCHAR close[] = {'\"',0};
3586 PROCESS_INFORMATION info;
3591 memset(&si,0,sizeof(STARTUPINFOW));
3593 filename = MSI_RecordGetString(row,1);
3594 file = get_loaded_file( package, filename );
3598 ERR("Unable to find file id %s\n",debugstr_w(filename));
3599 return ERROR_SUCCESS;
3602 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3604 FullName = msi_alloc(len*sizeof(WCHAR));
3605 strcpyW(FullName,ExeStr);
3606 strcatW( FullName, file->TargetPath );
3607 strcatW(FullName,close);
3609 TRACE("Registering %s\n",debugstr_w(FullName));
3610 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3615 CloseHandle(info.hThread);
3616 msi_dialog_check_messages(info.hProcess);
3617 CloseHandle(info.hProcess);
3623 uirow = MSI_CreateRecord( 2 );
3624 uipath = strdupW( file->TargetPath );
3625 p = strrchrW(uipath,'\\');
3628 MSI_RecordSetStringW( uirow, 1, &p[1] );
3629 MSI_RecordSetStringW( uirow, 2, uipath);
3630 ui_actiondata( package, szSelfRegModules, uirow);
3631 msiobj_release( &uirow->hdr );
3633 /* FIXME: call ui_progress? */
3635 return ERROR_SUCCESS;
3638 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3642 static const WCHAR ExecSeqQuery[] =
3643 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3644 '`','S','e','l','f','R','e','g','`',0};
3646 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3647 if (rc != ERROR_SUCCESS)
3649 TRACE("no SelfReg table\n");
3650 return ERROR_SUCCESS;
3653 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3654 msiobj_release(&view->hdr);
3656 return ERROR_SUCCESS;
3659 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3661 MSIFEATURE *feature;
3664 HKEY userdata = NULL;
3666 if (!msi_check_publish(package))
3667 return ERROR_SUCCESS;
3669 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3671 if (rc != ERROR_SUCCESS)
3674 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3676 if (rc != ERROR_SUCCESS)
3679 /* here the guids are base 85 encoded */
3680 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3686 BOOL absent = FALSE;
3689 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3690 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3691 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3695 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3699 if (feature->Feature_Parent)
3700 size += strlenW( feature->Feature_Parent )+2;
3702 data = msi_alloc(size * sizeof(WCHAR));
3705 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3707 MSICOMPONENT* component = cl->component;
3711 if (component->ComponentId)
3713 TRACE("From %s\n",debugstr_w(component->ComponentId));
3714 CLSIDFromString(component->ComponentId, &clsid);
3715 encode_base85_guid(&clsid,buf);
3716 TRACE("to %s\n",debugstr_w(buf));
3721 if (feature->Feature_Parent)
3723 static const WCHAR sep[] = {'\2',0};
3725 strcatW(data,feature->Feature_Parent);
3728 msi_reg_set_val_str( userdata, feature->Feature, data );
3732 if (feature->Feature_Parent)
3733 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3736 size += sizeof(WCHAR);
3737 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3738 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
3742 size += 2*sizeof(WCHAR);
3743 data = msi_alloc(size);
3746 if (feature->Feature_Parent)
3747 strcpyW( &data[1], feature->Feature_Parent );
3748 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3754 uirow = MSI_CreateRecord( 1 );
3755 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3756 ui_actiondata( package, szPublishFeatures, uirow);
3757 msiobj_release( &uirow->hdr );
3758 /* FIXME: call ui_progress? */
3763 RegCloseKey(userdata);
3767 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3772 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3774 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3776 if (r == ERROR_SUCCESS)
3778 RegDeleteValueW(hkey, feature->Feature);
3782 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3784 if (r == ERROR_SUCCESS)
3786 RegDeleteValueW(hkey, feature->Feature);
3790 return ERROR_SUCCESS;
3793 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3795 MSIFEATURE *feature;
3797 if (!msi_check_unpublish(package))
3798 return ERROR_SUCCESS;
3800 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3802 msi_unpublish_feature(package, feature);
3805 return ERROR_SUCCESS;
3808 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
3810 LPWSTR prop, val, key;
3816 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
3817 static const WCHAR szWindowsInstaller[] =
3818 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3819 static const WCHAR modpath_fmt[] =
3820 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3821 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3822 static const WCHAR szModifyPath[] =
3823 {'M','o','d','i','f','y','P','a','t','h',0};
3824 static const WCHAR szUninstallString[] =
3825 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3826 static const WCHAR szEstimatedSize[] =
3827 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3828 static const WCHAR szProductLanguage[] =
3829 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3830 static const WCHAR szProductVersion[] =
3831 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3832 static const WCHAR szProductName[] =
3833 {'P','r','o','d','u','c','t','N','a','m','e',0};
3834 static const WCHAR szDisplayName[] =
3835 {'D','i','s','p','l','a','y','N','a','m','e',0};
3836 static const WCHAR szDisplayVersion[] =
3837 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
3838 static const WCHAR szManufacturer[] =
3839 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
3841 static const LPCSTR propval[] = {
3842 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3843 "ARPCONTACT", "Contact",
3844 "ARPCOMMENTS", "Comments",
3845 "ProductName", "DisplayName",
3846 "ProductVersion", "DisplayVersion",
3847 "ARPHELPLINK", "HelpLink",
3848 "ARPHELPTELEPHONE", "HelpTelephone",
3849 "ARPINSTALLLOCATION", "InstallLocation",
3850 "SourceDir", "InstallSource",
3851 "Manufacturer", "Publisher",
3852 "ARPREADME", "Readme",
3854 "ARPURLINFOABOUT", "URLInfoAbout",
3855 "ARPURLUPDATEINFO", "URLUpdateInfo",
3858 const LPCSTR *p = propval;
3862 prop = strdupAtoW(*p++);
3863 key = strdupAtoW(*p++);
3864 val = msi_dup_property(package, prop);
3865 msi_reg_set_val_str(hkey, key, val);
3871 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
3873 size = deformat_string(package, modpath_fmt, &buffer);
3874 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
3875 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
3878 /* FIXME: Write real Estimated Size when we have it */
3879 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
3881 buffer = msi_dup_property(package, szProductName);
3882 msi_reg_set_val_str(hkey, szDisplayName, buffer);
3885 buffer = msi_dup_property(package, cszSourceDir);
3886 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
3889 buffer = msi_dup_property(package, szManufacturer);
3890 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
3893 GetLocalTime(&systime);
3894 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
3895 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
3897 langid = msi_get_property_int(package, szProductLanguage, 0);
3898 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3900 buffer = msi_dup_property(package, szProductVersion);
3901 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
3904 DWORD verdword = msi_version_str_to_dword(buffer);
3906 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3907 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
3908 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
3912 return ERROR_SUCCESS;
3915 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3917 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3918 LPWSTR upgrade_code;
3923 static const WCHAR szUpgradeCode[] = {
3924 'U','p','g','r','a','d','e','C','o','d','e',0};
3926 /* FIXME: also need to publish if the product is in advertise mode */
3927 if (!msi_check_publish(package))
3928 return ERROR_SUCCESS;
3930 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
3931 if (rc != ERROR_SUCCESS)
3934 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
3935 NULL, &props, TRUE);
3936 if (rc != ERROR_SUCCESS)
3939 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
3940 msi_free( package->db->localfile );
3941 package->db->localfile = NULL;
3943 rc = msi_publish_install_properties(package, hkey);
3944 if (rc != ERROR_SUCCESS)
3947 rc = msi_publish_install_properties(package, props);
3948 if (rc != ERROR_SUCCESS)
3951 upgrade_code = msi_dup_property(package, szUpgradeCode);
3954 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
3955 squash_guid(package->ProductCode, squashed_pc);
3956 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
3957 RegCloseKey(upgrade);
3958 msi_free(upgrade_code);
3964 return ERROR_SUCCESS;
3967 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
3969 return execute_script(package,INSTALL_SCRIPT);
3972 static UINT msi_unpublish_product(MSIPACKAGE *package)
3975 LPWSTR remove = NULL;
3976 LPWSTR *features = NULL;
3977 BOOL full_uninstall = TRUE;
3978 MSIFEATURE *feature;
3980 static const WCHAR szUpgradeCode[] =
3981 {'U','p','g','r','a','d','e','C','o','d','e',0};
3983 remove = msi_dup_property(package, szRemove);
3985 return ERROR_SUCCESS;
3987 features = msi_split_string(remove, ',');
3991 ERR("REMOVE feature list is empty!\n");
3992 return ERROR_FUNCTION_FAILED;
3995 if (!lstrcmpW(features[0], szAll))
3996 full_uninstall = TRUE;
3999 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4001 if (feature->Action != INSTALLSTATE_ABSENT)
4002 full_uninstall = FALSE;
4006 if (!full_uninstall)
4009 MSIREG_DeleteProductKey(package->ProductCode);
4010 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4011 MSIREG_DeleteUninstallKey(package->ProductCode);
4013 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4015 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4016 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4020 MSIREG_DeleteUserProductKey(package->ProductCode);
4021 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4024 upgrade = msi_dup_property(package, szUpgradeCode);
4027 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4034 return ERROR_SUCCESS;
4037 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4041 rc = msi_unpublish_product(package);
4042 if (rc != ERROR_SUCCESS)
4045 /* turn off scheduling */
4046 package->script->CurrentlyScripting= FALSE;
4048 /* first do the same as an InstallExecute */
4049 rc = ACTION_InstallExecute(package);
4050 if (rc != ERROR_SUCCESS)
4053 /* then handle Commit Actions */
4054 rc = execute_script(package,COMMIT_SCRIPT);
4059 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4061 static const WCHAR RunOnce[] = {
4062 'S','o','f','t','w','a','r','e','\\',
4063 'M','i','c','r','o','s','o','f','t','\\',
4064 'W','i','n','d','o','w','s','\\',
4065 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4066 'R','u','n','O','n','c','e',0};
4067 static const WCHAR InstallRunOnce[] = {
4068 'S','o','f','t','w','a','r','e','\\',
4069 'M','i','c','r','o','s','o','f','t','\\',
4070 'W','i','n','d','o','w','s','\\',
4071 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4072 'I','n','s','t','a','l','l','e','r','\\',
4073 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4075 static const WCHAR msiexec_fmt[] = {
4077 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4078 '\"','%','s','\"',0};
4079 static const WCHAR install_fmt[] = {
4080 '/','I',' ','\"','%','s','\"',' ',
4081 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4082 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4083 WCHAR buffer[256], sysdir[MAX_PATH];
4085 WCHAR squished_pc[100];
4087 squash_guid(package->ProductCode,squished_pc);
4089 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4090 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4091 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4094 msi_reg_set_val_str( hkey, squished_pc, buffer );
4097 TRACE("Reboot command %s\n",debugstr_w(buffer));
4099 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4100 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4102 msi_reg_set_val_str( hkey, squished_pc, buffer );
4105 return ERROR_INSTALL_SUSPEND;
4108 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4114 * We are currently doing what should be done here in the top level Install
4115 * however for Administrative and uninstalls this step will be needed
4117 if (!package->PackagePath)
4118 return ERROR_SUCCESS;
4120 msi_set_sourcedir_props(package, TRUE);
4122 attrib = GetFileAttributesW(package->db->path);
4123 if (attrib == INVALID_FILE_ATTRIBUTES)
4129 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4130 package->Context, MSICODE_PRODUCT,
4131 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4132 if (rc == ERROR_MORE_DATA)
4134 prompt = msi_alloc(size * sizeof(WCHAR));
4135 MsiSourceListGetInfoW(package->ProductCode, NULL,
4136 package->Context, MSICODE_PRODUCT,
4137 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4140 prompt = strdupW(package->db->path);
4142 msg = generate_error_string(package,1302,1,prompt);
4143 while(attrib == INVALID_FILE_ATTRIBUTES)
4145 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4148 rc = ERROR_INSTALL_USEREXIT;
4151 attrib = GetFileAttributesW(package->db->path);
4157 return ERROR_SUCCESS;
4162 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4169 static const WCHAR szPropKeys[][80] =
4171 {'P','r','o','d','u','c','t','I','D',0},
4172 {'U','S','E','R','N','A','M','E',0},
4173 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4177 static const WCHAR szRegKeys[][80] =
4179 {'P','r','o','d','u','c','t','I','D',0},
4180 {'R','e','g','O','w','n','e','r',0},
4181 {'R','e','g','C','o','m','p','a','n','y',0},
4185 if (msi_check_unpublish(package))
4187 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4188 return ERROR_SUCCESS;
4191 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4193 return ERROR_SUCCESS;
4195 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4197 if (rc != ERROR_SUCCESS)
4200 for( i = 0; szPropKeys[i][0]; i++ )
4202 buffer = msi_dup_property( package, szPropKeys[i] );
4203 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4208 msi_free(productid);
4211 /* FIXME: call ui_actiondata */
4217 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4221 package->script->InWhatSequence |= SEQUENCE_EXEC;
4222 rc = ACTION_ProcessExecSequence(package,FALSE);
4227 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4229 MSIPACKAGE *package = param;
4230 LPCWSTR compgroupid=NULL;
4231 LPCWSTR feature=NULL;
4232 LPCWSTR text = NULL;
4233 LPCWSTR qualifier = NULL;
4234 LPCWSTR component = NULL;
4235 LPWSTR advertise = NULL;
4236 LPWSTR output = NULL;
4238 UINT rc = ERROR_SUCCESS;
4243 component = MSI_RecordGetString(rec,3);
4244 comp = get_loaded_component(package,component);
4246 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4247 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4248 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4250 TRACE("Skipping: Component %s not scheduled for install\n",
4251 debugstr_w(component));
4253 return ERROR_SUCCESS;
4256 compgroupid = MSI_RecordGetString(rec,1);
4257 qualifier = MSI_RecordGetString(rec,2);
4259 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4260 if (rc != ERROR_SUCCESS)
4263 text = MSI_RecordGetString(rec,4);
4264 feature = MSI_RecordGetString(rec,5);
4266 advertise = create_component_advertise_string(package, comp, feature);
4268 sz = strlenW(advertise);
4271 sz += lstrlenW(text);
4274 sz *= sizeof(WCHAR);
4276 output = msi_alloc_zero(sz);
4277 strcpyW(output,advertise);
4278 msi_free(advertise);
4281 strcatW(output,text);
4283 msi_reg_set_val_multi_str( hkey, qualifier, output );
4290 uirow = MSI_CreateRecord( 2 );
4291 MSI_RecordSetStringW( uirow, 1, compgroupid );
4292 MSI_RecordSetStringW( uirow, 2, qualifier);
4293 ui_actiondata( package, szPublishComponents, uirow);
4294 msiobj_release( &uirow->hdr );
4295 /* FIXME: call ui_progress? */
4301 * At present I am ignorning the advertised components part of this and only
4302 * focusing on the qualified component sets
4304 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4308 static const WCHAR ExecSeqQuery[] =
4309 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4310 '`','P','u','b','l','i','s','h',
4311 'C','o','m','p','o','n','e','n','t','`',0};
4313 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4314 if (rc != ERROR_SUCCESS)
4315 return ERROR_SUCCESS;
4317 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4318 msiobj_release(&view->hdr);
4323 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4325 MSIPACKAGE *package = param;
4328 SC_HANDLE hscm, service = NULL;
4329 LPCWSTR comp, depends, pass;
4330 LPWSTR name = NULL, disp = NULL;
4331 LPCWSTR load_order, serv_name, key;
4332 DWORD serv_type, start_type;
4335 static const WCHAR query[] =
4336 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4337 '`','C','o','m','p','o','n','e','n','t','`',' ',
4338 'W','H','E','R','E',' ',
4339 '`','C','o','m','p','o','n','e','n','t','`',' ',
4340 '=','\'','%','s','\'',0};
4342 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4345 ERR("Failed to open the SC Manager!\n");
4349 start_type = MSI_RecordGetInteger(rec, 5);
4350 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4353 depends = MSI_RecordGetString(rec, 8);
4354 if (depends && *depends)
4355 FIXME("Dependency list unhandled!\n");
4357 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4358 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4359 serv_type = MSI_RecordGetInteger(rec, 4);
4360 err_control = MSI_RecordGetInteger(rec, 6);
4361 load_order = MSI_RecordGetString(rec, 7);
4362 serv_name = MSI_RecordGetString(rec, 9);
4363 pass = MSI_RecordGetString(rec, 10);
4364 comp = MSI_RecordGetString(rec, 12);
4366 /* fetch the service path */
4367 row = MSI_QueryGetRecord(package->db, query, comp);
4370 ERR("Control query failed!\n");
4374 key = MSI_RecordGetString(row, 6);
4376 file = get_loaded_file(package, key);
4377 msiobj_release(&row->hdr);
4380 ERR("Failed to load the service file\n");
4384 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4385 start_type, err_control, file->TargetPath,
4386 load_order, NULL, NULL, serv_name, pass);
4389 if (GetLastError() != ERROR_SERVICE_EXISTS)
4390 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4394 CloseServiceHandle(service);
4395 CloseServiceHandle(hscm);
4399 return ERROR_SUCCESS;
4402 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4406 static const WCHAR ExecSeqQuery[] =
4407 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4408 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4410 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4411 if (rc != ERROR_SUCCESS)
4412 return ERROR_SUCCESS;
4414 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4415 msiobj_release(&view->hdr);
4420 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4421 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4423 LPCWSTR *vector, *temp_vector;
4427 static const WCHAR separator[] = {'[','~',']',0};
4430 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4435 vector = msi_alloc(sizeof(LPWSTR));
4443 vector[*numargs - 1] = p;
4445 if ((q = strstrW(p, separator)))
4449 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4455 vector = temp_vector;
4464 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4466 MSIPACKAGE *package = param;
4468 SC_HANDLE scm, service = NULL;
4469 LPCWSTR name, *vector = NULL;
4471 DWORD event, numargs;
4472 UINT r = ERROR_FUNCTION_FAILED;
4474 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4475 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4476 return ERROR_SUCCESS;
4478 name = MSI_RecordGetString(rec, 2);
4479 event = MSI_RecordGetInteger(rec, 3);
4480 args = strdupW(MSI_RecordGetString(rec, 4));
4482 if (!(event & msidbServiceControlEventStart))
4483 return ERROR_SUCCESS;
4485 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4488 ERR("Failed to open the service control manager\n");
4492 service = OpenServiceW(scm, name, SERVICE_START);
4495 ERR("Failed to open service %s\n", debugstr_w(name));
4499 vector = msi_service_args_to_vector(args, &numargs);
4501 if (!StartServiceW(service, numargs, vector))
4503 ERR("Failed to start service %s\n", debugstr_w(name));
4510 CloseServiceHandle(service);
4511 CloseServiceHandle(scm);
4518 static UINT ACTION_StartServices( MSIPACKAGE *package )
4523 static const WCHAR query[] = {
4524 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4525 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4527 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4528 if (rc != ERROR_SUCCESS)
4529 return ERROR_SUCCESS;
4531 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4532 msiobj_release(&view->hdr);
4537 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4539 DWORD i, needed, count;
4540 ENUM_SERVICE_STATUSW *dependencies;
4544 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4545 0, &needed, &count))
4548 if (GetLastError() != ERROR_MORE_DATA)
4551 dependencies = msi_alloc(needed);
4555 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4556 needed, &needed, &count))
4559 for (i = 0; i < count; i++)
4561 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4562 SERVICE_STOP | SERVICE_QUERY_STATUS);
4566 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4573 msi_free(dependencies);
4577 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4579 MSIPACKAGE *package = param;
4581 SERVICE_STATUS status;
4582 SERVICE_STATUS_PROCESS ssp;
4583 SC_HANDLE scm = NULL, service = NULL;
4585 DWORD event, needed;
4587 event = MSI_RecordGetInteger(rec, 3);
4588 if (!(event & msidbServiceControlEventStop))
4589 return ERROR_SUCCESS;
4591 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4592 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4593 return ERROR_SUCCESS;
4595 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4596 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4597 args = strdupW(MSI_RecordGetString(rec, 4));
4599 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4602 WARN("Failed to open the SCM: %d\n", GetLastError());
4606 service = OpenServiceW(scm, name,
4608 SERVICE_QUERY_STATUS |
4609 SERVICE_ENUMERATE_DEPENDENTS);
4612 WARN("Failed to open service (%s): %d\n",
4613 debugstr_w(name), GetLastError());
4617 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4618 sizeof(SERVICE_STATUS_PROCESS), &needed))
4620 WARN("Failed to query service status (%s): %d\n",
4621 debugstr_w(name), GetLastError());
4625 if (ssp.dwCurrentState == SERVICE_STOPPED)
4628 stop_service_dependents(scm, service);
4630 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4631 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4634 CloseServiceHandle(service);
4635 CloseServiceHandle(scm);
4639 return ERROR_SUCCESS;
4642 static UINT ACTION_StopServices( MSIPACKAGE *package )
4647 static const WCHAR query[] = {
4648 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4649 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4651 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4652 if (rc != ERROR_SUCCESS)
4653 return ERROR_SUCCESS;
4655 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4656 msiobj_release(&view->hdr);
4661 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4665 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4667 if (!lstrcmpW(file->File, filename))
4674 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4676 MSIPACKAGE *package = param;
4677 LPWSTR driver, driver_path, ptr;
4678 WCHAR outpath[MAX_PATH];
4679 MSIFILE *driver_file, *setup_file;
4682 UINT r = ERROR_SUCCESS;
4684 static const WCHAR driver_fmt[] = {
4685 'D','r','i','v','e','r','=','%','s',0};
4686 static const WCHAR setup_fmt[] = {
4687 'S','e','t','u','p','=','%','s',0};
4688 static const WCHAR usage_fmt[] = {
4689 'F','i','l','e','U','s','a','g','e','=','1',0};
4691 desc = MSI_RecordGetString(rec, 3);
4693 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4694 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4696 if (!driver_file || !setup_file)
4698 ERR("ODBC Driver entry not found!\n");
4699 return ERROR_FUNCTION_FAILED;
4702 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4703 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4704 lstrlenW(usage_fmt) + 1;
4705 driver = msi_alloc(len * sizeof(WCHAR));
4707 return ERROR_OUTOFMEMORY;
4710 lstrcpyW(ptr, desc);
4711 ptr += lstrlenW(ptr) + 1;
4713 sprintfW(ptr, driver_fmt, driver_file->FileName);
4714 ptr += lstrlenW(ptr) + 1;
4716 sprintfW(ptr, setup_fmt, setup_file->FileName);
4717 ptr += lstrlenW(ptr) + 1;
4719 lstrcpyW(ptr, usage_fmt);
4720 ptr += lstrlenW(ptr) + 1;
4723 driver_path = strdupW(driver_file->TargetPath);
4724 ptr = strrchrW(driver_path, '\\');
4725 if (ptr) *ptr = '\0';
4727 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4728 NULL, ODBC_INSTALL_COMPLETE, &usage))
4730 ERR("Failed to install SQL driver!\n");
4731 r = ERROR_FUNCTION_FAILED;
4735 msi_free(driver_path);
4740 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4742 MSIPACKAGE *package = param;
4743 LPWSTR translator, translator_path, ptr;
4744 WCHAR outpath[MAX_PATH];
4745 MSIFILE *translator_file, *setup_file;
4748 UINT r = ERROR_SUCCESS;
4750 static const WCHAR translator_fmt[] = {
4751 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4752 static const WCHAR setup_fmt[] = {
4753 'S','e','t','u','p','=','%','s',0};
4755 desc = MSI_RecordGetString(rec, 3);
4757 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4758 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4760 if (!translator_file || !setup_file)
4762 ERR("ODBC Translator entry not found!\n");
4763 return ERROR_FUNCTION_FAILED;
4766 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4767 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4768 translator = msi_alloc(len * sizeof(WCHAR));
4770 return ERROR_OUTOFMEMORY;
4773 lstrcpyW(ptr, desc);
4774 ptr += lstrlenW(ptr) + 1;
4776 sprintfW(ptr, translator_fmt, translator_file->FileName);
4777 ptr += lstrlenW(ptr) + 1;
4779 sprintfW(ptr, setup_fmt, setup_file->FileName);
4780 ptr += lstrlenW(ptr) + 1;
4783 translator_path = strdupW(translator_file->TargetPath);
4784 ptr = strrchrW(translator_path, '\\');
4785 if (ptr) *ptr = '\0';
4787 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4788 NULL, ODBC_INSTALL_COMPLETE, &usage))
4790 ERR("Failed to install SQL translator!\n");
4791 r = ERROR_FUNCTION_FAILED;
4794 msi_free(translator);
4795 msi_free(translator_path);
4800 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4803 LPCWSTR desc, driver;
4804 WORD request = ODBC_ADD_SYS_DSN;
4807 UINT r = ERROR_SUCCESS;
4809 static const WCHAR attrs_fmt[] = {
4810 'D','S','N','=','%','s',0 };
4812 desc = MSI_RecordGetString(rec, 3);
4813 driver = MSI_RecordGetString(rec, 4);
4814 registration = MSI_RecordGetInteger(rec, 5);
4816 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4817 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4819 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4820 attrs = msi_alloc(len * sizeof(WCHAR));
4822 return ERROR_OUTOFMEMORY;
4824 sprintfW(attrs, attrs_fmt, desc);
4825 attrs[len - 1] = '\0';
4827 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4829 ERR("Failed to install SQL data source!\n");
4830 r = ERROR_FUNCTION_FAILED;
4838 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4843 static const WCHAR driver_query[] = {
4844 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4845 'O','D','B','C','D','r','i','v','e','r',0 };
4847 static const WCHAR translator_query[] = {
4848 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4849 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4851 static const WCHAR source_query[] = {
4852 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4853 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4855 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4856 if (rc != ERROR_SUCCESS)
4857 return ERROR_SUCCESS;
4859 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4860 msiobj_release(&view->hdr);
4862 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4863 if (rc != ERROR_SUCCESS)
4864 return ERROR_SUCCESS;
4866 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4867 msiobj_release(&view->hdr);
4869 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4870 if (rc != ERROR_SUCCESS)
4871 return ERROR_SUCCESS;
4873 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4874 msiobj_release(&view->hdr);
4879 #define ENV_ACT_SETALWAYS 0x1
4880 #define ENV_ACT_SETABSENT 0x2
4881 #define ENV_ACT_REMOVE 0x4
4882 #define ENV_ACT_REMOVEMATCH 0x8
4884 #define ENV_MOD_MACHINE 0x20000000
4885 #define ENV_MOD_APPEND 0x40000000
4886 #define ENV_MOD_PREFIX 0x80000000
4887 #define ENV_MOD_MASK 0xC0000000
4889 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4891 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4893 LPCWSTR cptr = *name;
4895 static const WCHAR prefix[] = {'[','~',']',0};
4896 static const int prefix_len = 3;
4902 *flags |= ENV_ACT_SETALWAYS;
4903 else if (*cptr == '+')
4904 *flags |= ENV_ACT_SETABSENT;
4905 else if (*cptr == '-')
4906 *flags |= ENV_ACT_REMOVE;
4907 else if (*cptr == '!')
4908 *flags |= ENV_ACT_REMOVEMATCH;
4909 else if (*cptr == '*')
4910 *flags |= ENV_MOD_MACHINE;
4920 ERR("Missing environment variable\n");
4921 return ERROR_FUNCTION_FAILED;
4926 LPCWSTR ptr = *value;
4927 if (!strncmpW(ptr, prefix, prefix_len))
4929 if (ptr[prefix_len] == szSemiColon[0])
4931 *flags |= ENV_MOD_APPEND;
4932 *value += lstrlenW(prefix);
4939 else if (lstrlenW(*value) >= prefix_len)
4941 ptr += lstrlenW(ptr) - prefix_len;
4942 if (!lstrcmpW(ptr, prefix))
4944 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
4946 *flags |= ENV_MOD_PREFIX;
4947 /* the "[~]" will be removed by deformat_string */;
4957 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4958 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4959 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4960 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4962 ERR("Invalid flags: %08x\n", *flags);
4963 return ERROR_FUNCTION_FAILED;
4967 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
4969 return ERROR_SUCCESS;
4972 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4974 MSIPACKAGE *package = param;
4975 LPCWSTR name, value;
4976 LPWSTR data = NULL, newval = NULL;
4977 LPWSTR deformatted = NULL, ptr;
4978 DWORD flags, type, size;
4980 HKEY env = NULL, root;
4981 LPCWSTR environment;
4983 static const WCHAR user_env[] =
4984 {'E','n','v','i','r','o','n','m','e','n','t',0};
4985 static const WCHAR machine_env[] =
4986 {'S','y','s','t','e','m','\\',
4987 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4988 'C','o','n','t','r','o','l','\\',
4989 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4990 'E','n','v','i','r','o','n','m','e','n','t',0};
4992 name = MSI_RecordGetString(rec, 2);
4993 value = MSI_RecordGetString(rec, 3);
4995 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
4997 res = env_set_flags(&name, &value, &flags);
4998 if (res != ERROR_SUCCESS || !value)
5001 if (value && !deformat_string(package, value, &deformatted))
5003 res = ERROR_OUTOFMEMORY;
5007 value = deformatted;
5009 if (flags & ENV_MOD_MACHINE)
5011 environment = machine_env;
5012 root = HKEY_LOCAL_MACHINE;
5016 environment = user_env;
5017 root = HKEY_CURRENT_USER;
5020 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5021 KEY_ALL_ACCESS, NULL, &env, NULL);
5022 if (res != ERROR_SUCCESS)
5025 if (flags & ENV_ACT_REMOVE)
5026 FIXME("Not removing environment variable on uninstall!\n");
5030 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5031 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5032 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5035 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
5037 /* Nothing to do. */
5040 res = ERROR_SUCCESS;
5044 /* If we are appending but the string was empty, strip ; */
5045 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
5047 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5048 newval = strdupW(value);
5051 res = ERROR_OUTOFMEMORY;
5057 /* Contrary to MSDN, +-variable to [~];path works */
5058 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
5060 res = ERROR_SUCCESS;
5064 data = msi_alloc(size);
5068 return ERROR_OUTOFMEMORY;
5071 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5072 if (res != ERROR_SUCCESS)
5075 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5077 res = RegDeleteKeyW(env, name);
5081 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
5082 if (flags & ENV_MOD_MASK)
5086 if (flags & ENV_MOD_APPEND) multiplier++;
5087 if (flags & ENV_MOD_PREFIX) multiplier++;
5088 mod_size = lstrlenW(value) * multiplier;
5089 size += mod_size * sizeof(WCHAR);
5092 newval = msi_alloc(size);
5096 res = ERROR_OUTOFMEMORY;
5100 if (flags & ENV_MOD_PREFIX)
5102 lstrcpyW(newval, value);
5103 ptr = newval + lstrlenW(value);
5106 lstrcpyW(ptr, data);
5108 if (flags & ENV_MOD_APPEND)
5110 lstrcatW(newval, value);
5113 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5114 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5117 if (env) RegCloseKey(env);
5118 msi_free(deformatted);
5124 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5128 static const WCHAR ExecSeqQuery[] =
5129 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5130 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5131 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5132 if (rc != ERROR_SUCCESS)
5133 return ERROR_SUCCESS;
5135 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5136 msiobj_release(&view->hdr);
5141 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5152 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5156 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5157 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5159 WARN("Source or dest is directory, not moving\n");
5163 if (options == msidbMoveFileOptionsMove)
5165 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5166 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5169 WARN("MoveFile failed: %d\n", GetLastError());
5175 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5176 ret = CopyFileW(source, dest, FALSE);
5179 WARN("CopyFile failed: %d\n", GetLastError());
5187 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5190 DWORD dirlen, pathlen;
5192 ptr = strrchrW(wildcard, '\\');
5193 dirlen = ptr - wildcard + 1;
5195 pathlen = dirlen + lstrlenW(filename) + 1;
5196 path = msi_alloc(pathlen * sizeof(WCHAR));
5198 lstrcpynW(path, wildcard, dirlen + 1);
5199 lstrcatW(path, filename);
5204 static void free_file_entry(FILE_LIST *file)
5206 msi_free(file->source);
5207 msi_free(file->dest);
5211 static void free_list(FILE_LIST *list)
5213 while (!list_empty(&list->entry))
5215 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5217 list_remove(&file->entry);
5218 free_file_entry(file);
5222 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5224 FILE_LIST *new, *file;
5225 LPWSTR ptr, filename;
5228 new = msi_alloc_zero(sizeof(FILE_LIST));
5232 new->source = strdupW(source);
5233 ptr = strrchrW(dest, '\\') + 1;
5234 filename = strrchrW(new->source, '\\') + 1;
5236 new->sourcename = filename;
5239 new->destname = ptr;
5241 new->destname = new->sourcename;
5243 size = (ptr - dest) + lstrlenW(filename) + 1;
5244 new->dest = msi_alloc(size * sizeof(WCHAR));
5247 free_file_entry(new);
5251 lstrcpynW(new->dest, dest, ptr - dest + 1);
5252 lstrcatW(new->dest, filename);
5254 if (list_empty(&files->entry))
5256 list_add_head(&files->entry, &new->entry);
5260 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5262 if (lstrcmpW(source, file->source) < 0)
5264 list_add_before(&file->entry, &new->entry);
5269 list_add_after(&file->entry, &new->entry);
5273 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5275 WIN32_FIND_DATAW wfd;
5279 FILE_LIST files, *file;
5282 hfile = FindFirstFileW(source, &wfd);
5283 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5285 list_init(&files.entry);
5287 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5289 if (is_dot_dir(wfd.cFileName)) continue;
5291 path = wildcard_to_file(source, wfd.cFileName);
5298 add_wildcard(&files, path, dest);
5302 /* no files match the wildcard */
5303 if (list_empty(&files.entry))
5306 /* only the first wildcard match gets renamed to dest */
5307 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5308 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5309 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5316 /* file->dest may be shorter after the reallocation, so add a NULL
5317 * terminator. This is needed for the call to strrchrW, as there will no
5318 * longer be a NULL terminator within the bounds of the allocation in this case.
5320 file->dest[size - 1] = '\0';
5321 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5323 while (!list_empty(&files.entry))
5325 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5327 msi_move_file(file->source, file->dest, options);
5329 list_remove(&file->entry);
5330 free_file_entry(file);
5341 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5343 MSIPACKAGE *package = param;
5346 LPWSTR destname = NULL;
5347 LPWSTR sourcedir = NULL, destdir = NULL;
5348 LPWSTR source = NULL, dest = NULL;
5351 BOOL ret, wildcards;
5353 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5354 if (!comp || !comp->Enabled ||
5355 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5357 TRACE("Component not set for install, not moving file\n");
5358 return ERROR_SUCCESS;
5361 sourcename = MSI_RecordGetString(rec, 3);
5362 options = MSI_RecordGetInteger(rec, 7);
5364 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5368 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5374 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5377 source = strdupW(sourcedir);
5383 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5384 source = msi_alloc(size * sizeof(WCHAR));
5388 lstrcpyW(source, sourcedir);
5389 if (source[lstrlenW(source) - 1] != '\\')
5390 lstrcatW(source, szBackSlash);
5391 lstrcatW(source, sourcename);
5394 wildcards = strchrW(source, '*') || strchrW(source, '?');
5396 if (MSI_RecordIsNull(rec, 4))
5400 destname = strdupW(sourcename);
5407 destname = strdupW(MSI_RecordGetString(rec, 4));
5409 reduce_to_longfilename(destname);
5414 size = lstrlenW(destname);
5416 size += lstrlenW(destdir) + 2;
5417 dest = msi_alloc(size * sizeof(WCHAR));
5421 lstrcpyW(dest, destdir);
5422 if (dest[lstrlenW(dest) - 1] != '\\')
5423 lstrcatW(dest, szBackSlash);
5426 lstrcatW(dest, destname);
5428 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5430 ret = CreateDirectoryW(destdir, NULL);
5433 WARN("CreateDirectory failed: %d\n", GetLastError());
5434 return ERROR_SUCCESS;
5439 msi_move_file(source, dest, options);
5441 move_files_wildcard(source, dest, options);
5444 msi_free(sourcedir);
5450 return ERROR_SUCCESS;
5453 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5458 static const WCHAR ExecSeqQuery[] =
5459 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5460 '`','M','o','v','e','F','i','l','e','`',0};
5462 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5463 if (rc != ERROR_SUCCESS)
5464 return ERROR_SUCCESS;
5466 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5467 msiobj_release(&view->hdr);
5472 typedef struct tagMSIASSEMBLY
5475 MSICOMPONENT *component;
5476 MSIFEATURE *feature;
5484 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5486 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5487 LPVOID pvReserved, HMODULE *phModDll);
5489 static BOOL init_functionpointers(void)
5495 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5497 hmscoree = LoadLibraryA("mscoree.dll");
5500 WARN("mscoree.dll not available\n");
5504 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5505 if (!pLoadLibraryShim)
5507 WARN("LoadLibraryShim not available\n");
5508 FreeLibrary(hmscoree);
5512 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5515 WARN("fusion.dll not available\n");
5516 FreeLibrary(hmscoree);
5520 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5522 FreeLibrary(hmscoree);
5526 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5529 IAssemblyCache *cache;
5531 UINT r = ERROR_FUNCTION_FAILED;
5533 TRACE("installing assembly: %s\n", debugstr_w(path));
5535 if (assembly->feature)
5536 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5538 if (assembly->manifest)
5539 FIXME("Manifest unhandled\n");
5541 if (assembly->application)
5543 FIXME("Assembly should be privately installed\n");
5544 return ERROR_SUCCESS;
5547 if (assembly->attributes == msidbAssemblyAttributesWin32)
5549 FIXME("Win32 assemblies not handled\n");
5550 return ERROR_SUCCESS;
5553 hr = pCreateAssemblyCache(&cache, 0);
5557 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5559 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5564 IAssemblyCache_Release(cache);
5568 typedef struct tagASSEMBLY_LIST
5570 MSIPACKAGE *package;
5571 IAssemblyCache *cache;
5572 struct list *assemblies;
5575 typedef struct tagASSEMBLY_NAME
5583 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
5585 ASSEMBLY_NAME *asmname = param;
5586 LPCWSTR name = MSI_RecordGetString(rec, 2);
5587 LPWSTR val = msi_dup_record_field(rec, 3);
5589 static const WCHAR Name[] = {'N','a','m','e',0};
5590 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
5591 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
5592 static const WCHAR PublicKeyToken[] = {
5593 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
5595 if (!strcmpiW(name, Name))
5596 asmname->name = val;
5597 else if (!strcmpiW(name, Version))
5598 asmname->version = val;
5599 else if (!strcmpiW(name, Culture))
5600 asmname->culture = val;
5601 else if (!strcmpiW(name, PublicKeyToken))
5602 asmname->pubkeytoken = val;
5606 return ERROR_SUCCESS;
5609 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
5613 *size = lstrlenW(append) + 1;
5614 *str = msi_alloc((*size) * sizeof(WCHAR));
5615 lstrcpyW(*str, append);
5619 (*size) += lstrlenW(append);
5620 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
5621 lstrcatW(*str, append);
5624 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
5627 ASSEMBLY_INFO asminfo;
5635 static const WCHAR separator[] = {',',' ',0};
5636 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
5637 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
5638 static const WCHAR PublicKeyToken[] = {
5639 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
5640 static const WCHAR query[] = {
5641 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5642 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
5643 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
5644 '=','\'','%','s','\'',0};
5648 ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
5649 ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
5651 r = MSI_OpenQuery(db, &view, query, comp->Component);
5652 if (r != ERROR_SUCCESS)
5653 return ERROR_SUCCESS;
5655 MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
5656 msiobj_release(&view->hdr);
5660 ERR("No assembly name specified!\n");
5664 append_str(&disp, &size, name.name);
5668 append_str(&disp, &size, separator);
5669 append_str(&disp, &size, Version);
5670 append_str(&disp, &size, name.version);
5675 append_str(&disp, &size, separator);
5676 append_str(&disp, &size, Culture);
5677 append_str(&disp, &size, name.culture);
5680 if (name.pubkeytoken)
5682 append_str(&disp, &size, separator);
5683 append_str(&disp, &size, PublicKeyToken);
5684 append_str(&disp, &size, name.pubkeytoken);
5687 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
5688 IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
5690 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
5694 msi_free(name.name);
5695 msi_free(name.version);
5696 msi_free(name.culture);
5697 msi_free(name.pubkeytoken);
5702 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
5704 ASSEMBLY_LIST *list = param;
5705 MSIASSEMBLY *assembly;
5707 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
5709 return ERROR_OUTOFMEMORY;
5711 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
5713 if (!assembly->component || !assembly->component->Enabled ||
5714 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5716 TRACE("Component not set for install, not publishing assembly\n");
5718 return ERROR_SUCCESS;
5721 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
5722 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
5724 if (!assembly->file)
5726 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
5727 return ERROR_FUNCTION_FAILED;
5730 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
5731 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
5732 assembly->attributes = MSI_RecordGetInteger(rec, 5);
5734 if (assembly->application)
5737 DWORD size = sizeof(version)/sizeof(WCHAR);
5739 /* FIXME: we should probably check the manifest file here */
5741 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
5742 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
5744 assembly->installed = TRUE;
5748 assembly->installed = check_assembly_installed(list->package->db,
5750 assembly->component);
5752 list_add_head(list->assemblies, &assembly->entry);
5753 return ERROR_SUCCESS;
5756 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
5758 IAssemblyCache *cache = NULL;
5764 static const WCHAR query[] =
5765 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5766 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5768 r = MSI_DatabaseOpenViewW(package->db, query, &view);
5769 if (r != ERROR_SUCCESS)
5770 return ERROR_SUCCESS;
5772 hr = pCreateAssemblyCache(&cache, 0);
5774 return ERROR_FUNCTION_FAILED;
5776 list.package = package;
5778 list.assemblies = assemblies;
5780 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
5781 msiobj_release(&view->hdr);
5783 IAssemblyCache_Release(cache);
5788 static void free_assemblies(struct list *assemblies)
5790 struct list *item, *cursor;
5792 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
5794 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
5796 list_remove(&assembly->entry);
5797 msi_free(assembly->application);
5798 msi_free(assembly->manifest);
5803 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
5805 MSIASSEMBLY *assembly;
5807 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
5809 if (!lstrcmpW(assembly->file->File, file))
5819 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
5820 LPWSTR *path, DWORD *attrs, PVOID user)
5822 MSIASSEMBLY *assembly;
5823 WCHAR temppath[MAX_PATH];
5824 struct list *assemblies = user;
5827 if (!find_assembly(assemblies, file, &assembly))
5830 GetTempPathW(MAX_PATH, temppath);
5831 PathAddBackslashW(temppath);
5832 lstrcatW(temppath, assembly->file->FileName);
5834 if (action == MSICABEXTRACT_BEGINEXTRACT)
5836 if (assembly->installed)
5839 *path = strdupW(temppath);
5840 *attrs = assembly->file->Attributes;
5842 else if (action == MSICABEXTRACT_FILEEXTRACTED)
5844 assembly->installed = TRUE;
5846 r = install_assembly(package, assembly, temppath);
5847 if (r != ERROR_SUCCESS)
5848 ERR("Failed to install assembly\n");
5854 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5857 struct list assemblies = LIST_INIT(assemblies);
5858 MSIASSEMBLY *assembly;
5861 if (!init_functionpointers() || !pCreateAssemblyCache)
5862 return ERROR_FUNCTION_FAILED;
5864 r = load_assemblies(package, &assemblies);
5865 if (r != ERROR_SUCCESS)
5868 if (list_empty(&assemblies))
5871 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
5874 r = ERROR_OUTOFMEMORY;
5878 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
5880 if (assembly->installed && !mi->is_continuous)
5883 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
5884 (assembly->file->IsCompressed && !mi->is_extracted))
5888 r = ready_media(package, assembly->file, mi);
5889 if (r != ERROR_SUCCESS)
5891 ERR("Failed to ready media\n");
5896 data.package = package;
5897 data.cb = installassembly_cb;
5898 data.user = &assemblies;
5900 if (assembly->file->IsCompressed &&
5901 !msi_cabextract(package, mi, &data))
5903 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
5904 r = ERROR_FUNCTION_FAILED;
5909 if (!assembly->file->IsCompressed)
5911 LPWSTR source = resolve_file_source(package, assembly->file);
5913 r = install_assembly(package, assembly, source);
5914 if (r != ERROR_SUCCESS)
5915 ERR("Failed to install assembly\n");
5920 /* FIXME: write Installer assembly reg values */
5924 free_assemblies(&assemblies);
5928 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5929 LPCSTR action, LPCWSTR table )
5931 static const WCHAR query[] = {
5932 'S','E','L','E','C','T',' ','*',' ',
5933 'F','R','O','M',' ','`','%','s','`',0 };
5934 MSIQUERY *view = NULL;
5938 r = MSI_OpenQuery( package->db, &view, query, table );
5939 if (r == ERROR_SUCCESS)
5941 r = MSI_IterateRecords(view, &count, NULL, package);
5942 msiobj_release(&view->hdr);
5946 FIXME("%s -> %u ignored %s table values\n",
5947 action, count, debugstr_w(table));
5949 return ERROR_SUCCESS;
5952 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5954 TRACE("%p\n", package);
5955 return ERROR_SUCCESS;
5958 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5960 static const WCHAR table[] =
5961 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5962 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5965 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5967 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5968 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5971 static UINT ACTION_BindImage( MSIPACKAGE *package )
5973 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5974 return msi_unimplemented_action_stub( package, "BindImage", table );
5977 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5979 static const WCHAR table[] = {
5980 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5981 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5984 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5986 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5987 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5990 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5992 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5993 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5996 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5998 static const WCHAR table[] = {
5999 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6000 return msi_unimplemented_action_stub( package, "DeleteServices", table );
6002 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6004 static const WCHAR table[] = {
6005 'P','r','o','d','u','c','t','I','D',0 };
6006 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6009 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6011 static const WCHAR table[] = {
6012 'E','n','v','i','r','o','n','m','e','n','t',0 };
6013 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6016 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6018 static const WCHAR table[] = {
6019 'M','s','i','A','s','s','e','m','b','l','y',0 };
6020 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6023 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6025 static const WCHAR table[] = { 'F','o','n','t',0 };
6026 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6029 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6031 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6032 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6035 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6037 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6038 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6041 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6043 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6044 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6047 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6049 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6050 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6053 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6055 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6056 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6059 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6061 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6062 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6065 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
6067 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
6068 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
6071 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6073 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6074 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6077 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6079 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6080 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6083 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6085 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6086 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6089 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6091 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6092 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6095 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6097 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6098 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6101 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6103 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6104 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6107 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6109 static const WCHAR table[] = { 'M','I','M','E',0 };
6110 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6113 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6115 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6116 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6119 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6121 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6122 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6125 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
6129 const WCHAR *action;
6130 UINT (*handler)(MSIPACKAGE *);
6134 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6135 { szAppSearch, ACTION_AppSearch },
6136 { szBindImage, ACTION_BindImage },
6137 { szCCPSearch, ACTION_CCPSearch },
6138 { szCostFinalize, ACTION_CostFinalize },
6139 { szCostInitialize, ACTION_CostInitialize },
6140 { szCreateFolders, ACTION_CreateFolders },
6141 { szCreateShortcuts, ACTION_CreateShortcuts },
6142 { szDeleteServices, ACTION_DeleteServices },
6143 { szDisableRollback, NULL },
6144 { szDuplicateFiles, ACTION_DuplicateFiles },
6145 { szExecuteAction, ACTION_ExecuteAction },
6146 { szFileCost, ACTION_FileCost },
6147 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6148 { szForceReboot, ACTION_ForceReboot },
6149 { szInstallAdminPackage, NULL },
6150 { szInstallExecute, ACTION_InstallExecute },
6151 { szInstallExecuteAgain, ACTION_InstallExecute },
6152 { szInstallFiles, ACTION_InstallFiles},
6153 { szInstallFinalize, ACTION_InstallFinalize },
6154 { szInstallInitialize, ACTION_InstallInitialize },
6155 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6156 { szInstallValidate, ACTION_InstallValidate },
6157 { szIsolateComponents, ACTION_IsolateComponents },
6158 { szLaunchConditions, ACTION_LaunchConditions },
6159 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6160 { szMoveFiles, ACTION_MoveFiles },
6161 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6162 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6163 { szInstallODBC, ACTION_InstallODBC },
6164 { szInstallServices, ACTION_InstallServices },
6165 { szPatchFiles, ACTION_PatchFiles },
6166 { szProcessComponents, ACTION_ProcessComponents },
6167 { szPublishComponents, ACTION_PublishComponents },
6168 { szPublishFeatures, ACTION_PublishFeatures },
6169 { szPublishProduct, ACTION_PublishProduct },
6170 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6171 { szRegisterComPlus, ACTION_RegisterComPlus},
6172 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6173 { szRegisterFonts, ACTION_RegisterFonts },
6174 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6175 { szRegisterProduct, ACTION_RegisterProduct },
6176 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6177 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6178 { szRegisterUser, ACTION_RegisterUser },
6179 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6180 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6181 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6182 { szRemoveFiles, ACTION_RemoveFiles },
6183 { szRemoveFolders, ACTION_RemoveFolders },
6184 { szRemoveIniValues, ACTION_RemoveIniValues },
6185 { szRemoveODBC, ACTION_RemoveODBC },
6186 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6187 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6188 { szResolveSource, ACTION_ResolveSource },
6189 { szRMCCPSearch, ACTION_RMCCPSearch },
6190 { szScheduleReboot, NULL },
6191 { szSelfRegModules, ACTION_SelfRegModules },
6192 { szSelfUnregModules, ACTION_SelfUnregModules },
6193 { szSetODBCFolders, NULL },
6194 { szStartServices, ACTION_StartServices },
6195 { szStopServices, ACTION_StopServices },
6196 { szUnpublishComponents, ACTION_UnpublishComponents },
6197 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6198 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6199 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6200 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6201 { szUnregisterFonts, ACTION_UnregisterFonts },
6202 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6203 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6204 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6205 { szValidateProductID, ACTION_ValidateProductID },
6206 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6207 { szWriteIniValues, ACTION_WriteIniValues },
6208 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6212 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6213 UINT* rc, BOOL force )
6219 if (!run && !package->script->CurrentlyScripting)
6224 if (strcmpW(action,szInstallFinalize) == 0 ||
6225 strcmpW(action,szInstallExecute) == 0 ||
6226 strcmpW(action,szInstallExecuteAgain) == 0)
6231 while (StandardActions[i].action != NULL)
6233 if (strcmpW(StandardActions[i].action, action)==0)
6237 ui_actioninfo(package, action, TRUE, 0);
6238 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6239 ui_actioninfo(package, action, FALSE, *rc);
6243 ui_actionstart(package, action);
6244 if (StandardActions[i].handler)
6246 *rc = StandardActions[i].handler(package);
6250 FIXME("unhandled standard action %s\n",debugstr_w(action));
6251 *rc = ERROR_SUCCESS;
6262 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
6264 UINT rc = ERROR_SUCCESS;
6267 TRACE("Performing action (%s)\n", debugstr_w(action));
6269 handled = ACTION_HandleStandardAction(package, action, &rc, force);
6272 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
6276 WARN("unhandled msi action %s\n", debugstr_w(action));
6277 rc = ERROR_FUNCTION_NOT_CALLED;
6283 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
6285 UINT rc = ERROR_SUCCESS;
6286 BOOL handled = FALSE;
6288 TRACE("Performing action (%s)\n", debugstr_w(action));
6290 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
6293 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
6295 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
6300 WARN("unhandled msi action %s\n", debugstr_w(action));
6301 rc = ERROR_FUNCTION_NOT_CALLED;
6307 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
6309 UINT rc = ERROR_SUCCESS;
6312 static const WCHAR ExecSeqQuery[] =
6313 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6314 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
6315 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
6316 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
6317 static const WCHAR UISeqQuery[] =
6318 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6319 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
6320 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
6321 ' ', '=',' ','%','i',0};
6323 if (needs_ui_sequence(package))
6324 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
6326 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
6330 LPCWSTR action, cond;
6332 TRACE("Running the actions\n");
6334 /* check conditions */
6335 cond = MSI_RecordGetString(row, 2);
6337 /* this is a hack to skip errors in the condition code */
6338 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
6340 msiobj_release(&row->hdr);
6341 return ERROR_SUCCESS;
6344 action = MSI_RecordGetString(row, 1);
6347 ERR("failed to fetch action\n");
6348 msiobj_release(&row->hdr);
6349 return ERROR_FUNCTION_FAILED;
6352 if (needs_ui_sequence(package))
6353 rc = ACTION_PerformUIAction(package, action, -1);
6355 rc = ACTION_PerformAction(package, action, -1, FALSE);
6357 msiobj_release(&row->hdr);
6363 /****************************************************
6364 * TOP level entry points
6365 *****************************************************/
6367 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
6368 LPCWSTR szCommandLine )
6373 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
6374 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
6376 MSI_SetPropertyW(package, szAction, szInstall);
6378 package->script->InWhatSequence = SEQUENCE_INSTALL;
6385 dir = strdupW(szPackagePath);
6386 p = strrchrW(dir, '\\');
6390 file = szPackagePath + (p - dir);
6395 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
6396 GetCurrentDirectoryW(MAX_PATH, dir);
6397 lstrcatW(dir, szBackSlash);
6398 file = szPackagePath;
6401 msi_free( package->PackagePath );
6402 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
6403 if (!package->PackagePath)
6406 return ERROR_OUTOFMEMORY;
6409 lstrcpyW(package->PackagePath, dir);
6410 lstrcatW(package->PackagePath, file);
6413 msi_set_sourcedir_props(package, FALSE);
6416 msi_parse_command_line( package, szCommandLine, FALSE );
6418 msi_apply_transforms( package );
6419 msi_apply_patches( package );
6421 if (!szCommandLine && msi_get_property_int( package, szInstalled, 0 ))
6423 TRACE("setting reinstall property\n");
6424 MSI_SetPropertyW( package, szReinstall, szAll );
6427 /* properties may have been added by a transform */
6428 msi_clone_properties( package );
6429 msi_set_context( package );
6431 if (needs_ui_sequence( package))
6433 package->script->InWhatSequence |= SEQUENCE_UI;
6434 rc = ACTION_ProcessUISequence(package);
6435 ui_exists = ui_sequence_exists(package);
6436 if (rc == ERROR_SUCCESS || !ui_exists)
6438 package->script->InWhatSequence |= SEQUENCE_EXEC;
6439 rc = ACTION_ProcessExecSequence(package, ui_exists);
6443 rc = ACTION_ProcessExecSequence(package, FALSE);
6445 package->script->CurrentlyScripting = FALSE;
6447 /* process the ending type action */
6448 if (rc == ERROR_SUCCESS)
6449 ACTION_PerformActionSequence(package, -1);
6450 else if (rc == ERROR_INSTALL_USEREXIT)
6451 ACTION_PerformActionSequence(package, -2);
6452 else if (rc == ERROR_INSTALL_SUSPEND)
6453 ACTION_PerformActionSequence(package, -4);
6455 ACTION_PerformActionSequence(package, -3);
6457 /* finish up running custom actions */
6458 ACTION_FinishCustomActions(package);
6460 if (rc == ERROR_SUCCESS && package->need_reboot)
6461 return ERROR_SUCCESS_REBOOT_REQUIRED;