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;
889 LPCWSTR dir, component;
895 component = MSI_RecordGetString(row, 2);
896 comp = get_loaded_component(package, component);
898 return ERROR_SUCCESS;
900 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
902 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
903 comp->Action = comp->Installed;
904 return ERROR_SUCCESS;
906 comp->Action = INSTALLSTATE_LOCAL;
908 dir = MSI_RecordGetString(row,1);
911 ERR("Unable to get folder id\n");
912 return ERROR_SUCCESS;
915 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
918 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
919 return ERROR_SUCCESS;
922 TRACE("Folder is %s\n",debugstr_w(full_path));
925 uirow = MSI_CreateRecord(1);
926 MSI_RecordSetStringW(uirow,1,full_path);
927 ui_actiondata(package,szCreateFolders,uirow);
928 msiobj_release( &uirow->hdr );
930 if (folder->State == 0)
931 create_full_pathW(full_path);
936 return ERROR_SUCCESS;
939 /* FIXME: probably should merge this with the above function */
940 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
942 UINT rc = ERROR_SUCCESS;
946 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
948 return ERROR_FUNCTION_FAILED;
950 /* create the path */
951 if (folder->State == 0)
953 create_full_pathW(install_path);
956 msi_free(install_path);
961 UINT msi_create_component_directories( MSIPACKAGE *package )
965 /* create all the folders required by the components are going to install */
966 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
968 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
970 msi_create_directory( package, comp->Directory );
973 return ERROR_SUCCESS;
976 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
978 static const WCHAR ExecSeqQuery[] =
979 {'S','E','L','E','C','T',' ',
980 '`','D','i','r','e','c','t','o','r','y','_','`',
981 ' ','F','R','O','M',' ',
982 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
986 /* create all the empty folders specified in the CreateFolder table */
987 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
988 if (rc != ERROR_SUCCESS)
989 return ERROR_SUCCESS;
991 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
992 msiobj_release(&view->hdr);
997 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
999 MSIPACKAGE *package = param;
1000 LPCWSTR dir, component;
1006 component = MSI_RecordGetString(row, 2);
1007 comp = get_loaded_component(package, component);
1009 return ERROR_SUCCESS;
1011 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
1013 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
1014 comp->Action = comp->Installed;
1015 return ERROR_SUCCESS;
1017 comp->Action = INSTALLSTATE_ABSENT;
1019 dir = MSI_RecordGetString( row, 1 );
1022 ERR("Unable to get folder id\n");
1023 return ERROR_SUCCESS;
1026 full_path = resolve_folder( package, dir, FALSE, FALSE, TRUE, &folder );
1029 ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1030 return ERROR_SUCCESS;
1033 TRACE("folder is %s\n", debugstr_w(full_path));
1035 uirow = MSI_CreateRecord( 1 );
1036 MSI_RecordSetStringW( uirow, 1, full_path );
1037 ui_actiondata( package, szRemoveFolders, uirow );
1038 msiobj_release( &uirow->hdr );
1040 RemoveDirectoryW( full_path );
1043 msi_free( full_path );
1044 return ERROR_SUCCESS;
1047 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1049 static const WCHAR query[] =
1050 {'S','E','L','E','C','T',' ', '`','D','i','r','e','c','t','o','r','y','_','`',
1051 ' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1056 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1057 if (rc != ERROR_SUCCESS)
1058 return ERROR_SUCCESS;
1060 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1061 msiobj_release( &view->hdr );
1066 static UINT load_component( MSIRECORD *row, LPVOID param )
1068 MSIPACKAGE *package = param;
1071 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1073 return ERROR_FUNCTION_FAILED;
1075 list_add_tail( &package->components, &comp->entry );
1077 /* fill in the data */
1078 comp->Component = msi_dup_record_field( row, 1 );
1080 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1082 comp->ComponentId = msi_dup_record_field( row, 2 );
1083 comp->Directory = msi_dup_record_field( row, 3 );
1084 comp->Attributes = MSI_RecordGetInteger(row,4);
1085 comp->Condition = msi_dup_record_field( row, 5 );
1086 comp->KeyPath = msi_dup_record_field( row, 6 );
1088 comp->Installed = INSTALLSTATE_UNKNOWN;
1089 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1091 return ERROR_SUCCESS;
1094 static UINT load_all_components( MSIPACKAGE *package )
1096 static const WCHAR query[] = {
1097 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1098 '`','C','o','m','p','o','n','e','n','t','`',0 };
1102 if (!list_empty(&package->components))
1103 return ERROR_SUCCESS;
1105 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1106 if (r != ERROR_SUCCESS)
1109 r = MSI_IterateRecords(view, NULL, load_component, package);
1110 msiobj_release(&view->hdr);
1115 MSIPACKAGE *package;
1116 MSIFEATURE *feature;
1119 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1123 cl = msi_alloc( sizeof (*cl) );
1125 return ERROR_NOT_ENOUGH_MEMORY;
1126 cl->component = comp;
1127 list_add_tail( &feature->Components, &cl->entry );
1129 return ERROR_SUCCESS;
1132 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1136 fl = msi_alloc( sizeof(*fl) );
1138 return ERROR_NOT_ENOUGH_MEMORY;
1139 fl->feature = child;
1140 list_add_tail( &parent->Children, &fl->entry );
1142 return ERROR_SUCCESS;
1145 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1147 _ilfs* ilfs = param;
1151 component = MSI_RecordGetString(row,1);
1153 /* check to see if the component is already loaded */
1154 comp = get_loaded_component( ilfs->package, component );
1157 ERR("unknown component %s\n", debugstr_w(component));
1158 return ERROR_FUNCTION_FAILED;
1161 add_feature_component( ilfs->feature, comp );
1162 comp->Enabled = TRUE;
1164 return ERROR_SUCCESS;
1167 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1169 MSIFEATURE *feature;
1174 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1176 if ( !lstrcmpW( feature->Feature, name ) )
1183 static UINT load_feature(MSIRECORD * row, LPVOID param)
1185 MSIPACKAGE* package = param;
1186 MSIFEATURE* feature;
1187 static const WCHAR Query1[] =
1188 {'S','E','L','E','C','T',' ',
1189 '`','C','o','m','p','o','n','e','n','t','_','`',
1190 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1191 'C','o','m','p','o','n','e','n','t','s','`',' ',
1192 'W','H','E','R','E',' ',
1193 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1198 /* fill in the data */
1200 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1202 return ERROR_NOT_ENOUGH_MEMORY;
1204 list_init( &feature->Children );
1205 list_init( &feature->Components );
1207 feature->Feature = msi_dup_record_field( row, 1 );
1209 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1211 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1212 feature->Title = msi_dup_record_field( row, 3 );
1213 feature->Description = msi_dup_record_field( row, 4 );
1215 if (!MSI_RecordIsNull(row,5))
1216 feature->Display = MSI_RecordGetInteger(row,5);
1218 feature->Level= MSI_RecordGetInteger(row,6);
1219 feature->Directory = msi_dup_record_field( row, 7 );
1220 feature->Attributes = MSI_RecordGetInteger(row,8);
1222 feature->Installed = INSTALLSTATE_UNKNOWN;
1223 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1225 list_add_tail( &package->features, &feature->entry );
1227 /* load feature components */
1229 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1230 if (rc != ERROR_SUCCESS)
1231 return ERROR_SUCCESS;
1233 ilfs.package = package;
1234 ilfs.feature = feature;
1236 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1237 msiobj_release(&view->hdr);
1239 return ERROR_SUCCESS;
1242 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1244 MSIPACKAGE* package = param;
1245 MSIFEATURE *parent, *child;
1247 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1249 return ERROR_FUNCTION_FAILED;
1251 if (!child->Feature_Parent)
1252 return ERROR_SUCCESS;
1254 parent = find_feature_by_name( package, child->Feature_Parent );
1256 return ERROR_FUNCTION_FAILED;
1258 add_feature_child( parent, child );
1259 return ERROR_SUCCESS;
1262 static UINT load_all_features( MSIPACKAGE *package )
1264 static const WCHAR query[] = {
1265 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1266 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1267 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1271 if (!list_empty(&package->features))
1272 return ERROR_SUCCESS;
1274 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1275 if (r != ERROR_SUCCESS)
1278 r = MSI_IterateRecords( view, NULL, load_feature, package );
1279 if (r != ERROR_SUCCESS)
1282 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1283 msiobj_release( &view->hdr );
1288 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1299 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1301 static const WCHAR query[] = {
1302 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1303 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1304 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1305 MSIQUERY *view = NULL;
1306 MSIRECORD *row = NULL;
1309 TRACE("%s\n", debugstr_w(file->File));
1311 r = MSI_OpenQuery(package->db, &view, query, file->File);
1312 if (r != ERROR_SUCCESS)
1315 r = MSI_ViewExecute(view, NULL);
1316 if (r != ERROR_SUCCESS)
1319 r = MSI_ViewFetch(view, &row);
1320 if (r != ERROR_SUCCESS)
1323 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1324 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1325 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1326 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1327 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1330 if (view) msiobj_release(&view->hdr);
1331 if (row) msiobj_release(&row->hdr);
1335 static UINT load_file(MSIRECORD *row, LPVOID param)
1337 MSIPACKAGE* package = param;
1341 /* fill in the data */
1343 file = msi_alloc_zero( sizeof (MSIFILE) );
1345 return ERROR_NOT_ENOUGH_MEMORY;
1347 file->File = msi_dup_record_field( row, 1 );
1349 component = MSI_RecordGetString( row, 2 );
1350 file->Component = get_loaded_component( package, component );
1352 if (!file->Component)
1354 WARN("Component not found: %s\n", debugstr_w(component));
1355 msi_free(file->File);
1357 return ERROR_SUCCESS;
1360 file->FileName = msi_dup_record_field( row, 3 );
1361 reduce_to_longfilename( file->FileName );
1363 file->ShortName = msi_dup_record_field( row, 3 );
1364 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1366 file->FileSize = MSI_RecordGetInteger( row, 4 );
1367 file->Version = msi_dup_record_field( row, 5 );
1368 file->Language = msi_dup_record_field( row, 6 );
1369 file->Attributes = MSI_RecordGetInteger( row, 7 );
1370 file->Sequence = MSI_RecordGetInteger( row, 8 );
1372 file->state = msifs_invalid;
1374 /* if the compressed bits are not set in the file attributes,
1375 * then read the information from the package word count property
1377 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1379 file->IsCompressed = FALSE;
1381 else if (file->Attributes &
1382 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1384 file->IsCompressed = TRUE;
1386 else if (file->Attributes & msidbFileAttributesNoncompressed)
1388 file->IsCompressed = FALSE;
1392 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1395 load_file_hash(package, file);
1397 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1399 list_add_tail( &package->files, &file->entry );
1401 return ERROR_SUCCESS;
1404 static UINT load_all_files(MSIPACKAGE *package)
1408 static const WCHAR Query[] =
1409 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1410 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1411 '`','S','e','q','u','e','n','c','e','`', 0};
1413 if (!list_empty(&package->files))
1414 return ERROR_SUCCESS;
1416 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1417 if (rc != ERROR_SUCCESS)
1418 return ERROR_SUCCESS;
1420 rc = MSI_IterateRecords(view, NULL, load_file, package);
1421 msiobj_release(&view->hdr);
1423 return ERROR_SUCCESS;
1426 static UINT load_folder( MSIRECORD *row, LPVOID param )
1428 MSIPACKAGE *package = param;
1429 static WCHAR szEmpty[] = { 0 };
1430 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1433 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1435 return ERROR_NOT_ENOUGH_MEMORY;
1437 folder->Directory = msi_dup_record_field( row, 1 );
1439 TRACE("%s\n", debugstr_w(folder->Directory));
1441 p = msi_dup_record_field(row, 3);
1443 /* split src and target dir */
1445 src_short = folder_split_path( p, ':' );
1447 /* split the long and short paths */
1448 tgt_long = folder_split_path( tgt_short, '|' );
1449 src_long = folder_split_path( src_short, '|' );
1451 /* check for no-op dirs */
1452 if (!lstrcmpW(szDot, tgt_short))
1453 tgt_short = szEmpty;
1454 if (!lstrcmpW(szDot, src_short))
1455 src_short = szEmpty;
1458 tgt_long = tgt_short;
1461 src_short = tgt_short;
1462 src_long = tgt_long;
1466 src_long = src_short;
1468 /* FIXME: use the target short path too */
1469 folder->TargetDefault = strdupW(tgt_long);
1470 folder->SourceShortPath = strdupW(src_short);
1471 folder->SourceLongPath = strdupW(src_long);
1474 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1475 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1476 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1478 folder->Parent = msi_dup_record_field( row, 2 );
1480 folder->Property = msi_dup_property( package, folder->Directory );
1482 list_add_tail( &package->folders, &folder->entry );
1484 TRACE("returning %p\n", folder);
1486 return ERROR_SUCCESS;
1489 static UINT load_all_folders( MSIPACKAGE *package )
1491 static const WCHAR query[] = {
1492 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1493 '`','D','i','r','e','c','t','o','r','y','`',0 };
1497 if (!list_empty(&package->folders))
1498 return ERROR_SUCCESS;
1500 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1501 if (r != ERROR_SUCCESS)
1504 r = MSI_IterateRecords(view, NULL, load_folder, package);
1505 msiobj_release(&view->hdr);
1510 * I am not doing any of the costing functionality yet.
1511 * Mostly looking at doing the Component and Feature loading
1513 * The native MSI does A LOT of modification to tables here. Mostly adding
1514 * a lot of temporary columns to the Feature and Component tables.
1516 * note: Native msi also tracks the short filename. But I am only going to
1517 * track the long ones. Also looking at this directory table
1518 * it appears that the directory table does not get the parents
1519 * resolved base on property only based on their entries in the
1522 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1524 static const WCHAR szCosting[] =
1525 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1527 MSI_SetPropertyW(package, szCosting, szZero);
1528 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1530 load_all_folders( package );
1531 load_all_components( package );
1532 load_all_features( package );
1533 load_all_files( package );
1535 return ERROR_SUCCESS;
1538 static UINT execute_script(MSIPACKAGE *package, UINT script )
1541 UINT rc = ERROR_SUCCESS;
1543 TRACE("Executing Script %i\n",script);
1545 if (!package->script)
1547 ERR("no script!\n");
1548 return ERROR_FUNCTION_FAILED;
1551 for (i = 0; i < package->script->ActionCount[script]; i++)
1554 action = package->script->Actions[script][i];
1555 ui_actionstart(package, action);
1556 TRACE("Executing Action (%s)\n",debugstr_w(action));
1557 rc = ACTION_PerformAction(package, action, script, TRUE);
1558 if (rc != ERROR_SUCCESS)
1561 msi_free_action_script(package, script);
1565 static UINT ACTION_FileCost(MSIPACKAGE *package)
1567 return ERROR_SUCCESS;
1570 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1576 state = MsiQueryProductStateW(package->ProductCode);
1578 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1580 if (!comp->ComponentId)
1583 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1584 comp->Installed = INSTALLSTATE_ABSENT;
1587 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1588 package->Context, comp->ComponentId,
1590 if (r != ERROR_SUCCESS)
1591 comp->Installed = INSTALLSTATE_ABSENT;
1596 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1598 MSIFEATURE *feature;
1601 state = MsiQueryProductStateW(package->ProductCode);
1603 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1605 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1606 feature->Installed = INSTALLSTATE_ABSENT;
1609 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1615 static BOOL process_state_property(MSIPACKAGE* package, int level,
1616 LPCWSTR property, INSTALLSTATE state)
1619 MSIFEATURE *feature;
1621 override = msi_dup_property( package, property );
1625 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1627 if (lstrcmpW(property, szRemove) &&
1628 (feature->Level <= 0 || feature->Level > level))
1631 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1633 if (strcmpiW(override, szAll)==0)
1634 msi_feature_set_state(package, feature, state);
1637 LPWSTR ptr = override;
1638 LPWSTR ptr2 = strchrW(override,',');
1642 int len = ptr2 - ptr;
1644 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1645 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1647 msi_feature_set_state(package, feature, state);
1653 ptr2 = strchrW(ptr,',');
1665 static BOOL process_overrides( MSIPACKAGE *package, int level )
1667 static const WCHAR szAddLocal[] =
1668 {'A','D','D','L','O','C','A','L',0};
1669 static const WCHAR szAddSource[] =
1670 {'A','D','D','S','O','U','R','C','E',0};
1671 static const WCHAR szAdvertise[] =
1672 {'A','D','V','E','R','T','I','S','E',0};
1675 /* all these activation/deactivation things happen in order and things
1676 * later on the list override things earlier on the list.
1678 * 0 INSTALLLEVEL processing
1691 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1692 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1693 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1694 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1695 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1698 MSI_SetPropertyW( package, szPreselected, szOne );
1703 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1706 static const WCHAR szlevel[] =
1707 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1708 MSICOMPONENT* component;
1709 MSIFEATURE *feature;
1711 TRACE("Checking Install Level\n");
1713 level = msi_get_property_int(package, szlevel, 1);
1715 if (!msi_get_property_int( package, szPreselected, 0 ))
1717 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1719 BOOL feature_state = ((feature->Level > 0) &&
1720 (feature->Level <= level));
1722 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1724 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1725 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1726 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1727 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1729 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1733 /* disable child features of unselected parent features */
1734 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1738 if (feature->Level > 0 && feature->Level <= level)
1741 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1742 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1747 * now we want to enable or disable components base on feature
1750 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1754 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1755 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1757 if (!feature->Level)
1760 /* features with components that have compressed files are made local */
1761 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1763 if (cl->component->Enabled &&
1764 cl->component->ForceLocalState &&
1765 feature->Action == INSTALLSTATE_SOURCE)
1767 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1772 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1774 component = cl->component;
1776 if (!component->Enabled)
1779 switch (feature->Action)
1781 case INSTALLSTATE_ABSENT:
1782 component->anyAbsent = 1;
1784 case INSTALLSTATE_ADVERTISED:
1785 component->hasAdvertiseFeature = 1;
1787 case INSTALLSTATE_SOURCE:
1788 component->hasSourceFeature = 1;
1790 case INSTALLSTATE_LOCAL:
1791 component->hasLocalFeature = 1;
1793 case INSTALLSTATE_DEFAULT:
1794 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1795 component->hasAdvertiseFeature = 1;
1796 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1797 component->hasSourceFeature = 1;
1799 component->hasLocalFeature = 1;
1807 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1809 /* if the component isn't enabled, leave it alone */
1810 if (!component->Enabled)
1813 /* check if it's local or source */
1814 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1815 (component->hasLocalFeature || component->hasSourceFeature))
1817 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1818 !component->ForceLocalState)
1819 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1821 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1825 /* if any feature is local, the component must be local too */
1826 if (component->hasLocalFeature)
1828 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1832 if (component->hasSourceFeature)
1834 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1838 if (component->hasAdvertiseFeature)
1840 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1844 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1845 if (component->anyAbsent)
1846 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1849 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1851 if (component->Action == INSTALLSTATE_DEFAULT)
1853 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1854 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1857 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1858 debugstr_w(component->Component), component->Installed, component->Action);
1862 return ERROR_SUCCESS;
1865 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1867 MSIPACKAGE *package = param;
1872 name = MSI_RecordGetString(row,1);
1874 f = get_loaded_folder(package, name);
1875 if (!f) return ERROR_SUCCESS;
1877 /* reset the ResolvedTarget */
1878 msi_free(f->ResolvedTarget);
1879 f->ResolvedTarget = NULL;
1881 /* This helper function now does ALL the work */
1882 TRACE("Dir %s ...\n",debugstr_w(name));
1883 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1884 TRACE("resolves to %s\n",debugstr_w(path));
1887 return ERROR_SUCCESS;
1890 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1892 MSIPACKAGE *package = param;
1894 MSIFEATURE *feature;
1896 name = MSI_RecordGetString( row, 1 );
1898 feature = get_loaded_feature( package, name );
1900 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1904 Condition = MSI_RecordGetString(row,3);
1906 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1908 int level = MSI_RecordGetInteger(row,2);
1909 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1910 feature->Level = level;
1913 return ERROR_SUCCESS;
1916 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1918 static const WCHAR name_fmt[] =
1919 {'%','u','.','%','u','.','%','u','.','%','u',0};
1920 static const WCHAR name[] = {'\\',0};
1921 VS_FIXEDFILEINFO *lpVer;
1922 WCHAR filever[0x100];
1928 TRACE("%s\n", debugstr_w(filename));
1930 versize = GetFileVersionInfoSizeW( filename, &handle );
1934 version = msi_alloc( versize );
1935 GetFileVersionInfoW( filename, 0, versize, version );
1937 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
1939 msi_free( version );
1943 sprintfW( filever, name_fmt,
1944 HIWORD(lpVer->dwFileVersionMS),
1945 LOWORD(lpVer->dwFileVersionMS),
1946 HIWORD(lpVer->dwFileVersionLS),
1947 LOWORD(lpVer->dwFileVersionLS));
1949 msi_free( version );
1951 return strdupW( filever );
1954 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1956 LPWSTR file_version;
1959 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1961 MSICOMPONENT* comp = file->Component;
1967 if (file->IsCompressed)
1968 comp->ForceLocalState = TRUE;
1970 /* calculate target */
1971 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
1973 msi_free(file->TargetPath);
1975 TRACE("file %s is named %s\n",
1976 debugstr_w(file->File), debugstr_w(file->FileName));
1978 file->TargetPath = build_directory_name(2, p, file->FileName);
1982 TRACE("file %s resolves to %s\n",
1983 debugstr_w(file->File), debugstr_w(file->TargetPath));
1985 /* don't check files of components that aren't installed */
1986 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
1987 comp->Installed == INSTALLSTATE_ABSENT)
1989 file->state = msifs_missing; /* assume files are missing */
1993 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1995 file->state = msifs_missing;
1996 comp->Cost += file->FileSize;
2000 if (file->Version &&
2001 (file_version = msi_get_disk_file_version( file->TargetPath )))
2003 TRACE("new %s old %s\n", debugstr_w(file->Version),
2004 debugstr_w(file_version));
2005 /* FIXME: seems like a bad way to compare version numbers */
2006 if (lstrcmpiW(file_version, file->Version)<0)
2008 file->state = msifs_overwrite;
2009 comp->Cost += file->FileSize;
2012 file->state = msifs_present;
2013 msi_free( file_version );
2016 file->state = msifs_present;
2019 return ERROR_SUCCESS;
2023 * A lot is done in this function aside from just the costing.
2024 * The costing needs to be implemented at some point but for now I am going
2025 * to focus on the directory building
2028 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2030 static const WCHAR ExecSeqQuery[] =
2031 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2032 '`','D','i','r','e','c','t','o','r','y','`',0};
2033 static const WCHAR ConditionQuery[] =
2034 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2035 '`','C','o','n','d','i','t','i','o','n','`',0};
2036 static const WCHAR szCosting[] =
2037 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2038 static const WCHAR szlevel[] =
2039 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2040 static const WCHAR szOutOfDiskSpace[] =
2041 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2043 UINT rc = ERROR_SUCCESS;
2047 TRACE("Building Directory properties\n");
2049 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2050 if (rc == ERROR_SUCCESS)
2052 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2054 msiobj_release(&view->hdr);
2057 /* read components states from the registry */
2058 ACTION_GetComponentInstallStates(package);
2059 ACTION_GetFeatureInstallStates(package);
2061 TRACE("File calculations\n");
2062 msi_check_file_install_states( package );
2064 if (!process_overrides( package, msi_get_property_int( package, szlevel, 1 ) ))
2066 TRACE("Evaluating Condition Table\n");
2068 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2069 if (rc == ERROR_SUCCESS)
2071 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2072 msiobj_release( &view->hdr );
2075 TRACE("Enabling or Disabling Components\n");
2076 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2078 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2080 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2081 comp->Enabled = FALSE;
2084 comp->Enabled = TRUE;
2088 MSI_SetPropertyW(package,szCosting,szOne);
2089 /* set default run level if not set */
2090 level = msi_dup_property( package, szlevel );
2092 MSI_SetPropertyW(package,szlevel, szOne);
2095 /* FIXME: check volume disk space */
2096 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2098 return MSI_SetFeatureStates(package);
2101 /* OK this value is "interpreted" and then formatted based on the
2102 first few characters */
2103 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2108 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2114 LPWSTR deformated = NULL;
2117 deformat_string(package, &value[2], &deformated);
2119 /* binary value type */
2123 *size = (strlenW(ptr)/2)+1;
2125 *size = strlenW(ptr)/2;
2127 data = msi_alloc(*size);
2133 /* if uneven pad with a zero in front */
2139 data[count] = (BYTE)strtol(byte,NULL,0);
2141 TRACE("Uneven byte count\n");
2149 data[count] = (BYTE)strtol(byte,NULL,0);
2152 msi_free(deformated);
2154 TRACE("Data %i bytes(%i)\n",*size,count);
2161 deformat_string(package, &value[1], &deformated);
2164 *size = sizeof(DWORD);
2165 data = msi_alloc(*size);
2171 if ( (*p < '0') || (*p > '9') )
2177 if (deformated[0] == '-')
2180 TRACE("DWORD %i\n",*(LPDWORD)data);
2182 msi_free(deformated);
2187 static const WCHAR szMulti[] = {'[','~',']',0};
2196 *type=REG_EXPAND_SZ;
2204 if (strstrW(value,szMulti))
2205 *type = REG_MULTI_SZ;
2207 /* remove initial delimiter */
2208 if (!strncmpW(value, szMulti, 3))
2211 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2213 /* add double NULL terminator */
2214 if (*type == REG_MULTI_SZ)
2216 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2217 data = msi_realloc_zero(data, *size);
2223 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2225 MSIPACKAGE *package = param;
2226 static const WCHAR szHCR[] =
2227 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2228 'R','O','O','T','\\',0};
2229 static const WCHAR szHCU[] =
2230 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2231 'U','S','E','R','\\',0};
2232 static const WCHAR szHLM[] =
2233 {'H','K','E','Y','_','L','O','C','A','L','_',
2234 'M','A','C','H','I','N','E','\\',0};
2235 static const WCHAR szHU[] =
2236 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2238 LPSTR value_data = NULL;
2239 HKEY root_key, hkey;
2242 LPCWSTR szRoot, component, name, key, value;
2247 BOOL check_first = FALSE;
2250 ui_progress(package,2,0,0,0);
2257 component = MSI_RecordGetString(row, 6);
2258 comp = get_loaded_component(package,component);
2260 return ERROR_SUCCESS;
2262 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2264 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2265 comp->Action = comp->Installed;
2266 return ERROR_SUCCESS;
2268 comp->Action = INSTALLSTATE_LOCAL;
2270 name = MSI_RecordGetString(row, 4);
2271 if( MSI_RecordIsNull(row,5) && name )
2273 /* null values can have special meanings */
2274 if (name[0]=='-' && name[1] == 0)
2275 return ERROR_SUCCESS;
2276 else if ((name[0]=='+' && name[1] == 0) ||
2277 (name[0] == '*' && name[1] == 0))
2282 root = MSI_RecordGetInteger(row,2);
2283 key = MSI_RecordGetString(row, 3);
2285 /* get the root key */
2290 LPWSTR all_users = msi_dup_property( package, szAllUsers );
2291 if (all_users && all_users[0] == '1')
2293 root_key = HKEY_LOCAL_MACHINE;
2298 root_key = HKEY_CURRENT_USER;
2301 msi_free(all_users);
2304 case 0: root_key = HKEY_CLASSES_ROOT;
2307 case 1: root_key = HKEY_CURRENT_USER;
2310 case 2: root_key = HKEY_LOCAL_MACHINE;
2313 case 3: root_key = HKEY_USERS;
2317 ERR("Unknown root %i\n",root);
2323 return ERROR_SUCCESS;
2325 deformat_string(package, key , &deformated);
2326 size = strlenW(deformated) + strlenW(szRoot) + 1;
2327 uikey = msi_alloc(size*sizeof(WCHAR));
2328 strcpyW(uikey,szRoot);
2329 strcatW(uikey,deformated);
2331 if (RegCreateKeyW( root_key, deformated, &hkey))
2333 ERR("Could not create key %s\n",debugstr_w(deformated));
2334 msi_free(deformated);
2336 return ERROR_SUCCESS;
2338 msi_free(deformated);
2340 value = MSI_RecordGetString(row,5);
2342 value_data = parse_value(package, value, &type, &size);
2345 value_data = (LPSTR)strdupW(szEmpty);
2346 size = sizeof(szEmpty);
2350 deformat_string(package, name, &deformated);
2354 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2356 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2361 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2362 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2364 TRACE("value %s of %s checked already exists\n",
2365 debugstr_w(deformated), debugstr_w(uikey));
2369 TRACE("Checked and setting value %s of %s\n",
2370 debugstr_w(deformated), debugstr_w(uikey));
2371 if (deformated || size)
2372 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2377 uirow = MSI_CreateRecord(3);
2378 MSI_RecordSetStringW(uirow,2,deformated);
2379 MSI_RecordSetStringW(uirow,1,uikey);
2382 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2384 MSI_RecordSetStringW(uirow,3,value);
2386 ui_actiondata(package,szWriteRegistryValues,uirow);
2387 msiobj_release( &uirow->hdr );
2389 msi_free(value_data);
2390 msi_free(deformated);
2393 return ERROR_SUCCESS;
2396 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2400 static const WCHAR ExecSeqQuery[] =
2401 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2402 '`','R','e','g','i','s','t','r','y','`',0 };
2404 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2405 if (rc != ERROR_SUCCESS)
2406 return ERROR_SUCCESS;
2408 /* increment progress bar each time action data is sent */
2409 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2411 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2413 msiobj_release(&view->hdr);
2417 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2419 package->script->CurrentlyScripting = TRUE;
2421 return ERROR_SUCCESS;
2425 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2430 static const WCHAR q1[]=
2431 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2432 '`','R','e','g','i','s','t','r','y','`',0};
2435 MSIFEATURE *feature;
2438 TRACE("InstallValidate\n");
2440 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2441 if (rc == ERROR_SUCCESS)
2443 MSI_IterateRecords( view, &progress, NULL, package );
2444 msiobj_release( &view->hdr );
2445 total += progress * REG_PROGRESS_VALUE;
2448 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2449 total += COMPONENT_PROGRESS_VALUE;
2451 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2452 total += file->FileSize;
2454 ui_progress(package,0,total,0,0);
2456 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2458 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2459 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2460 feature->ActionRequest);
2463 return ERROR_SUCCESS;
2466 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2468 MSIPACKAGE* package = param;
2469 LPCWSTR cond = NULL;
2470 LPCWSTR message = NULL;
2473 static const WCHAR title[]=
2474 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2476 cond = MSI_RecordGetString(row,1);
2478 r = MSI_EvaluateConditionW(package,cond);
2479 if (r == MSICONDITION_FALSE)
2481 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2484 message = MSI_RecordGetString(row,2);
2485 deformat_string(package,message,&deformated);
2486 MessageBoxW(NULL,deformated,title,MB_OK);
2487 msi_free(deformated);
2490 return ERROR_INSTALL_FAILURE;
2493 return ERROR_SUCCESS;
2496 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2499 MSIQUERY * view = NULL;
2500 static const WCHAR ExecSeqQuery[] =
2501 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2502 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2504 TRACE("Checking launch conditions\n");
2506 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2507 if (rc != ERROR_SUCCESS)
2508 return ERROR_SUCCESS;
2510 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2511 msiobj_release(&view->hdr);
2516 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2520 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2522 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2524 MSIRECORD * row = 0;
2526 LPWSTR deformated,buffer,deformated_name;
2528 static const WCHAR ExecSeqQuery[] =
2529 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2530 '`','R','e','g','i','s','t','r','y','`',' ',
2531 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2532 ' ','=',' ' ,'\'','%','s','\'',0 };
2533 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2534 static const WCHAR fmt2[]=
2535 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2537 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2541 root = MSI_RecordGetInteger(row,2);
2542 key = MSI_RecordGetString(row, 3);
2543 name = MSI_RecordGetString(row, 4);
2544 deformat_string(package, key , &deformated);
2545 deformat_string(package, name, &deformated_name);
2547 len = strlenW(deformated) + 6;
2548 if (deformated_name)
2549 len+=strlenW(deformated_name);
2551 buffer = msi_alloc( len *sizeof(WCHAR));
2553 if (deformated_name)
2554 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2556 sprintfW(buffer,fmt,root,deformated);
2558 msi_free(deformated);
2559 msi_free(deformated_name);
2560 msiobj_release(&row->hdr);
2564 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2566 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2571 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2574 return strdupW( file->TargetPath );
2579 static HKEY openSharedDLLsKey(void)
2582 static const WCHAR path[] =
2583 {'S','o','f','t','w','a','r','e','\\',
2584 'M','i','c','r','o','s','o','f','t','\\',
2585 'W','i','n','d','o','w','s','\\',
2586 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2587 'S','h','a','r','e','d','D','L','L','s',0};
2589 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2593 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2598 DWORD sz = sizeof(count);
2601 hkey = openSharedDLLsKey();
2602 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2603 if (rc != ERROR_SUCCESS)
2609 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2613 hkey = openSharedDLLsKey();
2615 msi_reg_set_val_dword( hkey, path, count );
2617 RegDeleteValueW(hkey,path);
2623 * Return TRUE if the count should be written out and FALSE if not
2625 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2627 MSIFEATURE *feature;
2631 /* only refcount DLLs */
2632 if (comp->KeyPath == NULL ||
2633 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2634 comp->Attributes & msidbComponentAttributesODBCDataSource)
2638 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2639 write = (count > 0);
2641 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2645 /* increment counts */
2646 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2650 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
2653 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2655 if ( cl->component == comp )
2660 /* decrement counts */
2661 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2665 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
2668 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2670 if ( cl->component == comp )
2675 /* ref count all the files in the component */
2680 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2682 if (file->Component == comp)
2683 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2687 /* add a count for permanent */
2688 if (comp->Attributes & msidbComponentAttributesPermanent)
2691 comp->RefCount = count;
2694 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2697 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2699 WCHAR squished_pc[GUID_SIZE];
2700 WCHAR squished_cc[GUID_SIZE];
2707 squash_guid(package->ProductCode,squished_pc);
2708 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2710 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2714 ui_progress(package,2,0,0,0);
2715 if (!comp->ComponentId)
2718 squash_guid(comp->ComponentId,squished_cc);
2720 msi_free(comp->FullKeypath);
2721 comp->FullKeypath = resolve_keypath( package, comp );
2723 ACTION_RefCountComponent( package, comp );
2725 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2726 debugstr_w(comp->Component),
2727 debugstr_w(squished_cc),
2728 debugstr_w(comp->FullKeypath),
2731 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
2732 comp->ActionRequest == INSTALLSTATE_SOURCE)
2734 if (!comp->FullKeypath)
2737 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2738 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2741 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2744 if (rc != ERROR_SUCCESS)
2747 if (comp->Attributes & msidbComponentAttributesPermanent)
2749 static const WCHAR szPermKey[] =
2750 { '0','0','0','0','0','0','0','0','0','0','0','0',
2751 '0','0','0','0','0','0','0','0','0','0','0','0',
2752 '0','0','0','0','0','0','0','0',0 };
2754 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2757 if (comp->Action == INSTALLSTATE_LOCAL)
2758 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2764 WCHAR source[MAX_PATH];
2765 WCHAR base[MAX_PATH];
2768 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2769 static const WCHAR query[] = {
2770 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2771 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2772 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2773 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2774 '`','D','i','s','k','I','d','`',0};
2776 file = get_loaded_file(package, comp->KeyPath);
2780 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2781 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2782 ptr2 = strrchrW(source, '\\') + 1;
2783 msiobj_release(&row->hdr);
2785 lstrcpyW(base, package->PackagePath);
2786 ptr = strrchrW(base, '\\');
2789 sourcepath = resolve_file_source(package, file);
2790 ptr = sourcepath + lstrlenW(base);
2791 lstrcpyW(ptr2, ptr);
2792 msi_free(sourcepath);
2794 msi_reg_set_val_str(hkey, squished_pc, source);
2798 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
2800 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2801 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
2803 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
2807 uirow = MSI_CreateRecord(3);
2808 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2809 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2810 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2811 ui_actiondata(package,szProcessComponents,uirow);
2812 msiobj_release( &uirow->hdr );
2815 return ERROR_SUCCESS;
2826 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2827 LPWSTR lpszName, LONG_PTR lParam)
2830 typelib_struct *tl_struct = (typelib_struct*) lParam;
2831 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2835 if (!IS_INTRESOURCE(lpszName))
2837 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2841 sz = strlenW(tl_struct->source)+4;
2842 sz *= sizeof(WCHAR);
2844 if ((INT_PTR)lpszName == 1)
2845 tl_struct->path = strdupW(tl_struct->source);
2848 tl_struct->path = msi_alloc(sz);
2849 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2852 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2853 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2856 msi_free(tl_struct->path);
2857 tl_struct->path = NULL;
2862 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2863 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2865 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2869 msi_free(tl_struct->path);
2870 tl_struct->path = NULL;
2872 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2873 ITypeLib_Release(tl_struct->ptLib);
2878 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2880 MSIPACKAGE* package = param;
2884 typelib_struct tl_struct;
2889 component = MSI_RecordGetString(row,3);
2890 comp = get_loaded_component(package,component);
2892 return ERROR_SUCCESS;
2894 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2896 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2897 comp->Action = comp->Installed;
2898 return ERROR_SUCCESS;
2900 comp->Action = INSTALLSTATE_LOCAL;
2902 file = get_loaded_file( package, comp->KeyPath );
2904 return ERROR_SUCCESS;
2906 ui_actiondata( package, szRegisterTypeLibraries, row );
2908 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2912 guid = MSI_RecordGetString(row,1);
2913 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2914 tl_struct.source = strdupW( file->TargetPath );
2915 tl_struct.path = NULL;
2917 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2918 (LONG_PTR)&tl_struct);
2926 helpid = MSI_RecordGetString(row,6);
2929 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2930 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2934 ERR("Failed to register type library %s\n",
2935 debugstr_w(tl_struct.path));
2937 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2939 ITypeLib_Release(tl_struct.ptLib);
2940 msi_free(tl_struct.path);
2943 ERR("Failed to load type library %s\n",
2944 debugstr_w(tl_struct.source));
2946 FreeLibrary(module);
2947 msi_free(tl_struct.source);
2951 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
2954 ERR("Failed to load type library: %08x\n", hr);
2955 return ERROR_INSTALL_FAILURE;
2958 ITypeLib_Release(tlib);
2961 return ERROR_SUCCESS;
2964 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2967 * OK this is a bit confusing.. I am given a _Component key and I believe
2968 * that the file that is being registered as a type library is the "key file
2969 * of that component" which I interpret to mean "The file in the KeyPath of
2974 static const WCHAR Query[] =
2975 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2976 '`','T','y','p','e','L','i','b','`',0};
2978 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2979 if (rc != ERROR_SUCCESS)
2980 return ERROR_SUCCESS;
2982 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2983 msiobj_release(&view->hdr);
2987 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
2989 MSIPACKAGE *package = param;
2990 LPCWSTR component, guid;
2998 component = MSI_RecordGetString( row, 3 );
2999 comp = get_loaded_component( package, component );
3001 return ERROR_SUCCESS;
3003 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3005 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3006 comp->Action = comp->Installed;
3007 return ERROR_SUCCESS;
3009 comp->Action = INSTALLSTATE_ABSENT;
3011 ui_actiondata( package, szUnregisterTypeLibraries, row );
3013 guid = MSI_RecordGetString( row, 1 );
3014 CLSIDFromString( (LPWSTR)guid, &libid );
3015 version = MSI_RecordGetInteger( row, 4 );
3016 language = MSI_RecordGetInteger( row, 2 );
3019 syskind = SYS_WIN64;
3021 syskind = SYS_WIN32;
3024 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3027 WARN("Failed to unregister typelib: %08x\n", hr);
3030 return ERROR_SUCCESS;
3033 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3037 static const WCHAR query[] =
3038 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3039 '`','T','y','p','e','L','i','b','`',0};
3041 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3042 if (rc != ERROR_SUCCESS)
3043 return ERROR_SUCCESS;
3045 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3046 msiobj_release( &view->hdr );
3050 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3052 static const WCHAR szlnk[] = {'.','l','n','k',0};
3053 LPCWSTR directory, extension;
3054 LPWSTR link_folder, link_file, filename;
3056 directory = MSI_RecordGetString( row, 2 );
3057 link_folder = resolve_folder( package, directory, FALSE, FALSE, TRUE, NULL );
3059 /* may be needed because of a bug somewhere else */
3060 create_full_pathW( link_folder );
3062 filename = msi_dup_record_field( row, 3 );
3063 reduce_to_longfilename( filename );
3065 extension = strchrW( filename, '.' );
3066 if (!extension || strcmpiW( extension, szlnk ))
3068 int len = strlenW( filename );
3069 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3070 memcpy( filename + len, szlnk, sizeof(szlnk) );
3072 link_file = build_directory_name( 2, link_folder, filename );
3073 msi_free( link_folder );
3074 msi_free( filename );
3079 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3081 MSIPACKAGE *package = param;
3082 LPWSTR link_file, deformated, path;
3083 LPCWSTR component, target;
3085 IShellLinkW *sl = NULL;
3086 IPersistFile *pf = NULL;
3089 component = MSI_RecordGetString(row, 4);
3090 comp = get_loaded_component(package, component);
3092 return ERROR_SUCCESS;
3094 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3096 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3097 comp->Action = comp->Installed;
3098 return ERROR_SUCCESS;
3100 comp->Action = INSTALLSTATE_LOCAL;
3102 ui_actiondata(package,szCreateShortcuts,row);
3104 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3105 &IID_IShellLinkW, (LPVOID *) &sl );
3109 ERR("CLSID_ShellLink not available\n");
3113 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3116 ERR("QueryInterface(IID_IPersistFile) failed\n");
3120 target = MSI_RecordGetString(row, 5);
3121 if (strchrW(target, '['))
3123 deformat_string(package, target, &deformated);
3124 IShellLinkW_SetPath(sl,deformated);
3125 msi_free(deformated);
3129 FIXME("poorly handled shortcut format, advertised shortcut\n");
3130 IShellLinkW_SetPath(sl,comp->FullKeypath);
3133 if (!MSI_RecordIsNull(row,6))
3135 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3136 deformat_string(package, arguments, &deformated);
3137 IShellLinkW_SetArguments(sl,deformated);
3138 msi_free(deformated);
3141 if (!MSI_RecordIsNull(row,7))
3143 LPCWSTR description = MSI_RecordGetString(row, 7);
3144 IShellLinkW_SetDescription(sl, description);
3147 if (!MSI_RecordIsNull(row,8))
3148 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3150 if (!MSI_RecordIsNull(row,9))
3153 LPCWSTR icon = MSI_RecordGetString(row, 9);
3155 path = build_icon_path(package, icon);
3156 index = MSI_RecordGetInteger(row,10);
3158 /* no value means 0 */
3159 if (index == MSI_NULL_INTEGER)
3162 IShellLinkW_SetIconLocation(sl, path, index);
3166 if (!MSI_RecordIsNull(row,11))
3167 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3169 if (!MSI_RecordIsNull(row,12))
3171 LPCWSTR wkdir = MSI_RecordGetString(row, 12);
3172 path = resolve_folder(package, wkdir, FALSE, FALSE, TRUE, NULL);
3174 IShellLinkW_SetWorkingDirectory(sl, path);
3178 link_file = get_link_file(package, row);
3180 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3181 IPersistFile_Save(pf, link_file, FALSE);
3183 msi_free(link_file);
3187 IPersistFile_Release( pf );
3189 IShellLinkW_Release( sl );
3191 return ERROR_SUCCESS;
3194 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3199 static const WCHAR Query[] =
3200 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3201 '`','S','h','o','r','t','c','u','t','`',0};
3203 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3204 if (rc != ERROR_SUCCESS)
3205 return ERROR_SUCCESS;
3207 res = CoInitialize( NULL );
3209 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3210 msiobj_release(&view->hdr);
3218 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3220 MSIPACKAGE *package = param;
3225 component = MSI_RecordGetString( row, 4 );
3226 comp = get_loaded_component( package, component );
3228 return ERROR_SUCCESS;
3230 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3232 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3233 comp->Action = comp->Installed;
3234 return ERROR_SUCCESS;
3236 comp->Action = INSTALLSTATE_ABSENT;
3238 ui_actiondata( package, szRemoveShortcuts, row );
3240 link_file = get_link_file( package, row );
3242 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3243 if (!DeleteFileW( link_file ))
3245 WARN("Failed to remove shortcut file %u\n", GetLastError());
3247 msi_free( link_file );
3249 return ERROR_SUCCESS;
3252 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3256 static const WCHAR query[] =
3257 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3258 '`','S','h','o','r','t','c','u','t','`',0};
3260 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3261 if (rc != ERROR_SUCCESS)
3262 return ERROR_SUCCESS;
3264 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3265 msiobj_release( &view->hdr );
3270 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3272 MSIPACKAGE* package = param;
3281 FileName = MSI_RecordGetString(row,1);
3284 ERR("Unable to get FileName\n");
3285 return ERROR_SUCCESS;
3288 FilePath = build_icon_path(package,FileName);
3290 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3292 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3293 FILE_ATTRIBUTE_NORMAL, NULL);
3295 if (the_file == INVALID_HANDLE_VALUE)
3297 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3299 return ERROR_SUCCESS;
3306 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3307 if (rc != ERROR_SUCCESS)
3309 ERR("Failed to get stream\n");
3310 CloseHandle(the_file);
3311 DeleteFileW(FilePath);
3314 WriteFile(the_file,buffer,sz,&write,NULL);
3315 } while (sz == 1024);
3319 CloseHandle(the_file);
3321 uirow = MSI_CreateRecord(1);
3322 MSI_RecordSetStringW(uirow,1,FileName);
3323 ui_actiondata(package,szPublishProduct,uirow);
3324 msiobj_release( &uirow->hdr );
3326 return ERROR_SUCCESS;
3329 static UINT msi_publish_icons(MSIPACKAGE *package)
3334 static const WCHAR query[]= {
3335 'S','E','L','E','C','T',' ','*',' ',
3336 'F','R','O','M',' ','`','I','c','o','n','`',0};
3338 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3339 if (r == ERROR_SUCCESS)
3341 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3342 msiobj_release(&view->hdr);
3345 return ERROR_SUCCESS;
3348 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3354 MSISOURCELISTINFO *info;
3356 r = RegCreateKeyW(hkey, szSourceList, &source);
3357 if (r != ERROR_SUCCESS)
3360 RegCloseKey(source);
3362 buffer = strrchrW(package->PackagePath, '\\') + 1;
3363 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3364 package->Context, MSICODE_PRODUCT,
3365 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3366 if (r != ERROR_SUCCESS)
3369 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3370 package->Context, MSICODE_PRODUCT,
3371 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3372 if (r != ERROR_SUCCESS)
3375 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3376 package->Context, MSICODE_PRODUCT,
3377 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3378 if (r != ERROR_SUCCESS)
3381 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3383 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3384 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3385 info->options, info->value);
3387 MsiSourceListSetInfoW(package->ProductCode, NULL,
3388 info->context, info->options,
3389 info->property, info->value);
3392 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3394 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3395 disk->context, disk->options,
3396 disk->disk_id, disk->volume_label, disk->disk_prompt);
3399 return ERROR_SUCCESS;
3402 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3404 MSIHANDLE hdb, suminfo;
3405 WCHAR guids[MAX_PATH];
3406 WCHAR packcode[SQUISH_GUID_SIZE];
3413 static const WCHAR szProductLanguage[] =
3414 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3415 static const WCHAR szARPProductIcon[] =
3416 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3417 static const WCHAR szProductVersion[] =
3418 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3419 static const WCHAR szAssignment[] =
3420 {'A','s','s','i','g','n','m','e','n','t',0};
3421 static const WCHAR szAdvertiseFlags[] =
3422 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3423 static const WCHAR szClients[] =
3424 {'C','l','i','e','n','t','s',0};
3425 static const WCHAR szColon[] = {':',0};
3427 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3428 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3431 langid = msi_get_property_int(package, szProductLanguage, 0);
3432 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3435 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3437 buffer = msi_dup_property(package, szARPProductIcon);
3440 LPWSTR path = build_icon_path(package,buffer);
3441 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3446 buffer = msi_dup_property(package, szProductVersion);
3449 DWORD verdword = msi_version_str_to_dword(buffer);
3450 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3454 msi_reg_set_val_dword(hkey, szAssignment, 0);
3455 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3456 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3457 msi_reg_set_val_str(hkey, szClients, szColon);
3459 hdb = alloc_msihandle(&package->db->hdr);
3461 return ERROR_NOT_ENOUGH_MEMORY;
3463 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3464 MsiCloseHandle(hdb);
3465 if (r != ERROR_SUCCESS)
3469 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3470 NULL, guids, &size);
3471 if (r != ERROR_SUCCESS)
3474 ptr = strchrW(guids, ';');
3476 squash_guid(guids, packcode);
3477 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3480 MsiCloseHandle(suminfo);
3481 return ERROR_SUCCESS;
3484 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3489 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3491 static const WCHAR szUpgradeCode[] =
3492 {'U','p','g','r','a','d','e','C','o','d','e',0};
3494 upgrade = msi_dup_property(package, szUpgradeCode);
3496 return ERROR_SUCCESS;
3498 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3500 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3501 if (r != ERROR_SUCCESS)
3506 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3507 if (r != ERROR_SUCCESS)
3511 squash_guid(package->ProductCode, squashed_pc);
3512 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3521 static BOOL msi_check_publish(MSIPACKAGE *package)
3523 MSIFEATURE *feature;
3525 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3527 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3534 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3536 MSIFEATURE *feature;
3538 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3540 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3547 static UINT msi_publish_patch(MSIPACKAGE *package, HKEY prodkey, HKEY hudkey)
3549 WCHAR patch_squashed[GUID_SIZE];
3552 UINT r = ERROR_FUNCTION_FAILED;
3554 res = RegCreateKeyExW(prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
3556 if (res != ERROR_SUCCESS)
3557 return ERROR_FUNCTION_FAILED;
3559 squash_guid(package->patch->patchcode, patch_squashed);
3561 res = RegSetValueExW(patches, szPatches, 0, REG_MULTI_SZ,
3562 (const BYTE *)patch_squashed,
3563 (lstrlenW(patch_squashed) + 1) * sizeof(WCHAR));
3564 if (res != ERROR_SUCCESS)
3567 res = RegSetValueExW(patches, patch_squashed, 0, REG_SZ,
3568 (const BYTE *)package->patch->transforms,
3569 (lstrlenW(package->patch->transforms) + 1) * sizeof(WCHAR));
3570 if (res == ERROR_SUCCESS)
3574 RegCloseKey(patches);
3579 * 99% of the work done here is only done for
3580 * advertised installs. However this is where the
3581 * Icon table is processed and written out
3582 * so that is what I am going to do here.
3584 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3590 /* FIXME: also need to publish if the product is in advertise mode */
3591 if (!msi_check_publish(package))
3592 return ERROR_SUCCESS;
3594 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3596 if (rc != ERROR_SUCCESS)
3599 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3600 NULL, &hudkey, TRUE);
3601 if (rc != ERROR_SUCCESS)
3604 rc = msi_publish_upgrade_code(package);
3605 if (rc != ERROR_SUCCESS)
3610 rc = msi_publish_patch(package, hukey, hudkey);
3611 if (rc != ERROR_SUCCESS)
3615 rc = msi_publish_product_properties(package, hukey);
3616 if (rc != ERROR_SUCCESS)
3619 rc = msi_publish_sourcelist(package, hukey);
3620 if (rc != ERROR_SUCCESS)
3623 rc = msi_publish_icons(package);
3627 RegCloseKey(hudkey);
3632 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3634 MSIPACKAGE *package = param;
3635 LPCWSTR component, section, key, value, identifier, dirproperty;
3636 LPWSTR deformated_section, deformated_key, deformated_value;
3637 LPWSTR folder, filename, fullname = NULL;
3638 LPCWSTR filenameptr;
3642 static const WCHAR szWindowsFolder[] =
3643 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3645 component = MSI_RecordGetString(row, 8);
3646 comp = get_loaded_component(package,component);
3648 return ERROR_SUCCESS;
3650 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3652 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3653 comp->Action = comp->Installed;
3654 return ERROR_SUCCESS;
3656 comp->Action = INSTALLSTATE_LOCAL;
3658 identifier = MSI_RecordGetString(row,1);
3659 dirproperty = MSI_RecordGetString(row,3);
3660 section = MSI_RecordGetString(row,4);
3661 key = MSI_RecordGetString(row,5);
3662 value = MSI_RecordGetString(row,6);
3663 action = MSI_RecordGetInteger(row,7);
3665 deformat_string(package,section,&deformated_section);
3666 deformat_string(package,key,&deformated_key);
3667 deformat_string(package,value,&deformated_value);
3669 filename = msi_dup_record_field(row, 2);
3670 if (filename && (filenameptr = strchrW(filename, '|')))
3673 filenameptr = filename;
3677 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3679 folder = msi_dup_property( package, dirproperty );
3682 folder = msi_dup_property( package, szWindowsFolder );
3686 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3690 fullname = build_directory_name(2, folder, filenameptr);
3694 TRACE("Adding value %s to section %s in %s\n",
3695 debugstr_w(deformated_key), debugstr_w(deformated_section),
3696 debugstr_w(fullname));
3697 WritePrivateProfileStringW(deformated_section, deformated_key,
3698 deformated_value, fullname);
3700 else if (action == 1)
3703 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3704 returned, 10, fullname);
3705 if (returned[0] == 0)
3707 TRACE("Adding value %s to section %s in %s\n",
3708 debugstr_w(deformated_key), debugstr_w(deformated_section),
3709 debugstr_w(fullname));
3711 WritePrivateProfileStringW(deformated_section, deformated_key,
3712 deformated_value, fullname);
3715 else if (action == 3)
3716 FIXME("Append to existing section not yet implemented\n");
3718 uirow = MSI_CreateRecord(4);
3719 MSI_RecordSetStringW(uirow,1,identifier);
3720 MSI_RecordSetStringW(uirow,2,deformated_section);
3721 MSI_RecordSetStringW(uirow,3,deformated_key);
3722 MSI_RecordSetStringW(uirow,4,deformated_value);
3723 ui_actiondata(package,szWriteIniValues,uirow);
3724 msiobj_release( &uirow->hdr );
3730 msi_free(deformated_key);
3731 msi_free(deformated_value);
3732 msi_free(deformated_section);
3733 return ERROR_SUCCESS;
3736 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3740 static const WCHAR ExecSeqQuery[] =
3741 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3742 '`','I','n','i','F','i','l','e','`',0};
3744 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3745 if (rc != ERROR_SUCCESS)
3747 TRACE("no IniFile table\n");
3748 return ERROR_SUCCESS;
3751 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3752 msiobj_release(&view->hdr);
3756 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3758 MSIPACKAGE *package = param;
3763 static const WCHAR ExeStr[] =
3764 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3765 static const WCHAR close[] = {'\"',0};
3767 PROCESS_INFORMATION info;
3772 memset(&si,0,sizeof(STARTUPINFOW));
3774 filename = MSI_RecordGetString(row,1);
3775 file = get_loaded_file( package, filename );
3779 ERR("Unable to find file id %s\n",debugstr_w(filename));
3780 return ERROR_SUCCESS;
3783 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3785 FullName = msi_alloc(len*sizeof(WCHAR));
3786 strcpyW(FullName,ExeStr);
3787 strcatW( FullName, file->TargetPath );
3788 strcatW(FullName,close);
3790 TRACE("Registering %s\n",debugstr_w(FullName));
3791 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3796 CloseHandle(info.hThread);
3797 msi_dialog_check_messages(info.hProcess);
3798 CloseHandle(info.hProcess);
3804 uirow = MSI_CreateRecord( 2 );
3805 uipath = strdupW( file->TargetPath );
3806 p = strrchrW(uipath,'\\');
3809 MSI_RecordSetStringW( uirow, 1, &p[1] );
3810 MSI_RecordSetStringW( uirow, 2, uipath);
3811 ui_actiondata( package, szSelfRegModules, uirow);
3812 msiobj_release( &uirow->hdr );
3814 /* FIXME: call ui_progress? */
3816 return ERROR_SUCCESS;
3819 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3823 static const WCHAR ExecSeqQuery[] =
3824 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3825 '`','S','e','l','f','R','e','g','`',0};
3827 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3828 if (rc != ERROR_SUCCESS)
3830 TRACE("no SelfReg table\n");
3831 return ERROR_SUCCESS;
3834 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3835 msiobj_release(&view->hdr);
3837 return ERROR_SUCCESS;
3840 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
3842 static const WCHAR regsvr32[] =
3843 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"',0};
3844 static const WCHAR close[] = {'\"',0};
3845 MSIPACKAGE *package = param;
3851 PROCESS_INFORMATION pi;
3856 memset( &si, 0, sizeof(STARTUPINFOW) );
3858 filename = MSI_RecordGetString( row, 1 );
3859 file = get_loaded_file( package, filename );
3863 ERR("Unable to find file id %s\n", debugstr_w(filename));
3864 return ERROR_SUCCESS;
3867 len = strlenW( regsvr32 ) + strlenW( file->TargetPath ) + 2;
3869 cmdline = msi_alloc( len * sizeof(WCHAR) );
3870 strcpyW( cmdline, regsvr32 );
3871 strcatW( cmdline, file->TargetPath );
3872 strcatW( cmdline, close );
3874 TRACE("Unregistering %s\n", debugstr_w(cmdline));
3876 ret = CreateProcessW( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, c_colon, &si, &pi );
3879 CloseHandle( pi.hThread );
3880 msi_dialog_check_messages( pi.hProcess );
3881 CloseHandle( pi.hProcess );
3884 msi_free( cmdline );
3886 uirow = MSI_CreateRecord( 2 );
3887 uipath = strdupW( file->TargetPath );
3888 if ((p = strrchrW( uipath, '\\' )))
3891 MSI_RecordSetStringW( uirow, 1, ++p );
3893 MSI_RecordSetStringW( uirow, 2, uipath );
3894 ui_actiondata( package, szSelfUnregModules, uirow );
3895 msiobj_release( &uirow->hdr );
3897 /* FIXME call ui_progress? */
3899 return ERROR_SUCCESS;
3902 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
3906 static const WCHAR query[] =
3907 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3908 '`','S','e','l','f','R','e','g','`',0};
3910 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3911 if (rc != ERROR_SUCCESS)
3913 TRACE("no SelfReg table\n");
3914 return ERROR_SUCCESS;
3917 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
3918 msiobj_release( &view->hdr );
3920 return ERROR_SUCCESS;
3923 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3925 MSIFEATURE *feature;
3928 HKEY userdata = NULL;
3930 if (!msi_check_publish(package))
3931 return ERROR_SUCCESS;
3933 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3935 if (rc != ERROR_SUCCESS)
3938 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3940 if (rc != ERROR_SUCCESS)
3943 /* here the guids are base 85 encoded */
3944 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3950 BOOL absent = FALSE;
3953 if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
3954 feature->ActionRequest != INSTALLSTATE_SOURCE &&
3955 feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
3958 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3962 if (feature->Feature_Parent)
3963 size += strlenW( feature->Feature_Parent )+2;
3965 data = msi_alloc(size * sizeof(WCHAR));
3968 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3970 MSICOMPONENT* component = cl->component;
3974 if (component->ComponentId)
3976 TRACE("From %s\n",debugstr_w(component->ComponentId));
3977 CLSIDFromString(component->ComponentId, &clsid);
3978 encode_base85_guid(&clsid,buf);
3979 TRACE("to %s\n",debugstr_w(buf));
3984 if (feature->Feature_Parent)
3986 static const WCHAR sep[] = {'\2',0};
3988 strcatW(data,feature->Feature_Parent);
3991 msi_reg_set_val_str( userdata, feature->Feature, data );
3995 if (feature->Feature_Parent)
3996 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3999 size += sizeof(WCHAR);
4000 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4001 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4005 size += 2*sizeof(WCHAR);
4006 data = msi_alloc(size);
4009 if (feature->Feature_Parent)
4010 strcpyW( &data[1], feature->Feature_Parent );
4011 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4017 uirow = MSI_CreateRecord( 1 );
4018 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4019 ui_actiondata( package, szPublishFeatures, uirow);
4020 msiobj_release( &uirow->hdr );
4021 /* FIXME: call ui_progress? */
4026 RegCloseKey(userdata);
4030 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4035 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4037 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4039 if (r == ERROR_SUCCESS)
4041 RegDeleteValueW(hkey, feature->Feature);
4045 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4047 if (r == ERROR_SUCCESS)
4049 RegDeleteValueW(hkey, feature->Feature);
4053 return ERROR_SUCCESS;
4056 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4058 MSIFEATURE *feature;
4060 if (!msi_check_unpublish(package))
4061 return ERROR_SUCCESS;
4063 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4065 msi_unpublish_feature(package, feature);
4068 return ERROR_SUCCESS;
4071 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4073 LPWSTR prop, val, key;
4079 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4080 static const WCHAR szWindowsInstaller[] =
4081 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4082 static const WCHAR modpath_fmt[] =
4083 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4084 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4085 static const WCHAR szModifyPath[] =
4086 {'M','o','d','i','f','y','P','a','t','h',0};
4087 static const WCHAR szUninstallString[] =
4088 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4089 static const WCHAR szEstimatedSize[] =
4090 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4091 static const WCHAR szProductLanguage[] =
4092 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4093 static const WCHAR szProductVersion[] =
4094 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4095 static const WCHAR szProductName[] =
4096 {'P','r','o','d','u','c','t','N','a','m','e',0};
4097 static const WCHAR szDisplayName[] =
4098 {'D','i','s','p','l','a','y','N','a','m','e',0};
4099 static const WCHAR szDisplayVersion[] =
4100 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4101 static const WCHAR szManufacturer[] =
4102 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4104 static const LPCSTR propval[] = {
4105 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4106 "ARPCONTACT", "Contact",
4107 "ARPCOMMENTS", "Comments",
4108 "ProductName", "DisplayName",
4109 "ProductVersion", "DisplayVersion",
4110 "ARPHELPLINK", "HelpLink",
4111 "ARPHELPTELEPHONE", "HelpTelephone",
4112 "ARPINSTALLLOCATION", "InstallLocation",
4113 "SourceDir", "InstallSource",
4114 "Manufacturer", "Publisher",
4115 "ARPREADME", "Readme",
4117 "ARPURLINFOABOUT", "URLInfoAbout",
4118 "ARPURLUPDATEINFO", "URLUpdateInfo",
4121 const LPCSTR *p = propval;
4125 prop = strdupAtoW(*p++);
4126 key = strdupAtoW(*p++);
4127 val = msi_dup_property(package, prop);
4128 msi_reg_set_val_str(hkey, key, val);
4134 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4136 size = deformat_string(package, modpath_fmt, &buffer);
4137 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4138 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4141 /* FIXME: Write real Estimated Size when we have it */
4142 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4144 buffer = msi_dup_property(package, szProductName);
4145 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4148 buffer = msi_dup_property(package, cszSourceDir);
4149 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4152 buffer = msi_dup_property(package, szManufacturer);
4153 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4156 GetLocalTime(&systime);
4157 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4158 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4160 langid = msi_get_property_int(package, szProductLanguage, 0);
4161 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4163 buffer = msi_dup_property(package, szProductVersion);
4164 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4167 DWORD verdword = msi_version_str_to_dword(buffer);
4169 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4170 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4171 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4175 return ERROR_SUCCESS;
4178 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4180 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4181 LPWSTR upgrade_code;
4186 static const WCHAR szUpgradeCode[] = {
4187 'U','p','g','r','a','d','e','C','o','d','e',0};
4189 /* FIXME: also need to publish if the product is in advertise mode */
4190 if (!msi_check_publish(package))
4191 return ERROR_SUCCESS;
4193 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4194 if (rc != ERROR_SUCCESS)
4197 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4198 NULL, &props, TRUE);
4199 if (rc != ERROR_SUCCESS)
4202 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4203 msi_free( package->db->localfile );
4204 package->db->localfile = NULL;
4206 rc = msi_publish_install_properties(package, hkey);
4207 if (rc != ERROR_SUCCESS)
4210 rc = msi_publish_install_properties(package, props);
4211 if (rc != ERROR_SUCCESS)
4214 upgrade_code = msi_dup_property(package, szUpgradeCode);
4217 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4218 squash_guid(package->ProductCode, squashed_pc);
4219 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4220 RegCloseKey(upgrade);
4221 msi_free(upgrade_code);
4227 return ERROR_SUCCESS;
4230 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4232 return execute_script(package,INSTALL_SCRIPT);
4235 static UINT msi_unpublish_product(MSIPACKAGE *package)
4238 LPWSTR remove = NULL;
4239 LPWSTR *features = NULL;
4240 BOOL full_uninstall = TRUE;
4241 MSIFEATURE *feature;
4243 static const WCHAR szUpgradeCode[] =
4244 {'U','p','g','r','a','d','e','C','o','d','e',0};
4246 remove = msi_dup_property(package, szRemove);
4248 return ERROR_SUCCESS;
4250 features = msi_split_string(remove, ',');
4254 ERR("REMOVE feature list is empty!\n");
4255 return ERROR_FUNCTION_FAILED;
4258 if (!lstrcmpW(features[0], szAll))
4259 full_uninstall = TRUE;
4262 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4264 if (feature->Action != INSTALLSTATE_ABSENT)
4265 full_uninstall = FALSE;
4269 if (!full_uninstall)
4272 MSIREG_DeleteProductKey(package->ProductCode);
4273 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4274 MSIREG_DeleteUninstallKey(package->ProductCode);
4276 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4278 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4279 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4283 MSIREG_DeleteUserProductKey(package->ProductCode);
4284 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4287 upgrade = msi_dup_property(package, szUpgradeCode);
4290 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4297 return ERROR_SUCCESS;
4300 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4304 rc = msi_unpublish_product(package);
4305 if (rc != ERROR_SUCCESS)
4308 /* turn off scheduling */
4309 package->script->CurrentlyScripting= FALSE;
4311 /* first do the same as an InstallExecute */
4312 rc = ACTION_InstallExecute(package);
4313 if (rc != ERROR_SUCCESS)
4316 /* then handle Commit Actions */
4317 rc = execute_script(package,COMMIT_SCRIPT);
4322 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4324 static const WCHAR RunOnce[] = {
4325 'S','o','f','t','w','a','r','e','\\',
4326 'M','i','c','r','o','s','o','f','t','\\',
4327 'W','i','n','d','o','w','s','\\',
4328 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4329 'R','u','n','O','n','c','e',0};
4330 static const WCHAR InstallRunOnce[] = {
4331 'S','o','f','t','w','a','r','e','\\',
4332 'M','i','c','r','o','s','o','f','t','\\',
4333 'W','i','n','d','o','w','s','\\',
4334 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4335 'I','n','s','t','a','l','l','e','r','\\',
4336 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4338 static const WCHAR msiexec_fmt[] = {
4340 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4341 '\"','%','s','\"',0};
4342 static const WCHAR install_fmt[] = {
4343 '/','I',' ','\"','%','s','\"',' ',
4344 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4345 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4346 WCHAR buffer[256], sysdir[MAX_PATH];
4348 WCHAR squished_pc[100];
4350 squash_guid(package->ProductCode,squished_pc);
4352 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4353 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4354 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4357 msi_reg_set_val_str( hkey, squished_pc, buffer );
4360 TRACE("Reboot command %s\n",debugstr_w(buffer));
4362 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4363 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4365 msi_reg_set_val_str( hkey, squished_pc, buffer );
4368 return ERROR_INSTALL_SUSPEND;
4371 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4377 * We are currently doing what should be done here in the top level Install
4378 * however for Administrative and uninstalls this step will be needed
4380 if (!package->PackagePath)
4381 return ERROR_SUCCESS;
4383 msi_set_sourcedir_props(package, TRUE);
4385 attrib = GetFileAttributesW(package->db->path);
4386 if (attrib == INVALID_FILE_ATTRIBUTES)
4392 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4393 package->Context, MSICODE_PRODUCT,
4394 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4395 if (rc == ERROR_MORE_DATA)
4397 prompt = msi_alloc(size * sizeof(WCHAR));
4398 MsiSourceListGetInfoW(package->ProductCode, NULL,
4399 package->Context, MSICODE_PRODUCT,
4400 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4403 prompt = strdupW(package->db->path);
4405 msg = generate_error_string(package,1302,1,prompt);
4406 while(attrib == INVALID_FILE_ATTRIBUTES)
4408 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4411 rc = ERROR_INSTALL_USEREXIT;
4414 attrib = GetFileAttributesW(package->db->path);
4420 return ERROR_SUCCESS;
4425 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4432 static const WCHAR szPropKeys[][80] =
4434 {'P','r','o','d','u','c','t','I','D',0},
4435 {'U','S','E','R','N','A','M','E',0},
4436 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4440 static const WCHAR szRegKeys[][80] =
4442 {'P','r','o','d','u','c','t','I','D',0},
4443 {'R','e','g','O','w','n','e','r',0},
4444 {'R','e','g','C','o','m','p','a','n','y',0},
4448 if (msi_check_unpublish(package))
4450 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4451 return ERROR_SUCCESS;
4454 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4456 return ERROR_SUCCESS;
4458 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4460 if (rc != ERROR_SUCCESS)
4463 for( i = 0; szPropKeys[i][0]; i++ )
4465 buffer = msi_dup_property( package, szPropKeys[i] );
4466 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4471 msi_free(productid);
4474 /* FIXME: call ui_actiondata */
4480 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4484 package->script->InWhatSequence |= SEQUENCE_EXEC;
4485 rc = ACTION_ProcessExecSequence(package,FALSE);
4490 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4492 MSIPACKAGE *package = param;
4493 LPCWSTR compgroupid, component, feature, qualifier, text;
4494 LPWSTR advertise = NULL, output = NULL;
4502 feature = MSI_RecordGetString(rec, 5);
4503 feat = get_loaded_feature(package, feature);
4505 return ERROR_SUCCESS;
4507 if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
4508 feat->ActionRequest != INSTALLSTATE_SOURCE &&
4509 feat->ActionRequest != INSTALLSTATE_ADVERTISED)
4511 TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
4512 feat->Action = feat->Installed;
4513 return ERROR_SUCCESS;
4516 component = MSI_RecordGetString(rec, 3);
4517 comp = get_loaded_component(package, component);
4519 return ERROR_SUCCESS;
4521 compgroupid = MSI_RecordGetString(rec,1);
4522 qualifier = MSI_RecordGetString(rec,2);
4524 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4525 if (rc != ERROR_SUCCESS)
4528 text = MSI_RecordGetString(rec,4);
4529 advertise = create_component_advertise_string(package, comp, feature);
4531 sz = strlenW(advertise);
4534 sz += lstrlenW(text);
4537 sz *= sizeof(WCHAR);
4539 output = msi_alloc_zero(sz);
4540 strcpyW(output,advertise);
4541 msi_free(advertise);
4544 strcatW(output,text);
4546 msi_reg_set_val_multi_str( hkey, qualifier, output );
4553 uirow = MSI_CreateRecord( 2 );
4554 MSI_RecordSetStringW( uirow, 1, compgroupid );
4555 MSI_RecordSetStringW( uirow, 2, qualifier);
4556 ui_actiondata( package, szPublishComponents, uirow);
4557 msiobj_release( &uirow->hdr );
4558 /* FIXME: call ui_progress? */
4564 * At present I am ignorning the advertised components part of this and only
4565 * focusing on the qualified component sets
4567 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4571 static const WCHAR ExecSeqQuery[] =
4572 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4573 '`','P','u','b','l','i','s','h',
4574 'C','o','m','p','o','n','e','n','t','`',0};
4576 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4577 if (rc != ERROR_SUCCESS)
4578 return ERROR_SUCCESS;
4580 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4581 msiobj_release(&view->hdr);
4586 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
4588 static const WCHAR szInstallerComponents[] = {
4589 'S','o','f','t','w','a','r','e','\\',
4590 'M','i','c','r','o','s','o','f','t','\\',
4591 'I','n','s','t','a','l','l','e','r','\\',
4592 'C','o','m','p','o','n','e','n','t','s','\\',0};
4594 MSIPACKAGE *package = param;
4595 LPCWSTR compgroupid, component, feature, qualifier;
4599 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
4602 feature = MSI_RecordGetString( rec, 5 );
4603 feat = get_loaded_feature( package, feature );
4605 return ERROR_SUCCESS;
4607 if (feat->ActionRequest != INSTALLSTATE_ABSENT)
4609 TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
4610 feat->Action = feat->Installed;
4611 return ERROR_SUCCESS;
4614 component = MSI_RecordGetString( rec, 3 );
4615 comp = get_loaded_component( package, component );
4617 return ERROR_SUCCESS;
4619 compgroupid = MSI_RecordGetString( rec, 1 );
4620 qualifier = MSI_RecordGetString( rec, 2 );
4622 squash_guid( compgroupid, squashed );
4623 strcpyW( keypath, szInstallerComponents );
4624 strcatW( keypath, squashed );
4626 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
4627 if (res != ERROR_SUCCESS)
4629 WARN("Unable to delete component key %d\n", res);
4632 uirow = MSI_CreateRecord( 2 );
4633 MSI_RecordSetStringW( uirow, 1, compgroupid );
4634 MSI_RecordSetStringW( uirow, 2, qualifier );
4635 ui_actiondata( package, szUnpublishComponents, uirow );
4636 msiobj_release( &uirow->hdr );
4638 return ERROR_SUCCESS;
4641 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
4645 static const WCHAR query[] =
4646 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4647 '`','P','u','b','l','i','s','h',
4648 'C','o','m','p','o','n','e','n','t','`',0};
4650 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4651 if (rc != ERROR_SUCCESS)
4652 return ERROR_SUCCESS;
4654 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
4655 msiobj_release( &view->hdr );
4660 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4662 MSIPACKAGE *package = param;
4665 SC_HANDLE hscm, service = NULL;
4666 LPCWSTR comp, depends, pass;
4667 LPWSTR name = NULL, disp = NULL;
4668 LPCWSTR load_order, serv_name, key;
4669 DWORD serv_type, start_type;
4672 static const WCHAR query[] =
4673 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4674 '`','C','o','m','p','o','n','e','n','t','`',' ',
4675 'W','H','E','R','E',' ',
4676 '`','C','o','m','p','o','n','e','n','t','`',' ',
4677 '=','\'','%','s','\'',0};
4679 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4682 ERR("Failed to open the SC Manager!\n");
4686 start_type = MSI_RecordGetInteger(rec, 5);
4687 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4690 depends = MSI_RecordGetString(rec, 8);
4691 if (depends && *depends)
4692 FIXME("Dependency list unhandled!\n");
4694 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4695 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4696 serv_type = MSI_RecordGetInteger(rec, 4);
4697 err_control = MSI_RecordGetInteger(rec, 6);
4698 load_order = MSI_RecordGetString(rec, 7);
4699 serv_name = MSI_RecordGetString(rec, 9);
4700 pass = MSI_RecordGetString(rec, 10);
4701 comp = MSI_RecordGetString(rec, 12);
4703 /* fetch the service path */
4704 row = MSI_QueryGetRecord(package->db, query, comp);
4707 ERR("Control query failed!\n");
4711 key = MSI_RecordGetString(row, 6);
4713 file = get_loaded_file(package, key);
4714 msiobj_release(&row->hdr);
4717 ERR("Failed to load the service file\n");
4721 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4722 start_type, err_control, file->TargetPath,
4723 load_order, NULL, NULL, serv_name, pass);
4726 if (GetLastError() != ERROR_SERVICE_EXISTS)
4727 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4731 CloseServiceHandle(service);
4732 CloseServiceHandle(hscm);
4736 return ERROR_SUCCESS;
4739 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4743 static const WCHAR ExecSeqQuery[] =
4744 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4745 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4747 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4748 if (rc != ERROR_SUCCESS)
4749 return ERROR_SUCCESS;
4751 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4752 msiobj_release(&view->hdr);
4757 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4758 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4760 LPCWSTR *vector, *temp_vector;
4764 static const WCHAR separator[] = {'[','~',']',0};
4767 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4772 vector = msi_alloc(sizeof(LPWSTR));
4780 vector[*numargs - 1] = p;
4782 if ((q = strstrW(p, separator)))
4786 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4792 vector = temp_vector;
4801 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4803 MSIPACKAGE *package = param;
4805 SC_HANDLE scm = NULL, service = NULL;
4806 LPCWSTR *vector = NULL;
4808 DWORD event, numargs;
4809 UINT r = ERROR_FUNCTION_FAILED;
4811 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4812 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4813 return ERROR_SUCCESS;
4815 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4816 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4817 event = MSI_RecordGetInteger(rec, 3);
4819 if (!(event & msidbServiceControlEventStart))
4825 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4828 ERR("Failed to open the service control manager\n");
4832 service = OpenServiceW(scm, name, SERVICE_START);
4835 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
4839 vector = msi_service_args_to_vector(args, &numargs);
4841 if (!StartServiceW(service, numargs, vector) &&
4842 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
4844 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
4851 CloseServiceHandle(service);
4852 CloseServiceHandle(scm);
4860 static UINT ACTION_StartServices( MSIPACKAGE *package )
4865 static const WCHAR query[] = {
4866 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4867 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4869 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4870 if (rc != ERROR_SUCCESS)
4871 return ERROR_SUCCESS;
4873 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4874 msiobj_release(&view->hdr);
4879 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4881 DWORD i, needed, count;
4882 ENUM_SERVICE_STATUSW *dependencies;
4886 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4887 0, &needed, &count))
4890 if (GetLastError() != ERROR_MORE_DATA)
4893 dependencies = msi_alloc(needed);
4897 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4898 needed, &needed, &count))
4901 for (i = 0; i < count; i++)
4903 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4904 SERVICE_STOP | SERVICE_QUERY_STATUS);
4908 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4915 msi_free(dependencies);
4919 static UINT stop_service( LPCWSTR name )
4921 SC_HANDLE scm = NULL, service = NULL;
4922 SERVICE_STATUS status;
4923 SERVICE_STATUS_PROCESS ssp;
4926 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4929 WARN("Failed to open the SCM: %d\n", GetLastError());
4933 service = OpenServiceW(scm, name,
4935 SERVICE_QUERY_STATUS |
4936 SERVICE_ENUMERATE_DEPENDENTS);
4939 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
4943 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4944 sizeof(SERVICE_STATUS_PROCESS), &needed))
4946 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
4950 if (ssp.dwCurrentState == SERVICE_STOPPED)
4953 stop_service_dependents(scm, service);
4955 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4956 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4959 CloseServiceHandle(service);
4960 CloseServiceHandle(scm);
4962 return ERROR_SUCCESS;
4965 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
4967 MSIPACKAGE *package = param;
4972 event = MSI_RecordGetInteger( rec, 3 );
4973 if (!(event & msidbServiceControlEventStop))
4974 return ERROR_SUCCESS;
4976 comp = get_loaded_component( package, MSI_RecordGetString( rec, 6 ) );
4977 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4978 return ERROR_SUCCESS;
4980 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
4981 stop_service( name );
4984 return ERROR_SUCCESS;
4987 static UINT ACTION_StopServices( MSIPACKAGE *package )
4992 static const WCHAR query[] = {
4993 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4994 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4996 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4997 if (rc != ERROR_SUCCESS)
4998 return ERROR_SUCCESS;
5000 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5001 msiobj_release(&view->hdr);
5006 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5008 MSIPACKAGE *package = param;
5012 SC_HANDLE scm = NULL, service = NULL;
5014 event = MSI_RecordGetInteger( rec, 3 );
5015 if (!(event & msidbServiceControlEventDelete))
5016 return ERROR_SUCCESS;
5018 comp = get_loaded_component( package, MSI_RecordGetString(rec, 6) );
5019 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
5020 return ERROR_SUCCESS;
5022 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5023 stop_service( name );
5025 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5028 WARN("Failed to open the SCM: %d\n", GetLastError());
5032 service = OpenServiceW( scm, name, DELETE );
5035 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5039 if (!DeleteService( service ))
5040 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5043 CloseServiceHandle( service );
5044 CloseServiceHandle( scm );
5047 return ERROR_SUCCESS;
5050 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5055 static const WCHAR query[] = {
5056 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5057 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5059 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5060 if (rc != ERROR_SUCCESS)
5061 return ERROR_SUCCESS;
5063 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5064 msiobj_release( &view->hdr );
5069 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
5073 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
5075 if (!lstrcmpW(file->File, filename))
5082 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5084 MSIPACKAGE *package = param;
5085 LPWSTR driver, driver_path, ptr;
5086 WCHAR outpath[MAX_PATH];
5087 MSIFILE *driver_file, *setup_file;
5090 UINT r = ERROR_SUCCESS;
5092 static const WCHAR driver_fmt[] = {
5093 'D','r','i','v','e','r','=','%','s',0};
5094 static const WCHAR setup_fmt[] = {
5095 'S','e','t','u','p','=','%','s',0};
5096 static const WCHAR usage_fmt[] = {
5097 'F','i','l','e','U','s','a','g','e','=','1',0};
5099 desc = MSI_RecordGetString(rec, 3);
5101 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5102 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5106 ERR("ODBC Driver entry not found!\n");
5107 return ERROR_FUNCTION_FAILED;
5110 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
5112 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5113 len += lstrlenW(usage_fmt) + 1;
5115 driver = msi_alloc(len * sizeof(WCHAR));
5117 return ERROR_OUTOFMEMORY;
5120 lstrcpyW(ptr, desc);
5121 ptr += lstrlenW(ptr) + 1;
5123 sprintfW(ptr, driver_fmt, driver_file->FileName);
5124 ptr += lstrlenW(ptr) + 1;
5128 sprintfW(ptr, setup_fmt, setup_file->FileName);
5129 ptr += lstrlenW(ptr) + 1;
5132 lstrcpyW(ptr, usage_fmt);
5133 ptr += lstrlenW(ptr) + 1;
5136 driver_path = strdupW(driver_file->TargetPath);
5137 ptr = strrchrW(driver_path, '\\');
5138 if (ptr) *ptr = '\0';
5140 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5141 NULL, ODBC_INSTALL_COMPLETE, &usage))
5143 ERR("Failed to install SQL driver!\n");
5144 r = ERROR_FUNCTION_FAILED;
5148 msi_free(driver_path);
5153 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5155 MSIPACKAGE *package = param;
5156 LPWSTR translator, translator_path, ptr;
5157 WCHAR outpath[MAX_PATH];
5158 MSIFILE *translator_file, *setup_file;
5161 UINT r = ERROR_SUCCESS;
5163 static const WCHAR translator_fmt[] = {
5164 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5165 static const WCHAR setup_fmt[] = {
5166 'S','e','t','u','p','=','%','s',0};
5168 desc = MSI_RecordGetString(rec, 3);
5170 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5171 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5173 if (!translator_file)
5175 ERR("ODBC Translator entry not found!\n");
5176 return ERROR_FUNCTION_FAILED;
5179 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 1;
5181 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5183 translator = msi_alloc(len * sizeof(WCHAR));
5185 return ERROR_OUTOFMEMORY;
5188 lstrcpyW(ptr, desc);
5189 ptr += lstrlenW(ptr) + 1;
5191 sprintfW(ptr, translator_fmt, translator_file->FileName);
5192 ptr += lstrlenW(ptr) + 1;
5196 sprintfW(ptr, setup_fmt, setup_file->FileName);
5197 ptr += lstrlenW(ptr) + 1;
5201 translator_path = strdupW(translator_file->TargetPath);
5202 ptr = strrchrW(translator_path, '\\');
5203 if (ptr) *ptr = '\0';
5205 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5206 NULL, ODBC_INSTALL_COMPLETE, &usage))
5208 ERR("Failed to install SQL translator!\n");
5209 r = ERROR_FUNCTION_FAILED;
5212 msi_free(translator);
5213 msi_free(translator_path);
5218 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5221 LPCWSTR desc, driver;
5222 WORD request = ODBC_ADD_SYS_DSN;
5225 UINT r = ERROR_SUCCESS;
5227 static const WCHAR attrs_fmt[] = {
5228 'D','S','N','=','%','s',0 };
5230 desc = MSI_RecordGetString(rec, 3);
5231 driver = MSI_RecordGetString(rec, 4);
5232 registration = MSI_RecordGetInteger(rec, 5);
5234 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5235 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5237 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5238 attrs = msi_alloc(len * sizeof(WCHAR));
5240 return ERROR_OUTOFMEMORY;
5242 len = sprintfW(attrs, attrs_fmt, desc);
5245 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5247 ERR("Failed to install SQL data source!\n");
5248 r = ERROR_FUNCTION_FAILED;
5256 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5261 static const WCHAR driver_query[] = {
5262 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5263 'O','D','B','C','D','r','i','v','e','r',0 };
5265 static const WCHAR translator_query[] = {
5266 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5267 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5269 static const WCHAR source_query[] = {
5270 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5271 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5273 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5274 if (rc != ERROR_SUCCESS)
5275 return ERROR_SUCCESS;
5277 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5278 msiobj_release(&view->hdr);
5280 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5281 if (rc != ERROR_SUCCESS)
5282 return ERROR_SUCCESS;
5284 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5285 msiobj_release(&view->hdr);
5287 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5288 if (rc != ERROR_SUCCESS)
5289 return ERROR_SUCCESS;
5291 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5292 msiobj_release(&view->hdr);
5297 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
5302 desc = MSI_RecordGetString( rec, 3 );
5303 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
5305 WARN("Failed to remove ODBC driver\n");
5309 FIXME("Usage count reached 0\n");
5312 return ERROR_SUCCESS;
5315 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
5320 desc = MSI_RecordGetString( rec, 3 );
5321 if (!SQLRemoveTranslatorW( desc, &usage ))
5323 WARN("Failed to remove ODBC translator\n");
5327 FIXME("Usage count reached 0\n");
5330 return ERROR_SUCCESS;
5333 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
5336 LPCWSTR desc, driver;
5337 WORD request = ODBC_REMOVE_SYS_DSN;
5341 static const WCHAR attrs_fmt[] = {
5342 'D','S','N','=','%','s',0 };
5344 desc = MSI_RecordGetString( rec, 3 );
5345 driver = MSI_RecordGetString( rec, 4 );
5346 registration = MSI_RecordGetInteger( rec, 5 );
5348 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
5349 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
5351 len = strlenW( attrs_fmt ) + strlenW( desc ) + 1 + 1;
5352 attrs = msi_alloc( len * sizeof(WCHAR) );
5354 return ERROR_OUTOFMEMORY;
5356 FIXME("Use ODBCSourceAttribute table\n");
5358 len = sprintfW( attrs, attrs_fmt, desc );
5361 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
5363 WARN("Failed to remove ODBC data source\n");
5367 return ERROR_SUCCESS;
5370 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5375 static const WCHAR driver_query[] = {
5376 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5377 'O','D','B','C','D','r','i','v','e','r',0 };
5379 static const WCHAR translator_query[] = {
5380 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5381 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5383 static const WCHAR source_query[] = {
5384 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5385 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5387 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
5388 if (rc != ERROR_SUCCESS)
5389 return ERROR_SUCCESS;
5391 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
5392 msiobj_release( &view->hdr );
5394 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
5395 if (rc != ERROR_SUCCESS)
5396 return ERROR_SUCCESS;
5398 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
5399 msiobj_release( &view->hdr );
5401 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
5402 if (rc != ERROR_SUCCESS)
5403 return ERROR_SUCCESS;
5405 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
5406 msiobj_release( &view->hdr );
5411 #define ENV_ACT_SETALWAYS 0x1
5412 #define ENV_ACT_SETABSENT 0x2
5413 #define ENV_ACT_REMOVE 0x4
5414 #define ENV_ACT_REMOVEMATCH 0x8
5416 #define ENV_MOD_MACHINE 0x20000000
5417 #define ENV_MOD_APPEND 0x40000000
5418 #define ENV_MOD_PREFIX 0x80000000
5419 #define ENV_MOD_MASK 0xC0000000
5421 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5423 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5425 LPCWSTR cptr = *name;
5427 static const WCHAR prefix[] = {'[','~',']',0};
5428 static const int prefix_len = 3;
5434 *flags |= ENV_ACT_SETALWAYS;
5435 else if (*cptr == '+')
5436 *flags |= ENV_ACT_SETABSENT;
5437 else if (*cptr == '-')
5438 *flags |= ENV_ACT_REMOVE;
5439 else if (*cptr == '!')
5440 *flags |= ENV_ACT_REMOVEMATCH;
5441 else if (*cptr == '*')
5442 *flags |= ENV_MOD_MACHINE;
5452 ERR("Missing environment variable\n");
5453 return ERROR_FUNCTION_FAILED;
5458 LPCWSTR ptr = *value;
5459 if (!strncmpW(ptr, prefix, prefix_len))
5461 if (ptr[prefix_len] == szSemiColon[0])
5463 *flags |= ENV_MOD_APPEND;
5464 *value += lstrlenW(prefix);
5471 else if (lstrlenW(*value) >= prefix_len)
5473 ptr += lstrlenW(ptr) - prefix_len;
5474 if (!lstrcmpW(ptr, prefix))
5476 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
5478 *flags |= ENV_MOD_PREFIX;
5479 /* the "[~]" will be removed by deformat_string */;
5489 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5490 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5491 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5492 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5494 ERR("Invalid flags: %08x\n", *flags);
5495 return ERROR_FUNCTION_FAILED;
5499 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
5501 return ERROR_SUCCESS;
5504 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5506 MSIPACKAGE *package = param;
5507 LPCWSTR name, value;
5508 LPWSTR data = NULL, newval = NULL;
5509 LPWSTR deformatted = NULL, ptr;
5510 DWORD flags, type, size;
5512 HKEY env = NULL, root;
5513 LPCWSTR environment;
5515 static const WCHAR user_env[] =
5516 {'E','n','v','i','r','o','n','m','e','n','t',0};
5517 static const WCHAR machine_env[] =
5518 {'S','y','s','t','e','m','\\',
5519 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5520 'C','o','n','t','r','o','l','\\',
5521 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5522 'E','n','v','i','r','o','n','m','e','n','t',0};
5524 name = MSI_RecordGetString(rec, 2);
5525 value = MSI_RecordGetString(rec, 3);
5527 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
5529 res = env_set_flags(&name, &value, &flags);
5530 if (res != ERROR_SUCCESS || !value)
5533 if (value && !deformat_string(package, value, &deformatted))
5535 res = ERROR_OUTOFMEMORY;
5539 value = deformatted;
5541 if (flags & ENV_MOD_MACHINE)
5543 environment = machine_env;
5544 root = HKEY_LOCAL_MACHINE;
5548 environment = user_env;
5549 root = HKEY_CURRENT_USER;
5552 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5553 KEY_ALL_ACCESS, NULL, &env, NULL);
5554 if (res != ERROR_SUCCESS)
5557 if (flags & ENV_ACT_REMOVE)
5558 FIXME("Not removing environment variable on uninstall!\n");
5562 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5563 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5564 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5567 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
5569 /* Nothing to do. */
5572 res = ERROR_SUCCESS;
5576 /* If we are appending but the string was empty, strip ; */
5577 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
5579 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5580 newval = strdupW(value);
5583 res = ERROR_OUTOFMEMORY;
5589 /* Contrary to MSDN, +-variable to [~];path works */
5590 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
5592 res = ERROR_SUCCESS;
5596 data = msi_alloc(size);
5600 return ERROR_OUTOFMEMORY;
5603 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5604 if (res != ERROR_SUCCESS)
5607 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5609 res = RegDeleteKeyW(env, name);
5613 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
5614 if (flags & ENV_MOD_MASK)
5618 if (flags & ENV_MOD_APPEND) multiplier++;
5619 if (flags & ENV_MOD_PREFIX) multiplier++;
5620 mod_size = lstrlenW(value) * multiplier;
5621 size += mod_size * sizeof(WCHAR);
5624 newval = msi_alloc(size);
5628 res = ERROR_OUTOFMEMORY;
5632 if (flags & ENV_MOD_PREFIX)
5634 lstrcpyW(newval, value);
5635 ptr = newval + lstrlenW(value);
5638 lstrcpyW(ptr, data);
5640 if (flags & ENV_MOD_APPEND)
5642 lstrcatW(newval, value);
5645 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5646 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5649 if (env) RegCloseKey(env);
5650 msi_free(deformatted);
5656 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5660 static const WCHAR ExecSeqQuery[] =
5661 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5662 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5663 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5664 if (rc != ERROR_SUCCESS)
5665 return ERROR_SUCCESS;
5667 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5668 msiobj_release(&view->hdr);
5673 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5684 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5688 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5689 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5691 WARN("Source or dest is directory, not moving\n");
5695 if (options == msidbMoveFileOptionsMove)
5697 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5698 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5701 WARN("MoveFile failed: %d\n", GetLastError());
5707 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5708 ret = CopyFileW(source, dest, FALSE);
5711 WARN("CopyFile failed: %d\n", GetLastError());
5719 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5722 DWORD dirlen, pathlen;
5724 ptr = strrchrW(wildcard, '\\');
5725 dirlen = ptr - wildcard + 1;
5727 pathlen = dirlen + lstrlenW(filename) + 1;
5728 path = msi_alloc(pathlen * sizeof(WCHAR));
5730 lstrcpynW(path, wildcard, dirlen + 1);
5731 lstrcatW(path, filename);
5736 static void free_file_entry(FILE_LIST *file)
5738 msi_free(file->source);
5739 msi_free(file->dest);
5743 static void free_list(FILE_LIST *list)
5745 while (!list_empty(&list->entry))
5747 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5749 list_remove(&file->entry);
5750 free_file_entry(file);
5754 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5756 FILE_LIST *new, *file;
5757 LPWSTR ptr, filename;
5760 new = msi_alloc_zero(sizeof(FILE_LIST));
5764 new->source = strdupW(source);
5765 ptr = strrchrW(dest, '\\') + 1;
5766 filename = strrchrW(new->source, '\\') + 1;
5768 new->sourcename = filename;
5771 new->destname = ptr;
5773 new->destname = new->sourcename;
5775 size = (ptr - dest) + lstrlenW(filename) + 1;
5776 new->dest = msi_alloc(size * sizeof(WCHAR));
5779 free_file_entry(new);
5783 lstrcpynW(new->dest, dest, ptr - dest + 1);
5784 lstrcatW(new->dest, filename);
5786 if (list_empty(&files->entry))
5788 list_add_head(&files->entry, &new->entry);
5792 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5794 if (lstrcmpW(source, file->source) < 0)
5796 list_add_before(&file->entry, &new->entry);
5801 list_add_after(&file->entry, &new->entry);
5805 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5807 WIN32_FIND_DATAW wfd;
5811 FILE_LIST files, *file;
5814 hfile = FindFirstFileW(source, &wfd);
5815 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5817 list_init(&files.entry);
5819 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5821 if (is_dot_dir(wfd.cFileName)) continue;
5823 path = wildcard_to_file(source, wfd.cFileName);
5830 add_wildcard(&files, path, dest);
5834 /* no files match the wildcard */
5835 if (list_empty(&files.entry))
5838 /* only the first wildcard match gets renamed to dest */
5839 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5840 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5841 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5848 /* file->dest may be shorter after the reallocation, so add a NULL
5849 * terminator. This is needed for the call to strrchrW, as there will no
5850 * longer be a NULL terminator within the bounds of the allocation in this case.
5852 file->dest[size - 1] = '\0';
5853 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5855 while (!list_empty(&files.entry))
5857 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5859 msi_move_file(file->source, file->dest, options);
5861 list_remove(&file->entry);
5862 free_file_entry(file);
5873 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5875 MSIPACKAGE *package = param;
5878 LPWSTR destname = NULL;
5879 LPWSTR sourcedir = NULL, destdir = NULL;
5880 LPWSTR source = NULL, dest = NULL;
5883 BOOL ret, wildcards;
5885 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5886 if (!comp || !comp->Enabled ||
5887 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5889 TRACE("Component not set for install, not moving file\n");
5890 return ERROR_SUCCESS;
5893 sourcename = MSI_RecordGetString(rec, 3);
5894 options = MSI_RecordGetInteger(rec, 7);
5896 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5900 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5906 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5909 source = strdupW(sourcedir);
5915 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5916 source = msi_alloc(size * sizeof(WCHAR));
5920 lstrcpyW(source, sourcedir);
5921 if (source[lstrlenW(source) - 1] != '\\')
5922 lstrcatW(source, szBackSlash);
5923 lstrcatW(source, sourcename);
5926 wildcards = strchrW(source, '*') || strchrW(source, '?');
5928 if (MSI_RecordIsNull(rec, 4))
5932 destname = strdupW(sourcename);
5939 destname = strdupW(MSI_RecordGetString(rec, 4));
5941 reduce_to_longfilename(destname);
5946 size = lstrlenW(destname);
5948 size += lstrlenW(destdir) + 2;
5949 dest = msi_alloc(size * sizeof(WCHAR));
5953 lstrcpyW(dest, destdir);
5954 if (dest[lstrlenW(dest) - 1] != '\\')
5955 lstrcatW(dest, szBackSlash);
5958 lstrcatW(dest, destname);
5960 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5962 ret = CreateDirectoryW(destdir, NULL);
5965 WARN("CreateDirectory failed: %d\n", GetLastError());
5966 return ERROR_SUCCESS;
5971 msi_move_file(source, dest, options);
5973 move_files_wildcard(source, dest, options);
5976 msi_free(sourcedir);
5982 return ERROR_SUCCESS;
5985 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5990 static const WCHAR ExecSeqQuery[] =
5991 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5992 '`','M','o','v','e','F','i','l','e','`',0};
5994 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5995 if (rc != ERROR_SUCCESS)
5996 return ERROR_SUCCESS;
5998 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5999 msiobj_release(&view->hdr);
6004 typedef struct tagMSIASSEMBLY
6007 MSICOMPONENT *component;
6008 MSIFEATURE *feature;
6016 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
6018 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
6019 LPVOID pvReserved, HMODULE *phModDll);
6021 static BOOL init_functionpointers(void)
6027 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
6029 hmscoree = LoadLibraryA("mscoree.dll");
6032 WARN("mscoree.dll not available\n");
6036 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
6037 if (!pLoadLibraryShim)
6039 WARN("LoadLibraryShim not available\n");
6040 FreeLibrary(hmscoree);
6044 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
6047 WARN("fusion.dll not available\n");
6048 FreeLibrary(hmscoree);
6052 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
6054 FreeLibrary(hmscoree);
6058 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
6061 IAssemblyCache *cache;
6063 UINT r = ERROR_FUNCTION_FAILED;
6065 TRACE("installing assembly: %s\n", debugstr_w(path));
6067 if (assembly->feature)
6068 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
6070 if (assembly->manifest)
6071 FIXME("Manifest unhandled\n");
6073 if (assembly->application)
6075 FIXME("Assembly should be privately installed\n");
6076 return ERROR_SUCCESS;
6079 if (assembly->attributes == msidbAssemblyAttributesWin32)
6081 FIXME("Win32 assemblies not handled\n");
6082 return ERROR_SUCCESS;
6085 hr = pCreateAssemblyCache(&cache, 0);
6089 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
6091 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
6096 IAssemblyCache_Release(cache);
6100 typedef struct tagASSEMBLY_LIST
6102 MSIPACKAGE *package;
6103 IAssemblyCache *cache;
6104 struct list *assemblies;
6107 typedef struct tagASSEMBLY_NAME
6115 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
6117 ASSEMBLY_NAME *asmname = param;
6118 LPCWSTR name = MSI_RecordGetString(rec, 2);
6119 LPWSTR val = msi_dup_record_field(rec, 3);
6121 static const WCHAR Name[] = {'N','a','m','e',0};
6122 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
6123 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
6124 static const WCHAR PublicKeyToken[] = {
6125 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
6127 if (!strcmpiW(name, Name))
6128 asmname->name = val;
6129 else if (!strcmpiW(name, Version))
6130 asmname->version = val;
6131 else if (!strcmpiW(name, Culture))
6132 asmname->culture = val;
6133 else if (!strcmpiW(name, PublicKeyToken))
6134 asmname->pubkeytoken = val;
6138 return ERROR_SUCCESS;
6141 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
6145 *size = lstrlenW(append) + 1;
6146 *str = msi_alloc((*size) * sizeof(WCHAR));
6147 lstrcpyW(*str, append);
6151 (*size) += lstrlenW(append);
6152 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
6153 lstrcatW(*str, append);
6156 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
6159 ASSEMBLY_INFO asminfo;
6167 static const WCHAR separator[] = {',',' ',0};
6168 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
6169 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
6170 static const WCHAR PublicKeyToken[] = {
6171 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
6172 static const WCHAR query[] = {
6173 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6174 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
6175 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
6176 '=','\'','%','s','\'',0};
6180 ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
6181 ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
6183 r = MSI_OpenQuery(db, &view, query, comp->Component);
6184 if (r != ERROR_SUCCESS)
6185 return ERROR_SUCCESS;
6187 MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
6188 msiobj_release(&view->hdr);
6192 ERR("No assembly name specified!\n");
6196 append_str(&disp, &size, name.name);
6200 append_str(&disp, &size, separator);
6201 append_str(&disp, &size, Version);
6202 append_str(&disp, &size, name.version);
6207 append_str(&disp, &size, separator);
6208 append_str(&disp, &size, Culture);
6209 append_str(&disp, &size, name.culture);
6212 if (name.pubkeytoken)
6214 append_str(&disp, &size, separator);
6215 append_str(&disp, &size, PublicKeyToken);
6216 append_str(&disp, &size, name.pubkeytoken);
6219 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6220 IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
6222 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6226 msi_free(name.name);
6227 msi_free(name.version);
6228 msi_free(name.culture);
6229 msi_free(name.pubkeytoken);
6234 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6236 ASSEMBLY_LIST *list = param;
6237 MSIASSEMBLY *assembly;
6239 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6241 return ERROR_OUTOFMEMORY;
6243 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
6245 if (!assembly->component || !assembly->component->Enabled ||
6246 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
6248 TRACE("Component not set for install, not publishing assembly\n");
6250 return ERROR_SUCCESS;
6253 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6254 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6256 if (!assembly->file)
6258 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6259 return ERROR_FUNCTION_FAILED;
6262 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6263 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6264 assembly->attributes = MSI_RecordGetInteger(rec, 5);
6266 if (assembly->application)
6269 DWORD size = sizeof(version)/sizeof(WCHAR);
6271 /* FIXME: we should probably check the manifest file here */
6273 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6274 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6276 assembly->installed = TRUE;
6280 assembly->installed = check_assembly_installed(list->package->db,
6282 assembly->component);
6284 list_add_head(list->assemblies, &assembly->entry);
6285 return ERROR_SUCCESS;
6288 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6290 IAssemblyCache *cache = NULL;
6296 static const WCHAR query[] =
6297 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6298 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6300 r = MSI_DatabaseOpenViewW(package->db, query, &view);
6301 if (r != ERROR_SUCCESS)
6302 return ERROR_SUCCESS;
6304 hr = pCreateAssemblyCache(&cache, 0);
6306 return ERROR_FUNCTION_FAILED;
6308 list.package = package;
6310 list.assemblies = assemblies;
6312 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6313 msiobj_release(&view->hdr);
6315 IAssemblyCache_Release(cache);
6320 static void free_assemblies(struct list *assemblies)
6322 struct list *item, *cursor;
6324 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6326 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6328 list_remove(&assembly->entry);
6329 msi_free(assembly->application);
6330 msi_free(assembly->manifest);
6335 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6337 MSIASSEMBLY *assembly;
6339 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6341 if (!lstrcmpW(assembly->file->File, file))
6351 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6352 LPWSTR *path, DWORD *attrs, PVOID user)
6354 MSIASSEMBLY *assembly;
6355 WCHAR temppath[MAX_PATH];
6356 struct list *assemblies = user;
6359 if (!find_assembly(assemblies, file, &assembly))
6362 GetTempPathW(MAX_PATH, temppath);
6363 PathAddBackslashW(temppath);
6364 lstrcatW(temppath, assembly->file->FileName);
6366 if (action == MSICABEXTRACT_BEGINEXTRACT)
6368 if (assembly->installed)
6371 *path = strdupW(temppath);
6372 *attrs = assembly->file->Attributes;
6374 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6376 assembly->installed = TRUE;
6378 r = install_assembly(package, assembly, temppath);
6379 if (r != ERROR_SUCCESS)
6380 ERR("Failed to install assembly\n");
6386 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6389 struct list assemblies = LIST_INIT(assemblies);
6390 MSIASSEMBLY *assembly;
6393 if (!init_functionpointers() || !pCreateAssemblyCache)
6394 return ERROR_FUNCTION_FAILED;
6396 r = load_assemblies(package, &assemblies);
6397 if (r != ERROR_SUCCESS)
6400 if (list_empty(&assemblies))
6403 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6406 r = ERROR_OUTOFMEMORY;
6410 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6412 if (assembly->installed && !mi->is_continuous)
6415 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6416 (assembly->file->IsCompressed && !mi->is_extracted))
6420 r = ready_media(package, assembly->file, mi);
6421 if (r != ERROR_SUCCESS)
6423 ERR("Failed to ready media\n");
6428 data.package = package;
6429 data.cb = installassembly_cb;
6430 data.user = &assemblies;
6432 if (assembly->file->IsCompressed &&
6433 !msi_cabextract(package, mi, &data))
6435 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6436 r = ERROR_FUNCTION_FAILED;
6441 if (!assembly->file->IsCompressed)
6443 LPWSTR source = resolve_file_source(package, assembly->file);
6445 r = install_assembly(package, assembly, source);
6446 if (r != ERROR_SUCCESS)
6447 ERR("Failed to install assembly\n");
6452 /* FIXME: write Installer assembly reg values */
6456 free_assemblies(&assemblies);
6460 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6462 LPWSTR key, template, id;
6463 UINT r = ERROR_SUCCESS;
6465 id = msi_dup_property( package, szProductID );
6469 return ERROR_SUCCESS;
6471 template = msi_dup_property( package, szPIDTemplate );
6472 key = msi_dup_property( package, szPIDKEY );
6474 if (key && template)
6476 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6477 r = MSI_SetPropertyW( package, szProductID, key );
6479 msi_free( template );
6484 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6487 package->need_reboot = 1;
6488 return ERROR_SUCCESS;
6491 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6493 TRACE("%p\n", package);
6494 return ERROR_SUCCESS;
6497 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6499 FIXME("%p\n", package);
6500 return ERROR_SUCCESS;
6503 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6505 FIXME("%p\n", package);
6506 return ERROR_SUCCESS;
6509 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6510 LPCSTR action, LPCWSTR table )
6512 static const WCHAR query[] = {
6513 'S','E','L','E','C','T',' ','*',' ',
6514 'F','R','O','M',' ','`','%','s','`',0 };
6515 MSIQUERY *view = NULL;
6519 r = MSI_OpenQuery( package->db, &view, query, table );
6520 if (r == ERROR_SUCCESS)
6522 r = MSI_IterateRecords(view, &count, NULL, package);
6523 msiobj_release(&view->hdr);
6527 FIXME("%s -> %u ignored %s table values\n",
6528 action, count, debugstr_w(table));
6530 return ERROR_SUCCESS;
6533 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6535 static const WCHAR table[] =
6536 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6537 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6540 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6542 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6543 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6546 static UINT ACTION_BindImage( MSIPACKAGE *package )
6548 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6549 return msi_unimplemented_action_stub( package, "BindImage", table );
6552 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6554 static const WCHAR table[] = {
6555 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6556 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6559 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6561 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6562 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6565 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6567 static const WCHAR table[] = {
6568 'E','n','v','i','r','o','n','m','e','n','t',0 };
6569 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6572 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6574 static const WCHAR table[] = {
6575 'M','s','i','A','s','s','e','m','b','l','y',0 };
6576 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6579 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6581 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6582 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6585 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6587 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6588 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6591 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6593 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6594 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6597 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6599 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6600 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6603 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6605 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6606 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6609 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6611 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6612 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6615 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6617 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6618 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6621 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6623 static const WCHAR table[] = { 'D','i','r','e','c','t','o','r','y',0 };
6624 return msi_unimplemented_action_stub( package, "SetODBCFolders", table );
6627 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6629 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6630 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6633 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6635 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6636 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6639 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6641 static const WCHAR table[] = { 'M','I','M','E',0 };
6642 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6645 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6647 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6648 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6651 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
6655 const WCHAR *action;
6656 UINT (*handler)(MSIPACKAGE *);
6660 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6661 { szAppSearch, ACTION_AppSearch },
6662 { szBindImage, ACTION_BindImage },
6663 { szCCPSearch, ACTION_CCPSearch },
6664 { szCostFinalize, ACTION_CostFinalize },
6665 { szCostInitialize, ACTION_CostInitialize },
6666 { szCreateFolders, ACTION_CreateFolders },
6667 { szCreateShortcuts, ACTION_CreateShortcuts },
6668 { szDeleteServices, ACTION_DeleteServices },
6669 { szDisableRollback, ACTION_DisableRollback },
6670 { szDuplicateFiles, ACTION_DuplicateFiles },
6671 { szExecuteAction, ACTION_ExecuteAction },
6672 { szFileCost, ACTION_FileCost },
6673 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6674 { szForceReboot, ACTION_ForceReboot },
6675 { szInstallAdminPackage, ACTION_InstallAdminPackage },
6676 { szInstallExecute, ACTION_InstallExecute },
6677 { szInstallExecuteAgain, ACTION_InstallExecute },
6678 { szInstallFiles, ACTION_InstallFiles},
6679 { szInstallFinalize, ACTION_InstallFinalize },
6680 { szInstallInitialize, ACTION_InstallInitialize },
6681 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6682 { szInstallValidate, ACTION_InstallValidate },
6683 { szIsolateComponents, ACTION_IsolateComponents },
6684 { szLaunchConditions, ACTION_LaunchConditions },
6685 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6686 { szMoveFiles, ACTION_MoveFiles },
6687 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6688 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6689 { szInstallODBC, ACTION_InstallODBC },
6690 { szInstallServices, ACTION_InstallServices },
6691 { szPatchFiles, ACTION_PatchFiles },
6692 { szProcessComponents, ACTION_ProcessComponents },
6693 { szPublishComponents, ACTION_PublishComponents },
6694 { szPublishFeatures, ACTION_PublishFeatures },
6695 { szPublishProduct, ACTION_PublishProduct },
6696 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6697 { szRegisterComPlus, ACTION_RegisterComPlus},
6698 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6699 { szRegisterFonts, ACTION_RegisterFonts },
6700 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6701 { szRegisterProduct, ACTION_RegisterProduct },
6702 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6703 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6704 { szRegisterUser, ACTION_RegisterUser },
6705 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6706 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6707 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6708 { szRemoveFiles, ACTION_RemoveFiles },
6709 { szRemoveFolders, ACTION_RemoveFolders },
6710 { szRemoveIniValues, ACTION_RemoveIniValues },
6711 { szRemoveODBC, ACTION_RemoveODBC },
6712 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6713 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6714 { szResolveSource, ACTION_ResolveSource },
6715 { szRMCCPSearch, ACTION_RMCCPSearch },
6716 { szScheduleReboot, ACTION_ScheduleReboot },
6717 { szSelfRegModules, ACTION_SelfRegModules },
6718 { szSelfUnregModules, ACTION_SelfUnregModules },
6719 { szSetODBCFolders, ACTION_SetODBCFolders },
6720 { szStartServices, ACTION_StartServices },
6721 { szStopServices, ACTION_StopServices },
6722 { szUnpublishComponents, ACTION_UnpublishComponents },
6723 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6724 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6725 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6726 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6727 { szUnregisterFonts, ACTION_UnregisterFonts },
6728 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6729 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6730 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6731 { szValidateProductID, ACTION_ValidateProductID },
6732 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6733 { szWriteIniValues, ACTION_WriteIniValues },
6734 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6738 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6739 UINT* rc, BOOL force )
6745 if (!run && !package->script->CurrentlyScripting)
6750 if (strcmpW(action,szInstallFinalize) == 0 ||
6751 strcmpW(action,szInstallExecute) == 0 ||
6752 strcmpW(action,szInstallExecuteAgain) == 0)
6757 while (StandardActions[i].action != NULL)
6759 if (strcmpW(StandardActions[i].action, action)==0)
6763 ui_actioninfo(package, action, TRUE, 0);
6764 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6765 ui_actioninfo(package, action, FALSE, *rc);
6769 ui_actionstart(package, action);
6770 if (StandardActions[i].handler)
6772 *rc = StandardActions[i].handler(package);
6776 FIXME("unhandled standard action %s\n",debugstr_w(action));
6777 *rc = ERROR_SUCCESS;
6788 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
6790 UINT rc = ERROR_SUCCESS;
6793 TRACE("Performing action (%s)\n", debugstr_w(action));
6795 handled = ACTION_HandleStandardAction(package, action, &rc, force);
6798 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
6802 WARN("unhandled msi action %s\n", debugstr_w(action));
6803 rc = ERROR_FUNCTION_NOT_CALLED;
6809 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
6811 UINT rc = ERROR_SUCCESS;
6812 BOOL handled = FALSE;
6814 TRACE("Performing action (%s)\n", debugstr_w(action));
6816 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
6819 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
6821 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
6826 WARN("unhandled msi action %s\n", debugstr_w(action));
6827 rc = ERROR_FUNCTION_NOT_CALLED;
6833 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
6835 UINT rc = ERROR_SUCCESS;
6838 static const WCHAR ExecSeqQuery[] =
6839 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6840 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
6841 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
6842 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
6843 static const WCHAR UISeqQuery[] =
6844 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6845 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
6846 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
6847 ' ', '=',' ','%','i',0};
6849 if (needs_ui_sequence(package))
6850 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
6852 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
6856 LPCWSTR action, cond;
6858 TRACE("Running the actions\n");
6860 /* check conditions */
6861 cond = MSI_RecordGetString(row, 2);
6863 /* this is a hack to skip errors in the condition code */
6864 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
6866 msiobj_release(&row->hdr);
6867 return ERROR_SUCCESS;
6870 action = MSI_RecordGetString(row, 1);
6873 ERR("failed to fetch action\n");
6874 msiobj_release(&row->hdr);
6875 return ERROR_FUNCTION_FAILED;
6878 if (needs_ui_sequence(package))
6879 rc = ACTION_PerformUIAction(package, action, -1);
6881 rc = ACTION_PerformAction(package, action, -1, FALSE);
6883 msiobj_release(&row->hdr);
6889 /****************************************************
6890 * TOP level entry points
6891 *****************************************************/
6893 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
6894 LPCWSTR szCommandLine )
6899 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
6900 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
6902 MSI_SetPropertyW(package, szAction, szInstall);
6904 package->script->InWhatSequence = SEQUENCE_INSTALL;
6911 dir = strdupW(szPackagePath);
6912 p = strrchrW(dir, '\\');
6916 file = szPackagePath + (p - dir);
6921 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
6922 GetCurrentDirectoryW(MAX_PATH, dir);
6923 lstrcatW(dir, szBackSlash);
6924 file = szPackagePath;
6927 msi_free( package->PackagePath );
6928 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
6929 if (!package->PackagePath)
6932 return ERROR_OUTOFMEMORY;
6935 lstrcpyW(package->PackagePath, dir);
6936 lstrcatW(package->PackagePath, file);
6939 msi_set_sourcedir_props(package, FALSE);
6942 msi_parse_command_line( package, szCommandLine, FALSE );
6944 msi_apply_transforms( package );
6945 msi_apply_patches( package );
6947 if (!szCommandLine && msi_get_property_int( package, szInstalled, 0 ))
6949 TRACE("setting reinstall property\n");
6950 MSI_SetPropertyW( package, szReinstall, szAll );
6953 /* properties may have been added by a transform */
6954 msi_clone_properties( package );
6955 msi_set_context( package );
6957 if (needs_ui_sequence( package))
6959 package->script->InWhatSequence |= SEQUENCE_UI;
6960 rc = ACTION_ProcessUISequence(package);
6961 ui_exists = ui_sequence_exists(package);
6962 if (rc == ERROR_SUCCESS || !ui_exists)
6964 package->script->InWhatSequence |= SEQUENCE_EXEC;
6965 rc = ACTION_ProcessExecSequence(package, ui_exists);
6969 rc = ACTION_ProcessExecSequence(package, FALSE);
6971 package->script->CurrentlyScripting = FALSE;
6973 /* process the ending type action */
6974 if (rc == ERROR_SUCCESS)
6975 ACTION_PerformActionSequence(package, -1);
6976 else if (rc == ERROR_INSTALL_USEREXIT)
6977 ACTION_PerformActionSequence(package, -2);
6978 else if (rc == ERROR_INSTALL_SUSPEND)
6979 ACTION_PerformActionSequence(package, -4);
6981 ACTION_PerformActionSequence(package, -3);
6983 /* finish up running custom actions */
6984 ACTION_FinishCustomActions(package);
6986 if (rc == ERROR_SUCCESS && package->need_reboot)
6987 return ERROR_SUCCESS_REBOOT_REQUIRED;