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 MSI_SetPropertyW(package, szAllUsers, szOne);
720 return ERROR_SUCCESS;
723 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
726 LPCWSTR cond, action;
727 MSIPACKAGE *package = param;
729 action = MSI_RecordGetString(row,1);
732 ERR("Error is retrieving action name\n");
733 return ERROR_FUNCTION_FAILED;
736 /* check conditions */
737 cond = MSI_RecordGetString(row,2);
739 /* this is a hack to skip errors in the condition code */
740 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
742 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
743 return ERROR_SUCCESS;
746 if (needs_ui_sequence(package))
747 rc = ACTION_PerformUIAction(package, action, -1);
749 rc = ACTION_PerformAction(package, action, -1, FALSE);
751 msi_dialog_check_messages( NULL );
753 if (package->CurrentInstallState != ERROR_SUCCESS)
754 rc = package->CurrentInstallState;
756 if (rc == ERROR_FUNCTION_NOT_CALLED)
759 if (rc != ERROR_SUCCESS)
760 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
765 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
769 static const WCHAR query[] =
770 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
772 ' ','W','H','E','R','E',' ',
773 '`','S','e','q','u','e','n','c','e','`',' ',
774 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
775 '`','S','e','q','u','e','n','c','e','`',0};
777 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
779 r = MSI_OpenQuery( package->db, &view, query, szTable );
780 if (r == ERROR_SUCCESS)
782 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
783 msiobj_release(&view->hdr);
789 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
793 static const WCHAR ExecSeqQuery[] =
794 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
795 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
796 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
797 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
798 'O','R','D','E','R',' ', 'B','Y',' ',
799 '`','S','e','q','u','e','n','c','e','`',0 };
800 static const WCHAR IVQuery[] =
801 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
802 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
803 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
804 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
805 ' ','\'', 'I','n','s','t','a','l','l',
806 'V','a','l','i','d','a','t','e','\'', 0};
809 if (package->script->ExecuteSequenceRun)
811 TRACE("Execute Sequence already Run\n");
812 return ERROR_SUCCESS;
815 package->script->ExecuteSequenceRun = TRUE;
817 /* get the sequence number */
820 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
822 return ERROR_FUNCTION_FAILED;
823 seq = MSI_RecordGetInteger(row,1);
824 msiobj_release(&row->hdr);
827 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
828 if (rc == ERROR_SUCCESS)
830 TRACE("Running the actions\n");
832 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
833 msiobj_release(&view->hdr);
839 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
843 static const WCHAR ExecSeqQuery [] =
844 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
845 '`','I','n','s','t','a','l','l',
846 'U','I','S','e','q','u','e','n','c','e','`',
847 ' ','W','H','E','R','E',' ',
848 '`','S','e','q','u','e','n','c','e','`',' ',
849 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
850 '`','S','e','q','u','e','n','c','e','`',0};
852 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
853 if (rc == ERROR_SUCCESS)
855 TRACE("Running the actions\n");
857 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
858 msiobj_release(&view->hdr);
864 /********************************************************
865 * ACTION helper functions and functions that perform the actions
866 *******************************************************/
867 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
868 UINT* rc, UINT script, BOOL force )
873 arc = ACTION_CustomAction(package, action, script, force);
875 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
884 * Actual Action Handlers
887 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
889 MSIPACKAGE *package = param;
895 dir = MSI_RecordGetString(row,1);
898 ERR("Unable to get folder id\n");
899 return ERROR_SUCCESS;
902 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
905 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
906 return ERROR_SUCCESS;
909 TRACE("Folder is %s\n",debugstr_w(full_path));
912 uirow = MSI_CreateRecord(1);
913 MSI_RecordSetStringW(uirow,1,full_path);
914 ui_actiondata(package,szCreateFolders,uirow);
915 msiobj_release( &uirow->hdr );
917 if (folder->State == 0)
918 create_full_pathW(full_path);
923 return ERROR_SUCCESS;
926 /* FIXME: probably should merge this with the above function */
927 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
929 UINT rc = ERROR_SUCCESS;
933 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
935 return ERROR_FUNCTION_FAILED;
937 /* create the path */
938 if (folder->State == 0)
940 create_full_pathW(install_path);
943 msi_free(install_path);
948 UINT msi_create_component_directories( MSIPACKAGE *package )
952 /* create all the folders required by the components are going to install */
953 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
955 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
957 msi_create_directory( package, comp->Directory );
960 return ERROR_SUCCESS;
964 * Also we cannot enable/disable components either, so for now I am just going
965 * to do all the directories for all the components.
967 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
969 static const WCHAR ExecSeqQuery[] =
970 {'S','E','L','E','C','T',' ',
971 '`','D','i','r','e','c','t','o','r','y','_','`',
972 ' ','F','R','O','M',' ',
973 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
977 /* create all the empty folders specified in the CreateFolder table */
978 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
979 if (rc != ERROR_SUCCESS)
980 return ERROR_SUCCESS;
982 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
983 msiobj_release(&view->hdr);
985 msi_create_component_directories( package );
990 static UINT load_component( MSIRECORD *row, LPVOID param )
992 MSIPACKAGE *package = param;
995 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
997 return ERROR_FUNCTION_FAILED;
999 list_add_tail( &package->components, &comp->entry );
1001 /* fill in the data */
1002 comp->Component = msi_dup_record_field( row, 1 );
1004 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1006 comp->ComponentId = msi_dup_record_field( row, 2 );
1007 comp->Directory = msi_dup_record_field( row, 3 );
1008 comp->Attributes = MSI_RecordGetInteger(row,4);
1009 comp->Condition = msi_dup_record_field( row, 5 );
1010 comp->KeyPath = msi_dup_record_field( row, 6 );
1012 comp->Installed = INSTALLSTATE_UNKNOWN;
1013 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1015 return ERROR_SUCCESS;
1018 static UINT load_all_components( MSIPACKAGE *package )
1020 static const WCHAR query[] = {
1021 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1022 '`','C','o','m','p','o','n','e','n','t','`',0 };
1026 if (!list_empty(&package->components))
1027 return ERROR_SUCCESS;
1029 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1030 if (r != ERROR_SUCCESS)
1033 r = MSI_IterateRecords(view, NULL, load_component, package);
1034 msiobj_release(&view->hdr);
1039 MSIPACKAGE *package;
1040 MSIFEATURE *feature;
1043 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1047 cl = msi_alloc( sizeof (*cl) );
1049 return ERROR_NOT_ENOUGH_MEMORY;
1050 cl->component = comp;
1051 list_add_tail( &feature->Components, &cl->entry );
1053 return ERROR_SUCCESS;
1056 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1060 fl = msi_alloc( sizeof(*fl) );
1062 return ERROR_NOT_ENOUGH_MEMORY;
1063 fl->feature = child;
1064 list_add_tail( &parent->Children, &fl->entry );
1066 return ERROR_SUCCESS;
1069 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1071 _ilfs* ilfs = param;
1075 component = MSI_RecordGetString(row,1);
1077 /* check to see if the component is already loaded */
1078 comp = get_loaded_component( ilfs->package, component );
1081 ERR("unknown component %s\n", debugstr_w(component));
1082 return ERROR_FUNCTION_FAILED;
1085 add_feature_component( ilfs->feature, comp );
1086 comp->Enabled = TRUE;
1088 return ERROR_SUCCESS;
1091 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1093 MSIFEATURE *feature;
1098 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1100 if ( !lstrcmpW( feature->Feature, name ) )
1107 static UINT load_feature(MSIRECORD * row, LPVOID param)
1109 MSIPACKAGE* package = param;
1110 MSIFEATURE* feature;
1111 static const WCHAR Query1[] =
1112 {'S','E','L','E','C','T',' ',
1113 '`','C','o','m','p','o','n','e','n','t','_','`',
1114 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1115 'C','o','m','p','o','n','e','n','t','s','`',' ',
1116 'W','H','E','R','E',' ',
1117 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1122 /* fill in the data */
1124 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1126 return ERROR_NOT_ENOUGH_MEMORY;
1128 list_init( &feature->Children );
1129 list_init( &feature->Components );
1131 feature->Feature = msi_dup_record_field( row, 1 );
1133 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1135 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1136 feature->Title = msi_dup_record_field( row, 3 );
1137 feature->Description = msi_dup_record_field( row, 4 );
1139 if (!MSI_RecordIsNull(row,5))
1140 feature->Display = MSI_RecordGetInteger(row,5);
1142 feature->Level= MSI_RecordGetInteger(row,6);
1143 feature->Directory = msi_dup_record_field( row, 7 );
1144 feature->Attributes = MSI_RecordGetInteger(row,8);
1146 feature->Installed = INSTALLSTATE_UNKNOWN;
1147 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1149 list_add_tail( &package->features, &feature->entry );
1151 /* load feature components */
1153 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1154 if (rc != ERROR_SUCCESS)
1155 return ERROR_SUCCESS;
1157 ilfs.package = package;
1158 ilfs.feature = feature;
1160 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1161 msiobj_release(&view->hdr);
1163 return ERROR_SUCCESS;
1166 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1168 MSIPACKAGE* package = param;
1169 MSIFEATURE *parent, *child;
1171 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1173 return ERROR_FUNCTION_FAILED;
1175 if (!child->Feature_Parent)
1176 return ERROR_SUCCESS;
1178 parent = find_feature_by_name( package, child->Feature_Parent );
1180 return ERROR_FUNCTION_FAILED;
1182 add_feature_child( parent, child );
1183 return ERROR_SUCCESS;
1186 static UINT load_all_features( MSIPACKAGE *package )
1188 static const WCHAR query[] = {
1189 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1190 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1191 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1195 if (!list_empty(&package->features))
1196 return ERROR_SUCCESS;
1198 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1199 if (r != ERROR_SUCCESS)
1202 r = MSI_IterateRecords( view, NULL, load_feature, package );
1203 if (r != ERROR_SUCCESS)
1206 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1207 msiobj_release( &view->hdr );
1212 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1223 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1225 static const WCHAR query[] = {
1226 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1227 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1228 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1229 MSIQUERY *view = NULL;
1230 MSIRECORD *row = NULL;
1233 TRACE("%s\n", debugstr_w(file->File));
1235 r = MSI_OpenQuery(package->db, &view, query, file->File);
1236 if (r != ERROR_SUCCESS)
1239 r = MSI_ViewExecute(view, NULL);
1240 if (r != ERROR_SUCCESS)
1243 r = MSI_ViewFetch(view, &row);
1244 if (r != ERROR_SUCCESS)
1247 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1248 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1249 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1250 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1251 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1254 if (view) msiobj_release(&view->hdr);
1255 if (row) msiobj_release(&row->hdr);
1259 static UINT load_file(MSIRECORD *row, LPVOID param)
1261 MSIPACKAGE* package = param;
1265 /* fill in the data */
1267 file = msi_alloc_zero( sizeof (MSIFILE) );
1269 return ERROR_NOT_ENOUGH_MEMORY;
1271 file->File = msi_dup_record_field( row, 1 );
1273 component = MSI_RecordGetString( row, 2 );
1274 file->Component = get_loaded_component( package, component );
1276 if (!file->Component)
1278 WARN("Component not found: %s\n", debugstr_w(component));
1279 msi_free(file->File);
1281 return ERROR_SUCCESS;
1284 file->FileName = msi_dup_record_field( row, 3 );
1285 reduce_to_longfilename( file->FileName );
1287 file->ShortName = msi_dup_record_field( row, 3 );
1288 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1290 file->FileSize = MSI_RecordGetInteger( row, 4 );
1291 file->Version = msi_dup_record_field( row, 5 );
1292 file->Language = msi_dup_record_field( row, 6 );
1293 file->Attributes = MSI_RecordGetInteger( row, 7 );
1294 file->Sequence = MSI_RecordGetInteger( row, 8 );
1296 file->state = msifs_invalid;
1298 /* if the compressed bits are not set in the file attributes,
1299 * then read the information from the package word count property
1301 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1303 file->IsCompressed = FALSE;
1305 else if (file->Attributes &
1306 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1308 file->IsCompressed = TRUE;
1310 else if (file->Attributes & msidbFileAttributesNoncompressed)
1312 file->IsCompressed = FALSE;
1316 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1319 load_file_hash(package, file);
1321 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1323 list_add_tail( &package->files, &file->entry );
1325 return ERROR_SUCCESS;
1328 static UINT load_all_files(MSIPACKAGE *package)
1332 static const WCHAR Query[] =
1333 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1334 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1335 '`','S','e','q','u','e','n','c','e','`', 0};
1337 if (!list_empty(&package->files))
1338 return ERROR_SUCCESS;
1340 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1341 if (rc != ERROR_SUCCESS)
1342 return ERROR_SUCCESS;
1344 rc = MSI_IterateRecords(view, NULL, load_file, package);
1345 msiobj_release(&view->hdr);
1347 return ERROR_SUCCESS;
1350 static UINT load_folder( MSIRECORD *row, LPVOID param )
1352 MSIPACKAGE *package = param;
1353 static WCHAR szEmpty[] = { 0 };
1354 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1357 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1359 return ERROR_NOT_ENOUGH_MEMORY;
1361 folder->Directory = msi_dup_record_field( row, 1 );
1363 TRACE("%s\n", debugstr_w(folder->Directory));
1365 p = msi_dup_record_field(row, 3);
1367 /* split src and target dir */
1369 src_short = folder_split_path( p, ':' );
1371 /* split the long and short paths */
1372 tgt_long = folder_split_path( tgt_short, '|' );
1373 src_long = folder_split_path( src_short, '|' );
1375 /* check for no-op dirs */
1376 if (!lstrcmpW(szDot, tgt_short))
1377 tgt_short = szEmpty;
1378 if (!lstrcmpW(szDot, src_short))
1379 src_short = szEmpty;
1382 tgt_long = tgt_short;
1385 src_short = tgt_short;
1386 src_long = tgt_long;
1390 src_long = src_short;
1392 /* FIXME: use the target short path too */
1393 folder->TargetDefault = strdupW(tgt_long);
1394 folder->SourceShortPath = strdupW(src_short);
1395 folder->SourceLongPath = strdupW(src_long);
1398 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1399 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1400 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1402 folder->Parent = msi_dup_record_field( row, 2 );
1404 folder->Property = msi_dup_property( package, folder->Directory );
1406 list_add_tail( &package->folders, &folder->entry );
1408 TRACE("returning %p\n", folder);
1410 return ERROR_SUCCESS;
1413 static UINT load_all_folders( MSIPACKAGE *package )
1415 static const WCHAR query[] = {
1416 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1417 '`','D','i','r','e','c','t','o','r','y','`',0 };
1421 if (!list_empty(&package->folders))
1422 return ERROR_SUCCESS;
1424 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1425 if (r != ERROR_SUCCESS)
1428 r = MSI_IterateRecords(view, NULL, load_folder, package);
1429 msiobj_release(&view->hdr);
1434 * I am not doing any of the costing functionality yet.
1435 * Mostly looking at doing the Component and Feature loading
1437 * The native MSI does A LOT of modification to tables here. Mostly adding
1438 * a lot of temporary columns to the Feature and Component tables.
1440 * note: Native msi also tracks the short filename. But I am only going to
1441 * track the long ones. Also looking at this directory table
1442 * it appears that the directory table does not get the parents
1443 * resolved base on property only based on their entries in the
1446 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1448 static const WCHAR szCosting[] =
1449 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1451 MSI_SetPropertyW(package, szCosting, szZero);
1452 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1454 load_all_folders( package );
1455 load_all_components( package );
1456 load_all_features( package );
1457 load_all_files( package );
1459 return ERROR_SUCCESS;
1462 static UINT execute_script(MSIPACKAGE *package, UINT script )
1465 UINT rc = ERROR_SUCCESS;
1467 TRACE("Executing Script %i\n",script);
1469 if (!package->script)
1471 ERR("no script!\n");
1472 return ERROR_FUNCTION_FAILED;
1475 for (i = 0; i < package->script->ActionCount[script]; i++)
1478 action = package->script->Actions[script][i];
1479 ui_actionstart(package, action);
1480 TRACE("Executing Action (%s)\n",debugstr_w(action));
1481 rc = ACTION_PerformAction(package, action, script, TRUE);
1482 if (rc != ERROR_SUCCESS)
1485 msi_free_action_script(package, script);
1489 static UINT ACTION_FileCost(MSIPACKAGE *package)
1491 return ERROR_SUCCESS;
1494 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1500 state = MsiQueryProductStateW(package->ProductCode);
1502 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1504 if (!comp->ComponentId)
1507 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1508 comp->Installed = INSTALLSTATE_ABSENT;
1511 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1512 package->Context, comp->ComponentId,
1514 if (r != ERROR_SUCCESS)
1515 comp->Installed = INSTALLSTATE_ABSENT;
1520 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1522 MSIFEATURE *feature;
1525 state = MsiQueryProductStateW(package->ProductCode);
1527 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1529 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1530 feature->Installed = INSTALLSTATE_ABSENT;
1533 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1539 static BOOL process_state_property(MSIPACKAGE* package, int level,
1540 LPCWSTR property, INSTALLSTATE state)
1543 MSIFEATURE *feature;
1545 override = msi_dup_property( package, property );
1549 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1551 if (lstrcmpW(property, szRemove) &&
1552 (feature->Level <= 0 || feature->Level > level))
1555 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1557 if (strcmpiW(override, szAll)==0)
1558 msi_feature_set_state(package, feature, state);
1561 LPWSTR ptr = override;
1562 LPWSTR ptr2 = strchrW(override,',');
1566 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1567 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1569 msi_feature_set_state(package, feature, state);
1575 ptr2 = strchrW(ptr,',');
1587 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1590 static const WCHAR szlevel[] =
1591 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1592 static const WCHAR szAddLocal[] =
1593 {'A','D','D','L','O','C','A','L',0};
1594 static const WCHAR szAddSource[] =
1595 {'A','D','D','S','O','U','R','C','E',0};
1596 static const WCHAR szAdvertise[] =
1597 {'A','D','V','E','R','T','I','S','E',0};
1598 BOOL override = FALSE;
1599 MSICOMPONENT* component;
1600 MSIFEATURE *feature;
1603 /* I do not know if this is where it should happen.. but */
1605 TRACE("Checking Install Level\n");
1607 level = msi_get_property_int(package, szlevel, 1);
1609 /* ok here is the _real_ rub
1610 * all these activation/deactivation things happen in order and things
1611 * later on the list override things earlier on the list.
1612 * 0) INSTALLLEVEL processing
1623 * 11) FILEADDDEFAULT
1625 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1626 * REMOVE are the big ones, since we don't handle administrative installs
1629 override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
1630 override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
1631 override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
1632 override |= process_state_property(package, level, szReinstall, INSTALLSTATE_UNKNOWN);
1633 override |= process_state_property(package, level, szAdvertise, INSTALLSTATE_ADVERTISED);
1637 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1639 BOOL feature_state = ((feature->Level > 0) &&
1640 (feature->Level <= level));
1642 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1644 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1645 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1646 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1647 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1649 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1653 /* disable child features of unselected parent features */
1654 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1658 if (feature->Level > 0 && feature->Level <= level)
1661 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1662 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1666 MSI_SetPropertyW(package, szPreselected, szOne);
1669 * now we want to enable or disable components base on feature
1672 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1676 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1677 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1679 if (!feature->Level)
1682 /* features with components that have compressed files are made local */
1683 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1685 if (cl->component->Enabled &&
1686 cl->component->ForceLocalState &&
1687 feature->Action == INSTALLSTATE_SOURCE)
1689 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1694 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1696 component = cl->component;
1698 if (!component->Enabled)
1701 switch (feature->Action)
1703 case INSTALLSTATE_ABSENT:
1704 component->anyAbsent = 1;
1706 case INSTALLSTATE_ADVERTISED:
1707 component->hasAdvertiseFeature = 1;
1709 case INSTALLSTATE_SOURCE:
1710 component->hasSourceFeature = 1;
1712 case INSTALLSTATE_LOCAL:
1713 component->hasLocalFeature = 1;
1715 case INSTALLSTATE_DEFAULT:
1716 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1717 component->hasAdvertiseFeature = 1;
1718 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1719 component->hasSourceFeature = 1;
1721 component->hasLocalFeature = 1;
1729 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1731 /* if the component isn't enabled, leave it alone */
1732 if (!component->Enabled)
1735 /* check if it's local or source */
1736 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1737 (component->hasLocalFeature || component->hasSourceFeature))
1739 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1740 !component->ForceLocalState)
1741 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1743 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1747 /* if any feature is local, the component must be local too */
1748 if (component->hasLocalFeature)
1750 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1754 if (component->hasSourceFeature)
1756 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1760 if (component->hasAdvertiseFeature)
1762 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1766 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1767 if (component->anyAbsent)
1768 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1771 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1773 if (component->Action == INSTALLSTATE_DEFAULT)
1775 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1776 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1779 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1780 debugstr_w(component->Component), component->Installed, component->Action);
1784 return ERROR_SUCCESS;
1787 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1789 MSIPACKAGE *package = param;
1794 name = MSI_RecordGetString(row,1);
1796 f = get_loaded_folder(package, name);
1797 if (!f) return ERROR_SUCCESS;
1799 /* reset the ResolvedTarget */
1800 msi_free(f->ResolvedTarget);
1801 f->ResolvedTarget = NULL;
1803 /* This helper function now does ALL the work */
1804 TRACE("Dir %s ...\n",debugstr_w(name));
1805 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1806 TRACE("resolves to %s\n",debugstr_w(path));
1809 return ERROR_SUCCESS;
1812 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1814 MSIPACKAGE *package = param;
1816 MSIFEATURE *feature;
1818 name = MSI_RecordGetString( row, 1 );
1820 feature = get_loaded_feature( package, name );
1822 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1826 Condition = MSI_RecordGetString(row,3);
1828 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1830 int level = MSI_RecordGetInteger(row,2);
1831 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1832 feature->Level = level;
1835 return ERROR_SUCCESS;
1838 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1840 static const WCHAR name_fmt[] =
1841 {'%','u','.','%','u','.','%','u','.','%','u',0};
1842 static const WCHAR name[] = {'\\',0};
1843 VS_FIXEDFILEINFO *lpVer;
1844 WCHAR filever[0x100];
1850 TRACE("%s\n", debugstr_w(filename));
1852 versize = GetFileVersionInfoSizeW( filename, &handle );
1856 version = msi_alloc( versize );
1857 GetFileVersionInfoW( filename, 0, versize, version );
1859 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
1861 msi_free( version );
1865 sprintfW( filever, name_fmt,
1866 HIWORD(lpVer->dwFileVersionMS),
1867 LOWORD(lpVer->dwFileVersionMS),
1868 HIWORD(lpVer->dwFileVersionLS),
1869 LOWORD(lpVer->dwFileVersionLS));
1871 msi_free( version );
1873 return strdupW( filever );
1876 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1878 LPWSTR file_version;
1881 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1883 MSICOMPONENT* comp = file->Component;
1889 if (file->IsCompressed)
1890 comp->ForceLocalState = TRUE;
1892 /* calculate target */
1893 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
1895 msi_free(file->TargetPath);
1897 TRACE("file %s is named %s\n",
1898 debugstr_w(file->File), debugstr_w(file->FileName));
1900 file->TargetPath = build_directory_name(2, p, file->FileName);
1904 TRACE("file %s resolves to %s\n",
1905 debugstr_w(file->File), debugstr_w(file->TargetPath));
1907 /* don't check files of components that aren't installed */
1908 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
1909 comp->Installed == INSTALLSTATE_ABSENT)
1911 file->state = msifs_missing; /* assume files are missing */
1915 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1917 file->state = msifs_missing;
1918 comp->Cost += file->FileSize;
1922 if (file->Version &&
1923 (file_version = msi_get_disk_file_version( file->TargetPath )))
1925 TRACE("new %s old %s\n", debugstr_w(file->Version),
1926 debugstr_w(file_version));
1927 /* FIXME: seems like a bad way to compare version numbers */
1928 if (lstrcmpiW(file_version, file->Version)<0)
1930 file->state = msifs_overwrite;
1931 comp->Cost += file->FileSize;
1934 file->state = msifs_present;
1935 msi_free( file_version );
1938 file->state = msifs_present;
1941 return ERROR_SUCCESS;
1945 * A lot is done in this function aside from just the costing.
1946 * The costing needs to be implemented at some point but for now I am going
1947 * to focus on the directory building
1950 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
1952 static const WCHAR ExecSeqQuery[] =
1953 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1954 '`','D','i','r','e','c','t','o','r','y','`',0};
1955 static const WCHAR ConditionQuery[] =
1956 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1957 '`','C','o','n','d','i','t','i','o','n','`',0};
1958 static const WCHAR szCosting[] =
1959 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1960 static const WCHAR szlevel[] =
1961 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1962 static const WCHAR szOutOfDiskSpace[] =
1963 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
1969 TRACE("Building Directory properties\n");
1971 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1972 if (rc == ERROR_SUCCESS)
1974 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
1976 msiobj_release(&view->hdr);
1979 /* read components states from the registry */
1980 ACTION_GetComponentInstallStates(package);
1981 ACTION_GetFeatureInstallStates(package);
1983 TRACE("File calculations\n");
1984 msi_check_file_install_states( package );
1986 TRACE("Evaluating Condition Table\n");
1988 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
1989 if (rc == ERROR_SUCCESS)
1991 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
1993 msiobj_release(&view->hdr);
1996 TRACE("Enabling or Disabling Components\n");
1997 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1999 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2001 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2002 comp->Enabled = FALSE;
2005 comp->Enabled = TRUE;
2008 MSI_SetPropertyW(package,szCosting,szOne);
2009 /* set default run level if not set */
2010 level = msi_dup_property( package, szlevel );
2012 MSI_SetPropertyW(package,szlevel, szOne);
2015 /* FIXME: check volume disk space */
2016 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2018 return MSI_SetFeatureStates(package);
2021 /* OK this value is "interpreted" and then formatted based on the
2022 first few characters */
2023 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2028 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2034 LPWSTR deformated = NULL;
2037 deformat_string(package, &value[2], &deformated);
2039 /* binary value type */
2043 *size = (strlenW(ptr)/2)+1;
2045 *size = strlenW(ptr)/2;
2047 data = msi_alloc(*size);
2053 /* if uneven pad with a zero in front */
2059 data[count] = (BYTE)strtol(byte,NULL,0);
2061 TRACE("Uneven byte count\n");
2069 data[count] = (BYTE)strtol(byte,NULL,0);
2072 msi_free(deformated);
2074 TRACE("Data %i bytes(%i)\n",*size,count);
2081 deformat_string(package, &value[1], &deformated);
2084 *size = sizeof(DWORD);
2085 data = msi_alloc(*size);
2091 if ( (*p < '0') || (*p > '9') )
2097 if (deformated[0] == '-')
2100 TRACE("DWORD %i\n",*(LPDWORD)data);
2102 msi_free(deformated);
2107 static const WCHAR szMulti[] = {'[','~',']',0};
2116 *type=REG_EXPAND_SZ;
2124 if (strstrW(value,szMulti))
2125 *type = REG_MULTI_SZ;
2127 /* remove initial delimiter */
2128 if (!strncmpW(value, szMulti, 3))
2131 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2133 /* add double NULL terminator */
2134 if (*type == REG_MULTI_SZ)
2136 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2137 data = msi_realloc_zero(data, *size);
2143 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2145 MSIPACKAGE *package = param;
2146 static const WCHAR szHCR[] =
2147 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2148 'R','O','O','T','\\',0};
2149 static const WCHAR szHCU[] =
2150 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2151 'U','S','E','R','\\',0};
2152 static const WCHAR szHLM[] =
2153 {'H','K','E','Y','_','L','O','C','A','L','_',
2154 'M','A','C','H','I','N','E','\\',0};
2155 static const WCHAR szHU[] =
2156 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2158 LPSTR value_data = NULL;
2159 HKEY root_key, hkey;
2162 LPCWSTR szRoot, component, name, key, value;
2167 BOOL check_first = FALSE;
2170 ui_progress(package,2,0,0,0);
2177 component = MSI_RecordGetString(row, 6);
2178 comp = get_loaded_component(package,component);
2180 return ERROR_SUCCESS;
2182 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2184 TRACE("Skipping write due to disabled component %s\n",
2185 debugstr_w(component));
2187 comp->Action = comp->Installed;
2189 return ERROR_SUCCESS;
2192 comp->Action = INSTALLSTATE_LOCAL;
2194 name = MSI_RecordGetString(row, 4);
2195 if( MSI_RecordIsNull(row,5) && name )
2197 /* null values can have special meanings */
2198 if (name[0]=='-' && name[1] == 0)
2199 return ERROR_SUCCESS;
2200 else if ((name[0]=='+' && name[1] == 0) ||
2201 (name[0] == '*' && name[1] == 0))
2206 root = MSI_RecordGetInteger(row,2);
2207 key = MSI_RecordGetString(row, 3);
2209 /* get the root key */
2214 LPWSTR all_users = msi_dup_property( package, szAllUsers );
2215 if (all_users && all_users[0] == '1')
2217 root_key = HKEY_LOCAL_MACHINE;
2222 root_key = HKEY_CURRENT_USER;
2225 msi_free(all_users);
2228 case 0: root_key = HKEY_CLASSES_ROOT;
2231 case 1: root_key = HKEY_CURRENT_USER;
2234 case 2: root_key = HKEY_LOCAL_MACHINE;
2237 case 3: root_key = HKEY_USERS;
2241 ERR("Unknown root %i\n",root);
2247 return ERROR_SUCCESS;
2249 deformat_string(package, key , &deformated);
2250 size = strlenW(deformated) + strlenW(szRoot) + 1;
2251 uikey = msi_alloc(size*sizeof(WCHAR));
2252 strcpyW(uikey,szRoot);
2253 strcatW(uikey,deformated);
2255 if (RegCreateKeyW( root_key, deformated, &hkey))
2257 ERR("Could not create key %s\n",debugstr_w(deformated));
2258 msi_free(deformated);
2260 return ERROR_SUCCESS;
2262 msi_free(deformated);
2264 value = MSI_RecordGetString(row,5);
2266 value_data = parse_value(package, value, &type, &size);
2269 value_data = (LPSTR)strdupW(szEmpty);
2270 size = sizeof(szEmpty);
2274 deformat_string(package, name, &deformated);
2278 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2280 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2285 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2286 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2288 TRACE("value %s of %s checked already exists\n",
2289 debugstr_w(deformated), debugstr_w(uikey));
2293 TRACE("Checked and setting value %s of %s\n",
2294 debugstr_w(deformated), debugstr_w(uikey));
2295 if (deformated || size)
2296 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2301 uirow = MSI_CreateRecord(3);
2302 MSI_RecordSetStringW(uirow,2,deformated);
2303 MSI_RecordSetStringW(uirow,1,uikey);
2306 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2308 MSI_RecordSetStringW(uirow,3,value);
2310 ui_actiondata(package,szWriteRegistryValues,uirow);
2311 msiobj_release( &uirow->hdr );
2313 msi_free(value_data);
2314 msi_free(deformated);
2317 return ERROR_SUCCESS;
2320 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2324 static const WCHAR ExecSeqQuery[] =
2325 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2326 '`','R','e','g','i','s','t','r','y','`',0 };
2328 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2329 if (rc != ERROR_SUCCESS)
2330 return ERROR_SUCCESS;
2332 /* increment progress bar each time action data is sent */
2333 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2335 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2337 msiobj_release(&view->hdr);
2341 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2343 package->script->CurrentlyScripting = TRUE;
2345 return ERROR_SUCCESS;
2349 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2354 static const WCHAR q1[]=
2355 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2356 '`','R','e','g','i','s','t','r','y','`',0};
2359 MSIFEATURE *feature;
2362 TRACE("InstallValidate\n");
2364 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2365 if (rc == ERROR_SUCCESS)
2367 MSI_IterateRecords( view, &progress, NULL, package );
2368 msiobj_release( &view->hdr );
2369 total += progress * REG_PROGRESS_VALUE;
2372 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2373 total += COMPONENT_PROGRESS_VALUE;
2375 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2376 total += file->FileSize;
2378 ui_progress(package,0,total,0,0);
2380 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2382 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2383 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2384 feature->ActionRequest);
2387 return ERROR_SUCCESS;
2390 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2392 MSIPACKAGE* package = param;
2393 LPCWSTR cond = NULL;
2394 LPCWSTR message = NULL;
2397 static const WCHAR title[]=
2398 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2400 cond = MSI_RecordGetString(row,1);
2402 r = MSI_EvaluateConditionW(package,cond);
2403 if (r == MSICONDITION_FALSE)
2405 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2408 message = MSI_RecordGetString(row,2);
2409 deformat_string(package,message,&deformated);
2410 MessageBoxW(NULL,deformated,title,MB_OK);
2411 msi_free(deformated);
2414 return ERROR_INSTALL_FAILURE;
2417 return ERROR_SUCCESS;
2420 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2423 MSIQUERY * view = NULL;
2424 static const WCHAR ExecSeqQuery[] =
2425 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2426 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2428 TRACE("Checking launch conditions\n");
2430 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2431 if (rc != ERROR_SUCCESS)
2432 return ERROR_SUCCESS;
2434 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2435 msiobj_release(&view->hdr);
2440 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2444 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2446 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2448 MSIRECORD * row = 0;
2450 LPWSTR deformated,buffer,deformated_name;
2452 static const WCHAR ExecSeqQuery[] =
2453 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2454 '`','R','e','g','i','s','t','r','y','`',' ',
2455 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2456 ' ','=',' ' ,'\'','%','s','\'',0 };
2457 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2458 static const WCHAR fmt2[]=
2459 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2461 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2465 root = MSI_RecordGetInteger(row,2);
2466 key = MSI_RecordGetString(row, 3);
2467 name = MSI_RecordGetString(row, 4);
2468 deformat_string(package, key , &deformated);
2469 deformat_string(package, name, &deformated_name);
2471 len = strlenW(deformated) + 6;
2472 if (deformated_name)
2473 len+=strlenW(deformated_name);
2475 buffer = msi_alloc( len *sizeof(WCHAR));
2477 if (deformated_name)
2478 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2480 sprintfW(buffer,fmt,root,deformated);
2482 msi_free(deformated);
2483 msi_free(deformated_name);
2484 msiobj_release(&row->hdr);
2488 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2490 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2495 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2498 return strdupW( file->TargetPath );
2503 static HKEY openSharedDLLsKey(void)
2506 static const WCHAR path[] =
2507 {'S','o','f','t','w','a','r','e','\\',
2508 'M','i','c','r','o','s','o','f','t','\\',
2509 'W','i','n','d','o','w','s','\\',
2510 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2511 'S','h','a','r','e','d','D','L','L','s',0};
2513 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2517 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2522 DWORD sz = sizeof(count);
2525 hkey = openSharedDLLsKey();
2526 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2527 if (rc != ERROR_SUCCESS)
2533 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2537 hkey = openSharedDLLsKey();
2539 msi_reg_set_val_dword( hkey, path, count );
2541 RegDeleteValueW(hkey,path);
2547 * Return TRUE if the count should be written out and FALSE if not
2549 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2551 MSIFEATURE *feature;
2555 /* only refcount DLLs */
2556 if (comp->KeyPath == NULL ||
2557 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2558 comp->Attributes & msidbComponentAttributesODBCDataSource)
2562 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2563 write = (count > 0);
2565 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2569 /* increment counts */
2570 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2574 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2577 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2579 if ( cl->component == comp )
2584 /* decrement counts */
2585 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2589 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2592 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2594 if ( cl->component == comp )
2599 /* ref count all the files in the component */
2604 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2606 if (file->Component == comp)
2607 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2611 /* add a count for permanent */
2612 if (comp->Attributes & msidbComponentAttributesPermanent)
2615 comp->RefCount = count;
2618 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2621 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2623 WCHAR squished_pc[GUID_SIZE];
2624 WCHAR squished_cc[GUID_SIZE];
2631 squash_guid(package->ProductCode,squished_pc);
2632 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2634 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2638 ui_progress(package,2,0,0,0);
2639 if (!comp->ComponentId)
2642 squash_guid(comp->ComponentId,squished_cc);
2644 msi_free(comp->FullKeypath);
2645 comp->FullKeypath = resolve_keypath( package, comp );
2647 ACTION_RefCountComponent( package, comp );
2649 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2650 debugstr_w(comp->Component),
2651 debugstr_w(squished_cc),
2652 debugstr_w(comp->FullKeypath),
2655 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2656 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2658 if (!comp->FullKeypath)
2661 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2662 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2665 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2668 if (rc != ERROR_SUCCESS)
2671 if (comp->Attributes & msidbComponentAttributesPermanent)
2673 static const WCHAR szPermKey[] =
2674 { '0','0','0','0','0','0','0','0','0','0','0','0',
2675 '0','0','0','0','0','0','0','0','0','0','0','0',
2676 '0','0','0','0','0','0','0','0',0 };
2678 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2681 if (comp->Action == INSTALLSTATE_LOCAL)
2682 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2688 WCHAR source[MAX_PATH];
2689 WCHAR base[MAX_PATH];
2692 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2693 static const WCHAR query[] = {
2694 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2695 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2696 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2697 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2698 '`','D','i','s','k','I','d','`',0};
2700 file = get_loaded_file(package, comp->KeyPath);
2704 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2705 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2706 ptr2 = strrchrW(source, '\\') + 1;
2707 msiobj_release(&row->hdr);
2709 lstrcpyW(base, package->PackagePath);
2710 ptr = strrchrW(base, '\\');
2713 sourcepath = resolve_file_source(package, file);
2714 ptr = sourcepath + lstrlenW(base);
2715 lstrcpyW(ptr2, ptr);
2716 msi_free(sourcepath);
2718 msi_reg_set_val_str(hkey, squished_pc, source);
2722 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2724 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2725 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
2727 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
2731 uirow = MSI_CreateRecord(3);
2732 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2733 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2734 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2735 ui_actiondata(package,szProcessComponents,uirow);
2736 msiobj_release( &uirow->hdr );
2739 return ERROR_SUCCESS;
2750 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2751 LPWSTR lpszName, LONG_PTR lParam)
2754 typelib_struct *tl_struct = (typelib_struct*) lParam;
2755 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2759 if (!IS_INTRESOURCE(lpszName))
2761 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2765 sz = strlenW(tl_struct->source)+4;
2766 sz *= sizeof(WCHAR);
2768 if ((INT_PTR)lpszName == 1)
2769 tl_struct->path = strdupW(tl_struct->source);
2772 tl_struct->path = msi_alloc(sz);
2773 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2776 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2777 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2780 msi_free(tl_struct->path);
2781 tl_struct->path = NULL;
2786 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2787 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2789 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2793 msi_free(tl_struct->path);
2794 tl_struct->path = NULL;
2796 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2797 ITypeLib_Release(tl_struct->ptLib);
2802 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2804 MSIPACKAGE* package = param;
2808 typelib_struct tl_struct;
2813 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2815 component = MSI_RecordGetString(row,3);
2816 comp = get_loaded_component(package,component);
2818 return ERROR_SUCCESS;
2820 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2822 TRACE("Skipping typelib reg due to disabled component\n");
2824 comp->Action = comp->Installed;
2826 return ERROR_SUCCESS;
2829 comp->Action = INSTALLSTATE_LOCAL;
2831 file = get_loaded_file( package, comp->KeyPath );
2833 return ERROR_SUCCESS;
2835 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2839 guid = MSI_RecordGetString(row,1);
2840 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2841 tl_struct.source = strdupW( file->TargetPath );
2842 tl_struct.path = NULL;
2844 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2845 (LONG_PTR)&tl_struct);
2853 helpid = MSI_RecordGetString(row,6);
2856 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2857 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2861 ERR("Failed to register type library %s\n",
2862 debugstr_w(tl_struct.path));
2865 ui_actiondata(package,szRegisterTypeLibraries,row);
2867 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2870 ITypeLib_Release(tl_struct.ptLib);
2871 msi_free(tl_struct.path);
2874 ERR("Failed to load type library %s\n",
2875 debugstr_w(tl_struct.source));
2877 FreeLibrary(module);
2878 msi_free(tl_struct.source);
2882 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
2885 ERR("Failed to load type library: %08x\n", hr);
2886 return ERROR_FUNCTION_FAILED;
2889 ITypeLib_Release(tlib);
2892 return ERROR_SUCCESS;
2895 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2898 * OK this is a bit confusing.. I am given a _Component key and I believe
2899 * that the file that is being registered as a type library is the "key file
2900 * of that component" which I interpret to mean "The file in the KeyPath of
2905 static const WCHAR Query[] =
2906 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2907 '`','T','y','p','e','L','i','b','`',0};
2909 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2910 if (rc != ERROR_SUCCESS)
2911 return ERROR_SUCCESS;
2913 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2914 msiobj_release(&view->hdr);
2918 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
2920 MSIPACKAGE *package = param;
2921 LPWSTR target_file, target_folder, filename;
2922 LPCWSTR buffer, extension;
2924 static const WCHAR szlnk[]={'.','l','n','k',0};
2925 IShellLinkW *sl = NULL;
2926 IPersistFile *pf = NULL;
2929 buffer = MSI_RecordGetString(row,4);
2930 comp = get_loaded_component(package,buffer);
2932 return ERROR_SUCCESS;
2934 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
2936 TRACE("Skipping shortcut creation due to disabled component\n");
2938 comp->Action = comp->Installed;
2940 return ERROR_SUCCESS;
2943 comp->Action = INSTALLSTATE_LOCAL;
2945 ui_actiondata(package,szCreateShortcuts,row);
2947 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
2948 &IID_IShellLinkW, (LPVOID *) &sl );
2952 ERR("CLSID_ShellLink not available\n");
2956 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
2959 ERR("QueryInterface(IID_IPersistFile) failed\n");
2963 buffer = MSI_RecordGetString(row,2);
2964 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
2966 /* may be needed because of a bug somewhere else */
2967 create_full_pathW(target_folder);
2969 filename = msi_dup_record_field( row, 3 );
2970 reduce_to_longfilename(filename);
2972 extension = strchrW(filename,'.');
2973 if (!extension || strcmpiW(extension,szlnk))
2975 int len = strlenW(filename);
2976 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
2977 memcpy(filename + len, szlnk, sizeof(szlnk));
2979 target_file = build_directory_name(2, target_folder, filename);
2980 msi_free(target_folder);
2983 buffer = MSI_RecordGetString(row,5);
2984 if (strchrW(buffer,'['))
2987 deformat_string(package,buffer,&deformated);
2988 IShellLinkW_SetPath(sl,deformated);
2989 msi_free(deformated);
2993 FIXME("poorly handled shortcut format, advertised shortcut\n");
2994 IShellLinkW_SetPath(sl,comp->FullKeypath);
2997 if (!MSI_RecordIsNull(row,6))
3000 buffer = MSI_RecordGetString(row,6);
3001 deformat_string(package,buffer,&deformated);
3002 IShellLinkW_SetArguments(sl,deformated);
3003 msi_free(deformated);
3006 if (!MSI_RecordIsNull(row,7))
3008 buffer = MSI_RecordGetString(row,7);
3009 IShellLinkW_SetDescription(sl,buffer);
3012 if (!MSI_RecordIsNull(row,8))
3013 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3015 if (!MSI_RecordIsNull(row,9))
3020 buffer = MSI_RecordGetString(row,9);
3022 Path = build_icon_path(package,buffer);
3023 index = MSI_RecordGetInteger(row,10);
3025 /* no value means 0 */
3026 if (index == MSI_NULL_INTEGER)
3029 IShellLinkW_SetIconLocation(sl,Path,index);
3033 if (!MSI_RecordIsNull(row,11))
3034 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3036 if (!MSI_RecordIsNull(row,12))
3039 buffer = MSI_RecordGetString(row,12);
3040 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3042 IShellLinkW_SetWorkingDirectory(sl,Path);
3046 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3047 IPersistFile_Save(pf,target_file,FALSE);
3049 msi_free(target_file);
3053 IPersistFile_Release( pf );
3055 IShellLinkW_Release( sl );
3057 return ERROR_SUCCESS;
3060 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3065 static const WCHAR Query[] =
3066 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3067 '`','S','h','o','r','t','c','u','t','`',0};
3069 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3070 if (rc != ERROR_SUCCESS)
3071 return ERROR_SUCCESS;
3073 res = CoInitialize( NULL );
3075 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3076 msiobj_release(&view->hdr);
3084 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3086 MSIPACKAGE* package = param;
3095 FileName = MSI_RecordGetString(row,1);
3098 ERR("Unable to get FileName\n");
3099 return ERROR_SUCCESS;
3102 FilePath = build_icon_path(package,FileName);
3104 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3106 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3107 FILE_ATTRIBUTE_NORMAL, NULL);
3109 if (the_file == INVALID_HANDLE_VALUE)
3111 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3113 return ERROR_SUCCESS;
3120 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3121 if (rc != ERROR_SUCCESS)
3123 ERR("Failed to get stream\n");
3124 CloseHandle(the_file);
3125 DeleteFileW(FilePath);
3128 WriteFile(the_file,buffer,sz,&write,NULL);
3129 } while (sz == 1024);
3133 CloseHandle(the_file);
3135 uirow = MSI_CreateRecord(1);
3136 MSI_RecordSetStringW(uirow,1,FileName);
3137 ui_actiondata(package,szPublishProduct,uirow);
3138 msiobj_release( &uirow->hdr );
3140 return ERROR_SUCCESS;
3143 static UINT msi_publish_icons(MSIPACKAGE *package)
3148 static const WCHAR query[]= {
3149 'S','E','L','E','C','T',' ','*',' ',
3150 'F','R','O','M',' ','`','I','c','o','n','`',0};
3152 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3153 if (r == ERROR_SUCCESS)
3155 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3156 msiobj_release(&view->hdr);
3159 return ERROR_SUCCESS;
3162 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3168 MSISOURCELISTINFO *info;
3170 r = RegCreateKeyW(hkey, szSourceList, &source);
3171 if (r != ERROR_SUCCESS)
3174 RegCloseKey(source);
3176 buffer = strrchrW(package->PackagePath, '\\') + 1;
3177 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3178 package->Context, MSICODE_PRODUCT,
3179 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3180 if (r != ERROR_SUCCESS)
3183 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3184 package->Context, MSICODE_PRODUCT,
3185 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3186 if (r != ERROR_SUCCESS)
3189 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3190 package->Context, MSICODE_PRODUCT,
3191 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3192 if (r != ERROR_SUCCESS)
3195 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3197 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3198 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3199 info->options, info->value);
3201 MsiSourceListSetInfoW(package->ProductCode, NULL,
3202 info->context, info->options,
3203 info->property, info->value);
3206 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3208 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3209 disk->context, disk->options,
3210 disk->disk_id, disk->volume_label, disk->disk_prompt);
3213 return ERROR_SUCCESS;
3216 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3218 MSIHANDLE hdb, suminfo;
3219 WCHAR guids[MAX_PATH];
3220 WCHAR packcode[SQUISH_GUID_SIZE];
3227 static const WCHAR szProductLanguage[] =
3228 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3229 static const WCHAR szARPProductIcon[] =
3230 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3231 static const WCHAR szProductVersion[] =
3232 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3233 static const WCHAR szAssignment[] =
3234 {'A','s','s','i','g','n','m','e','n','t',0};
3235 static const WCHAR szAdvertiseFlags[] =
3236 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3237 static const WCHAR szClients[] =
3238 {'C','l','i','e','n','t','s',0};
3239 static const WCHAR szColon[] = {':',0};
3241 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3242 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3245 langid = msi_get_property_int(package, szProductLanguage, 0);
3246 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3249 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3251 buffer = msi_dup_property(package, szARPProductIcon);
3254 LPWSTR path = build_icon_path(package,buffer);
3255 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3260 buffer = msi_dup_property(package, szProductVersion);
3263 DWORD verdword = msi_version_str_to_dword(buffer);
3264 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3268 msi_reg_set_val_dword(hkey, szAssignment, 0);
3269 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3270 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3271 msi_reg_set_val_str(hkey, szClients, szColon);
3273 hdb = alloc_msihandle(&package->db->hdr);
3275 return ERROR_NOT_ENOUGH_MEMORY;
3277 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3278 MsiCloseHandle(hdb);
3279 if (r != ERROR_SUCCESS)
3283 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3284 NULL, guids, &size);
3285 if (r != ERROR_SUCCESS)
3288 ptr = strchrW(guids, ';');
3290 squash_guid(guids, packcode);
3291 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3294 MsiCloseHandle(suminfo);
3295 return ERROR_SUCCESS;
3298 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3303 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3305 static const WCHAR szUpgradeCode[] =
3306 {'U','p','g','r','a','d','e','C','o','d','e',0};
3308 upgrade = msi_dup_property(package, szUpgradeCode);
3310 return ERROR_SUCCESS;
3312 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3314 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3315 if (r != ERROR_SUCCESS)
3320 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3321 if (r != ERROR_SUCCESS)
3325 squash_guid(package->ProductCode, squashed_pc);
3326 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3335 static BOOL msi_check_publish(MSIPACKAGE *package)
3337 MSIFEATURE *feature;
3339 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3341 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3348 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3350 MSIFEATURE *feature;
3352 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3354 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3361 static UINT msi_publish_patch(MSIPACKAGE *package, HKEY prodkey, HKEY hudkey)
3363 WCHAR patch_squashed[GUID_SIZE];
3366 UINT r = ERROR_FUNCTION_FAILED;
3368 res = RegCreateKeyExW(prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
3370 if (res != ERROR_SUCCESS)
3371 return ERROR_FUNCTION_FAILED;
3373 squash_guid(package->patch->patchcode, patch_squashed);
3375 res = RegSetValueExW(patches, szPatches, 0, REG_MULTI_SZ,
3376 (const BYTE *)patch_squashed,
3377 (lstrlenW(patch_squashed) + 1) * sizeof(WCHAR));
3378 if (res != ERROR_SUCCESS)
3381 res = RegSetValueExW(patches, patch_squashed, 0, REG_SZ,
3382 (const BYTE *)package->patch->transforms,
3383 (lstrlenW(package->patch->transforms) + 1) * sizeof(WCHAR));
3384 if (res == ERROR_SUCCESS)
3388 RegCloseKey(patches);
3393 * 99% of the work done here is only done for
3394 * advertised installs. However this is where the
3395 * Icon table is processed and written out
3396 * so that is what I am going to do here.
3398 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3404 /* FIXME: also need to publish if the product is in advertise mode */
3405 if (!msi_check_publish(package))
3406 return ERROR_SUCCESS;
3408 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3410 if (rc != ERROR_SUCCESS)
3413 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3414 NULL, &hudkey, TRUE);
3415 if (rc != ERROR_SUCCESS)
3418 rc = msi_publish_upgrade_code(package);
3419 if (rc != ERROR_SUCCESS)
3424 rc = msi_publish_patch(package, hukey, hudkey);
3425 if (rc != ERROR_SUCCESS)
3429 rc = msi_publish_product_properties(package, hukey);
3430 if (rc != ERROR_SUCCESS)
3433 rc = msi_publish_sourcelist(package, hukey);
3434 if (rc != ERROR_SUCCESS)
3437 rc = msi_publish_icons(package);
3441 RegCloseKey(hudkey);
3446 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3448 MSIPACKAGE *package = param;
3449 LPCWSTR component, section, key, value, identifier, dirproperty;
3450 LPWSTR deformated_section, deformated_key, deformated_value;
3451 LPWSTR folder, filename, fullname = NULL;
3452 LPCWSTR filenameptr;
3456 static const WCHAR szWindowsFolder[] =
3457 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3459 component = MSI_RecordGetString(row, 8);
3460 comp = get_loaded_component(package,component);
3462 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3464 TRACE("Skipping ini file due to disabled component %s\n",
3465 debugstr_w(component));
3467 comp->Action = comp->Installed;
3469 return ERROR_SUCCESS;
3472 comp->Action = INSTALLSTATE_LOCAL;
3474 identifier = MSI_RecordGetString(row,1);
3475 dirproperty = MSI_RecordGetString(row,3);
3476 section = MSI_RecordGetString(row,4);
3477 key = MSI_RecordGetString(row,5);
3478 value = MSI_RecordGetString(row,6);
3479 action = MSI_RecordGetInteger(row,7);
3481 deformat_string(package,section,&deformated_section);
3482 deformat_string(package,key,&deformated_key);
3483 deformat_string(package,value,&deformated_value);
3485 filename = msi_dup_record_field(row, 2);
3486 if (filename && (filenameptr = strchrW(filename, '|')))
3489 filenameptr = filename;
3493 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3495 folder = msi_dup_property( package, dirproperty );
3498 folder = msi_dup_property( package, szWindowsFolder );
3502 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3506 fullname = build_directory_name(2, folder, filenameptr);
3510 TRACE("Adding value %s to section %s in %s\n",
3511 debugstr_w(deformated_key), debugstr_w(deformated_section),
3512 debugstr_w(fullname));
3513 WritePrivateProfileStringW(deformated_section, deformated_key,
3514 deformated_value, fullname);
3516 else if (action == 1)
3519 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3520 returned, 10, fullname);
3521 if (returned[0] == 0)
3523 TRACE("Adding value %s to section %s in %s\n",
3524 debugstr_w(deformated_key), debugstr_w(deformated_section),
3525 debugstr_w(fullname));
3527 WritePrivateProfileStringW(deformated_section, deformated_key,
3528 deformated_value, fullname);
3531 else if (action == 3)
3532 FIXME("Append to existing section not yet implemented\n");
3534 uirow = MSI_CreateRecord(4);
3535 MSI_RecordSetStringW(uirow,1,identifier);
3536 MSI_RecordSetStringW(uirow,2,deformated_section);
3537 MSI_RecordSetStringW(uirow,3,deformated_key);
3538 MSI_RecordSetStringW(uirow,4,deformated_value);
3539 ui_actiondata(package,szWriteIniValues,uirow);
3540 msiobj_release( &uirow->hdr );
3546 msi_free(deformated_key);
3547 msi_free(deformated_value);
3548 msi_free(deformated_section);
3549 return ERROR_SUCCESS;
3552 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3556 static const WCHAR ExecSeqQuery[] =
3557 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3558 '`','I','n','i','F','i','l','e','`',0};
3560 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3561 if (rc != ERROR_SUCCESS)
3563 TRACE("no IniFile table\n");
3564 return ERROR_SUCCESS;
3567 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3568 msiobj_release(&view->hdr);
3572 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3574 MSIPACKAGE *package = param;
3579 static const WCHAR ExeStr[] =
3580 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3581 static const WCHAR close[] = {'\"',0};
3583 PROCESS_INFORMATION info;
3588 memset(&si,0,sizeof(STARTUPINFOW));
3590 filename = MSI_RecordGetString(row,1);
3591 file = get_loaded_file( package, filename );
3595 ERR("Unable to find file id %s\n",debugstr_w(filename));
3596 return ERROR_SUCCESS;
3599 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3601 FullName = msi_alloc(len*sizeof(WCHAR));
3602 strcpyW(FullName,ExeStr);
3603 strcatW( FullName, file->TargetPath );
3604 strcatW(FullName,close);
3606 TRACE("Registering %s\n",debugstr_w(FullName));
3607 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3612 CloseHandle(info.hThread);
3613 msi_dialog_check_messages(info.hProcess);
3614 CloseHandle(info.hProcess);
3620 uirow = MSI_CreateRecord( 2 );
3621 uipath = strdupW( file->TargetPath );
3622 p = strrchrW(uipath,'\\');
3625 MSI_RecordSetStringW( uirow, 1, &p[1] );
3626 MSI_RecordSetStringW( uirow, 2, uipath);
3627 ui_actiondata( package, szSelfRegModules, uirow);
3628 msiobj_release( &uirow->hdr );
3630 /* FIXME: call ui_progress? */
3632 return ERROR_SUCCESS;
3635 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3639 static const WCHAR ExecSeqQuery[] =
3640 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3641 '`','S','e','l','f','R','e','g','`',0};
3643 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3644 if (rc != ERROR_SUCCESS)
3646 TRACE("no SelfReg table\n");
3647 return ERROR_SUCCESS;
3650 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3651 msiobj_release(&view->hdr);
3653 return ERROR_SUCCESS;
3656 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3658 MSIFEATURE *feature;
3661 HKEY userdata = NULL;
3663 if (!msi_check_publish(package))
3664 return ERROR_SUCCESS;
3666 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3668 if (rc != ERROR_SUCCESS)
3671 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3673 if (rc != ERROR_SUCCESS)
3676 /* here the guids are base 85 encoded */
3677 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3683 BOOL absent = FALSE;
3686 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3687 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3688 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3692 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3696 if (feature->Feature_Parent)
3697 size += strlenW( feature->Feature_Parent )+2;
3699 data = msi_alloc(size * sizeof(WCHAR));
3702 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3704 MSICOMPONENT* component = cl->component;
3708 if (component->ComponentId)
3710 TRACE("From %s\n",debugstr_w(component->ComponentId));
3711 CLSIDFromString(component->ComponentId, &clsid);
3712 encode_base85_guid(&clsid,buf);
3713 TRACE("to %s\n",debugstr_w(buf));
3718 if (feature->Feature_Parent)
3720 static const WCHAR sep[] = {'\2',0};
3722 strcatW(data,feature->Feature_Parent);
3725 msi_reg_set_val_str( userdata, feature->Feature, data );
3729 if (feature->Feature_Parent)
3730 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3733 size += sizeof(WCHAR);
3734 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3735 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
3739 size += 2*sizeof(WCHAR);
3740 data = msi_alloc(size);
3743 if (feature->Feature_Parent)
3744 strcpyW( &data[1], feature->Feature_Parent );
3745 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3751 uirow = MSI_CreateRecord( 1 );
3752 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3753 ui_actiondata( package, szPublishFeatures, uirow);
3754 msiobj_release( &uirow->hdr );
3755 /* FIXME: call ui_progress? */
3760 RegCloseKey(userdata);
3764 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3769 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3771 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3773 if (r == ERROR_SUCCESS)
3775 RegDeleteValueW(hkey, feature->Feature);
3779 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3781 if (r == ERROR_SUCCESS)
3783 RegDeleteValueW(hkey, feature->Feature);
3787 return ERROR_SUCCESS;
3790 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3792 MSIFEATURE *feature;
3794 if (!msi_check_unpublish(package))
3795 return ERROR_SUCCESS;
3797 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3799 msi_unpublish_feature(package, feature);
3802 return ERROR_SUCCESS;
3805 static UINT msi_get_local_package_name( LPWSTR path )
3807 static const WCHAR szInstaller[] = {
3808 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3809 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3813 time = GetTickCount();
3814 GetWindowsDirectoryW( path, MAX_PATH );
3815 lstrcatW( path, szInstaller );
3816 CreateDirectoryW( path, NULL );
3818 len = lstrlenW(path);
3819 for (i=0; i<0x10000; i++)
3821 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3822 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3823 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3824 if (handle != INVALID_HANDLE_VALUE)
3826 CloseHandle(handle);
3829 if (GetLastError() != ERROR_FILE_EXISTS &&
3830 GetLastError() != ERROR_SHARING_VIOLATION)
3831 return ERROR_FUNCTION_FAILED;
3834 return ERROR_SUCCESS;
3837 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3839 WCHAR packagefile[MAX_PATH];
3842 r = msi_get_local_package_name( packagefile );
3843 if (r != ERROR_SUCCESS)
3846 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3848 r = CopyFileW( package->db->path, packagefile, FALSE);
3852 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3853 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
3854 return ERROR_FUNCTION_FAILED;
3857 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3859 return ERROR_SUCCESS;
3862 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
3864 LPWSTR prop, val, key;
3870 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
3871 static const WCHAR szWindowsInstaller[] =
3872 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3873 static const WCHAR modpath_fmt[] =
3874 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3875 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3876 static const WCHAR szModifyPath[] =
3877 {'M','o','d','i','f','y','P','a','t','h',0};
3878 static const WCHAR szUninstallString[] =
3879 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3880 static const WCHAR szEstimatedSize[] =
3881 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3882 static const WCHAR szProductLanguage[] =
3883 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3884 static const WCHAR szProductVersion[] =
3885 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3886 static const WCHAR szProductName[] =
3887 {'P','r','o','d','u','c','t','N','a','m','e',0};
3888 static const WCHAR szDisplayName[] =
3889 {'D','i','s','p','l','a','y','N','a','m','e',0};
3890 static const WCHAR szDisplayVersion[] =
3891 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
3892 static const WCHAR szManufacturer[] =
3893 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
3895 static const LPCSTR propval[] = {
3896 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3897 "ARPCONTACT", "Contact",
3898 "ARPCOMMENTS", "Comments",
3899 "ProductName", "DisplayName",
3900 "ProductVersion", "DisplayVersion",
3901 "ARPHELPLINK", "HelpLink",
3902 "ARPHELPTELEPHONE", "HelpTelephone",
3903 "ARPINSTALLLOCATION", "InstallLocation",
3904 "SourceDir", "InstallSource",
3905 "Manufacturer", "Publisher",
3906 "ARPREADME", "Readme",
3908 "ARPURLINFOABOUT", "URLInfoAbout",
3909 "ARPURLUPDATEINFO", "URLUpdateInfo",
3912 const LPCSTR *p = propval;
3916 prop = strdupAtoW(*p++);
3917 key = strdupAtoW(*p++);
3918 val = msi_dup_property(package, prop);
3919 msi_reg_set_val_str(hkey, key, val);
3925 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
3927 size = deformat_string(package, modpath_fmt, &buffer);
3928 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
3929 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
3932 /* FIXME: Write real Estimated Size when we have it */
3933 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
3935 buffer = msi_dup_property(package, szProductName);
3936 msi_reg_set_val_str(hkey, szDisplayName, buffer);
3939 buffer = msi_dup_property(package, cszSourceDir);
3940 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
3943 buffer = msi_dup_property(package, szManufacturer);
3944 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
3947 GetLocalTime(&systime);
3948 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
3949 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
3951 langid = msi_get_property_int(package, szProductLanguage, 0);
3952 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3954 buffer = msi_dup_property(package, szProductVersion);
3955 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
3958 DWORD verdword = msi_version_str_to_dword(buffer);
3960 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3961 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
3962 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
3966 return ERROR_SUCCESS;
3969 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3971 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3972 LPWSTR upgrade_code;
3977 static const WCHAR szUpgradeCode[] = {
3978 'U','p','g','r','a','d','e','C','o','d','e',0};
3980 /* FIXME: also need to publish if the product is in advertise mode */
3981 if (!msi_check_publish(package))
3982 return ERROR_SUCCESS;
3984 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
3985 if (rc != ERROR_SUCCESS)
3988 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
3989 NULL, &props, TRUE);
3990 if (rc != ERROR_SUCCESS)
3993 msi_make_package_local(package, props);
3995 rc = msi_publish_install_properties(package, hkey);
3996 if (rc != ERROR_SUCCESS)
3999 rc = msi_publish_install_properties(package, props);
4000 if (rc != ERROR_SUCCESS)
4003 upgrade_code = msi_dup_property(package, szUpgradeCode);
4006 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4007 squash_guid(package->ProductCode, squashed_pc);
4008 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4009 RegCloseKey(upgrade);
4010 msi_free(upgrade_code);
4016 return ERROR_SUCCESS;
4019 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4021 return execute_script(package,INSTALL_SCRIPT);
4024 static UINT msi_unpublish_product(MSIPACKAGE *package)
4027 LPWSTR remove = NULL;
4028 LPWSTR *features = NULL;
4029 BOOL full_uninstall = TRUE;
4030 MSIFEATURE *feature;
4032 static const WCHAR szUpgradeCode[] =
4033 {'U','p','g','r','a','d','e','C','o','d','e',0};
4035 remove = msi_dup_property(package, szRemove);
4037 return ERROR_SUCCESS;
4039 features = msi_split_string(remove, ',');
4043 ERR("REMOVE feature list is empty!\n");
4044 return ERROR_FUNCTION_FAILED;
4047 if (!lstrcmpW(features[0], szAll))
4048 full_uninstall = TRUE;
4051 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4053 if (feature->Action != INSTALLSTATE_ABSENT)
4054 full_uninstall = FALSE;
4058 if (!full_uninstall)
4061 MSIREG_DeleteProductKey(package->ProductCode);
4062 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4063 MSIREG_DeleteUninstallKey(package->ProductCode);
4065 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4067 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4068 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4072 MSIREG_DeleteUserProductKey(package->ProductCode);
4073 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4076 upgrade = msi_dup_property(package, szUpgradeCode);
4079 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4086 return ERROR_SUCCESS;
4089 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4093 rc = msi_unpublish_product(package);
4094 if (rc != ERROR_SUCCESS)
4097 /* turn off scheduling */
4098 package->script->CurrentlyScripting= FALSE;
4100 /* first do the same as an InstallExecute */
4101 rc = ACTION_InstallExecute(package);
4102 if (rc != ERROR_SUCCESS)
4105 /* then handle Commit Actions */
4106 rc = execute_script(package,COMMIT_SCRIPT);
4111 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4113 static const WCHAR RunOnce[] = {
4114 'S','o','f','t','w','a','r','e','\\',
4115 'M','i','c','r','o','s','o','f','t','\\',
4116 'W','i','n','d','o','w','s','\\',
4117 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4118 'R','u','n','O','n','c','e',0};
4119 static const WCHAR InstallRunOnce[] = {
4120 'S','o','f','t','w','a','r','e','\\',
4121 'M','i','c','r','o','s','o','f','t','\\',
4122 'W','i','n','d','o','w','s','\\',
4123 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4124 'I','n','s','t','a','l','l','e','r','\\',
4125 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4127 static const WCHAR msiexec_fmt[] = {
4129 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4130 '\"','%','s','\"',0};
4131 static const WCHAR install_fmt[] = {
4132 '/','I',' ','\"','%','s','\"',' ',
4133 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4134 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4135 WCHAR buffer[256], sysdir[MAX_PATH];
4137 WCHAR squished_pc[100];
4139 squash_guid(package->ProductCode,squished_pc);
4141 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4142 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4143 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4146 msi_reg_set_val_str( hkey, squished_pc, buffer );
4149 TRACE("Reboot command %s\n",debugstr_w(buffer));
4151 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4152 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4154 msi_reg_set_val_str( hkey, squished_pc, buffer );
4157 return ERROR_INSTALL_SUSPEND;
4160 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4166 * We are currently doing what should be done here in the top level Install
4167 * however for Administrative and uninstalls this step will be needed
4169 if (!package->PackagePath)
4170 return ERROR_SUCCESS;
4172 msi_set_sourcedir_props(package, TRUE);
4174 attrib = GetFileAttributesW(package->db->path);
4175 if (attrib == INVALID_FILE_ATTRIBUTES)
4181 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4182 package->Context, MSICODE_PRODUCT,
4183 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4184 if (rc == ERROR_MORE_DATA)
4186 prompt = msi_alloc(size * sizeof(WCHAR));
4187 MsiSourceListGetInfoW(package->ProductCode, NULL,
4188 package->Context, MSICODE_PRODUCT,
4189 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4192 prompt = strdupW(package->db->path);
4194 msg = generate_error_string(package,1302,1,prompt);
4195 while(attrib == INVALID_FILE_ATTRIBUTES)
4197 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4200 rc = ERROR_INSTALL_USEREXIT;
4203 attrib = GetFileAttributesW(package->db->path);
4209 return ERROR_SUCCESS;
4214 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4221 static const WCHAR szPropKeys[][80] =
4223 {'P','r','o','d','u','c','t','I','D',0},
4224 {'U','S','E','R','N','A','M','E',0},
4225 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4229 static const WCHAR szRegKeys[][80] =
4231 {'P','r','o','d','u','c','t','I','D',0},
4232 {'R','e','g','O','w','n','e','r',0},
4233 {'R','e','g','C','o','m','p','a','n','y',0},
4237 if (msi_check_unpublish(package))
4239 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4240 return ERROR_SUCCESS;
4243 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4245 return ERROR_SUCCESS;
4247 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4249 if (rc != ERROR_SUCCESS)
4252 for( i = 0; szPropKeys[i][0]; i++ )
4254 buffer = msi_dup_property( package, szPropKeys[i] );
4255 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4260 msi_free(productid);
4263 /* FIXME: call ui_actiondata */
4269 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4273 package->script->InWhatSequence |= SEQUENCE_EXEC;
4274 rc = ACTION_ProcessExecSequence(package,FALSE);
4279 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4281 MSIPACKAGE *package = param;
4282 LPCWSTR compgroupid=NULL;
4283 LPCWSTR feature=NULL;
4284 LPCWSTR text = NULL;
4285 LPCWSTR qualifier = NULL;
4286 LPCWSTR component = NULL;
4287 LPWSTR advertise = NULL;
4288 LPWSTR output = NULL;
4290 UINT rc = ERROR_SUCCESS;
4295 component = MSI_RecordGetString(rec,3);
4296 comp = get_loaded_component(package,component);
4298 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4299 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4300 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4302 TRACE("Skipping: Component %s not scheduled for install\n",
4303 debugstr_w(component));
4305 return ERROR_SUCCESS;
4308 compgroupid = MSI_RecordGetString(rec,1);
4309 qualifier = MSI_RecordGetString(rec,2);
4311 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4312 if (rc != ERROR_SUCCESS)
4315 text = MSI_RecordGetString(rec,4);
4316 feature = MSI_RecordGetString(rec,5);
4318 advertise = create_component_advertise_string(package, comp, feature);
4320 sz = strlenW(advertise);
4323 sz += lstrlenW(text);
4326 sz *= sizeof(WCHAR);
4328 output = msi_alloc_zero(sz);
4329 strcpyW(output,advertise);
4330 msi_free(advertise);
4333 strcatW(output,text);
4335 msi_reg_set_val_multi_str( hkey, qualifier, output );
4342 uirow = MSI_CreateRecord( 2 );
4343 MSI_RecordSetStringW( uirow, 1, compgroupid );
4344 MSI_RecordSetStringW( uirow, 2, qualifier);
4345 ui_actiondata( package, szPublishComponents, uirow);
4346 msiobj_release( &uirow->hdr );
4347 /* FIXME: call ui_progress? */
4353 * At present I am ignorning the advertised components part of this and only
4354 * focusing on the qualified component sets
4356 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4360 static const WCHAR ExecSeqQuery[] =
4361 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4362 '`','P','u','b','l','i','s','h',
4363 'C','o','m','p','o','n','e','n','t','`',0};
4365 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4366 if (rc != ERROR_SUCCESS)
4367 return ERROR_SUCCESS;
4369 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4370 msiobj_release(&view->hdr);
4375 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4377 MSIPACKAGE *package = param;
4380 SC_HANDLE hscm, service = NULL;
4381 LPCWSTR comp, depends, pass;
4382 LPWSTR name = NULL, disp = NULL;
4383 LPCWSTR load_order, serv_name, key;
4384 DWORD serv_type, start_type;
4387 static const WCHAR query[] =
4388 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4389 '`','C','o','m','p','o','n','e','n','t','`',' ',
4390 'W','H','E','R','E',' ',
4391 '`','C','o','m','p','o','n','e','n','t','`',' ',
4392 '=','\'','%','s','\'',0};
4394 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4397 ERR("Failed to open the SC Manager!\n");
4401 start_type = MSI_RecordGetInteger(rec, 5);
4402 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4405 depends = MSI_RecordGetString(rec, 8);
4406 if (depends && *depends)
4407 FIXME("Dependency list unhandled!\n");
4409 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4410 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4411 serv_type = MSI_RecordGetInteger(rec, 4);
4412 err_control = MSI_RecordGetInteger(rec, 6);
4413 load_order = MSI_RecordGetString(rec, 7);
4414 serv_name = MSI_RecordGetString(rec, 9);
4415 pass = MSI_RecordGetString(rec, 10);
4416 comp = MSI_RecordGetString(rec, 12);
4418 /* fetch the service path */
4419 row = MSI_QueryGetRecord(package->db, query, comp);
4422 ERR("Control query failed!\n");
4426 key = MSI_RecordGetString(row, 6);
4428 file = get_loaded_file(package, key);
4429 msiobj_release(&row->hdr);
4432 ERR("Failed to load the service file\n");
4436 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4437 start_type, err_control, file->TargetPath,
4438 load_order, NULL, NULL, serv_name, pass);
4441 if (GetLastError() != ERROR_SERVICE_EXISTS)
4442 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4446 CloseServiceHandle(service);
4447 CloseServiceHandle(hscm);
4451 return ERROR_SUCCESS;
4454 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4458 static const WCHAR ExecSeqQuery[] =
4459 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4460 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4462 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4463 if (rc != ERROR_SUCCESS)
4464 return ERROR_SUCCESS;
4466 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4467 msiobj_release(&view->hdr);
4472 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4473 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4475 LPCWSTR *vector, *temp_vector;
4479 static const WCHAR separator[] = {'[','~',']',0};
4482 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4487 vector = msi_alloc(sizeof(LPWSTR));
4495 vector[*numargs - 1] = p;
4497 if ((q = strstrW(p, separator)))
4501 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4507 vector = temp_vector;
4516 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4518 MSIPACKAGE *package = param;
4520 SC_HANDLE scm, service = NULL;
4521 LPCWSTR name, *vector = NULL;
4523 DWORD event, numargs;
4524 UINT r = ERROR_FUNCTION_FAILED;
4526 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4527 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4528 return ERROR_SUCCESS;
4530 name = MSI_RecordGetString(rec, 2);
4531 event = MSI_RecordGetInteger(rec, 3);
4532 args = strdupW(MSI_RecordGetString(rec, 4));
4534 if (!(event & msidbServiceControlEventStart))
4535 return ERROR_SUCCESS;
4537 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4540 ERR("Failed to open the service control manager\n");
4544 service = OpenServiceW(scm, name, SERVICE_START);
4547 ERR("Failed to open service %s\n", debugstr_w(name));
4551 vector = msi_service_args_to_vector(args, &numargs);
4553 if (!StartServiceW(service, numargs, vector))
4555 ERR("Failed to start service %s\n", debugstr_w(name));
4562 CloseServiceHandle(service);
4563 CloseServiceHandle(scm);
4570 static UINT ACTION_StartServices( MSIPACKAGE *package )
4575 static const WCHAR query[] = {
4576 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4577 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4579 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4580 if (rc != ERROR_SUCCESS)
4581 return ERROR_SUCCESS;
4583 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4584 msiobj_release(&view->hdr);
4589 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4591 DWORD i, needed, count;
4592 ENUM_SERVICE_STATUSW *dependencies;
4596 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4597 0, &needed, &count))
4600 if (GetLastError() != ERROR_MORE_DATA)
4603 dependencies = msi_alloc(needed);
4607 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4608 needed, &needed, &count))
4611 for (i = 0; i < count; i++)
4613 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4614 SERVICE_STOP | SERVICE_QUERY_STATUS);
4618 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4625 msi_free(dependencies);
4629 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4631 MSIPACKAGE *package = param;
4633 SERVICE_STATUS status;
4634 SERVICE_STATUS_PROCESS ssp;
4635 SC_HANDLE scm = NULL, service = NULL;
4637 DWORD event, needed;
4639 event = MSI_RecordGetInteger(rec, 3);
4640 if (!(event & msidbServiceControlEventStop))
4641 return ERROR_SUCCESS;
4643 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4644 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4645 return ERROR_SUCCESS;
4647 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4648 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4649 args = strdupW(MSI_RecordGetString(rec, 4));
4651 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4654 WARN("Failed to open the SCM: %d\n", GetLastError());
4658 service = OpenServiceW(scm, name,
4660 SERVICE_QUERY_STATUS |
4661 SERVICE_ENUMERATE_DEPENDENTS);
4664 WARN("Failed to open service (%s): %d\n",
4665 debugstr_w(name), GetLastError());
4669 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4670 sizeof(SERVICE_STATUS_PROCESS), &needed))
4672 WARN("Failed to query service status (%s): %d\n",
4673 debugstr_w(name), GetLastError());
4677 if (ssp.dwCurrentState == SERVICE_STOPPED)
4680 stop_service_dependents(scm, service);
4682 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4683 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4686 CloseServiceHandle(service);
4687 CloseServiceHandle(scm);
4691 return ERROR_SUCCESS;
4694 static UINT ACTION_StopServices( MSIPACKAGE *package )
4699 static const WCHAR query[] = {
4700 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4701 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4703 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4704 if (rc != ERROR_SUCCESS)
4705 return ERROR_SUCCESS;
4707 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4708 msiobj_release(&view->hdr);
4713 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4717 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4719 if (!lstrcmpW(file->File, filename))
4726 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4728 MSIPACKAGE *package = param;
4729 LPWSTR driver, driver_path, ptr;
4730 WCHAR outpath[MAX_PATH];
4731 MSIFILE *driver_file, *setup_file;
4734 UINT r = ERROR_SUCCESS;
4736 static const WCHAR driver_fmt[] = {
4737 'D','r','i','v','e','r','=','%','s',0};
4738 static const WCHAR setup_fmt[] = {
4739 'S','e','t','u','p','=','%','s',0};
4740 static const WCHAR usage_fmt[] = {
4741 'F','i','l','e','U','s','a','g','e','=','1',0};
4743 desc = MSI_RecordGetString(rec, 3);
4745 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4746 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4748 if (!driver_file || !setup_file)
4750 ERR("ODBC Driver entry not found!\n");
4751 return ERROR_FUNCTION_FAILED;
4754 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4755 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4756 lstrlenW(usage_fmt) + 1;
4757 driver = msi_alloc(len * sizeof(WCHAR));
4759 return ERROR_OUTOFMEMORY;
4762 lstrcpyW(ptr, desc);
4763 ptr += lstrlenW(ptr) + 1;
4765 sprintfW(ptr, driver_fmt, driver_file->FileName);
4766 ptr += lstrlenW(ptr) + 1;
4768 sprintfW(ptr, setup_fmt, setup_file->FileName);
4769 ptr += lstrlenW(ptr) + 1;
4771 lstrcpyW(ptr, usage_fmt);
4772 ptr += lstrlenW(ptr) + 1;
4775 driver_path = strdupW(driver_file->TargetPath);
4776 ptr = strrchrW(driver_path, '\\');
4777 if (ptr) *ptr = '\0';
4779 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4780 NULL, ODBC_INSTALL_COMPLETE, &usage))
4782 ERR("Failed to install SQL driver!\n");
4783 r = ERROR_FUNCTION_FAILED;
4787 msi_free(driver_path);
4792 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4794 MSIPACKAGE *package = param;
4795 LPWSTR translator, translator_path, ptr;
4796 WCHAR outpath[MAX_PATH];
4797 MSIFILE *translator_file, *setup_file;
4800 UINT r = ERROR_SUCCESS;
4802 static const WCHAR translator_fmt[] = {
4803 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4804 static const WCHAR setup_fmt[] = {
4805 'S','e','t','u','p','=','%','s',0};
4807 desc = MSI_RecordGetString(rec, 3);
4809 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4810 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4812 if (!translator_file || !setup_file)
4814 ERR("ODBC Translator entry not found!\n");
4815 return ERROR_FUNCTION_FAILED;
4818 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4819 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4820 translator = msi_alloc(len * sizeof(WCHAR));
4822 return ERROR_OUTOFMEMORY;
4825 lstrcpyW(ptr, desc);
4826 ptr += lstrlenW(ptr) + 1;
4828 sprintfW(ptr, translator_fmt, translator_file->FileName);
4829 ptr += lstrlenW(ptr) + 1;
4831 sprintfW(ptr, setup_fmt, setup_file->FileName);
4832 ptr += lstrlenW(ptr) + 1;
4835 translator_path = strdupW(translator_file->TargetPath);
4836 ptr = strrchrW(translator_path, '\\');
4837 if (ptr) *ptr = '\0';
4839 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4840 NULL, ODBC_INSTALL_COMPLETE, &usage))
4842 ERR("Failed to install SQL translator!\n");
4843 r = ERROR_FUNCTION_FAILED;
4846 msi_free(translator);
4847 msi_free(translator_path);
4852 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4855 LPCWSTR desc, driver;
4856 WORD request = ODBC_ADD_SYS_DSN;
4859 UINT r = ERROR_SUCCESS;
4861 static const WCHAR attrs_fmt[] = {
4862 'D','S','N','=','%','s',0 };
4864 desc = MSI_RecordGetString(rec, 3);
4865 driver = MSI_RecordGetString(rec, 4);
4866 registration = MSI_RecordGetInteger(rec, 5);
4868 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4869 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4871 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4872 attrs = msi_alloc(len * sizeof(WCHAR));
4874 return ERROR_OUTOFMEMORY;
4876 sprintfW(attrs, attrs_fmt, desc);
4877 attrs[len - 1] = '\0';
4879 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4881 ERR("Failed to install SQL data source!\n");
4882 r = ERROR_FUNCTION_FAILED;
4890 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4895 static const WCHAR driver_query[] = {
4896 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4897 'O','D','B','C','D','r','i','v','e','r',0 };
4899 static const WCHAR translator_query[] = {
4900 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4901 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4903 static const WCHAR source_query[] = {
4904 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4905 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4907 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4908 if (rc != ERROR_SUCCESS)
4909 return ERROR_SUCCESS;
4911 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4912 msiobj_release(&view->hdr);
4914 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4915 if (rc != ERROR_SUCCESS)
4916 return ERROR_SUCCESS;
4918 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4919 msiobj_release(&view->hdr);
4921 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4922 if (rc != ERROR_SUCCESS)
4923 return ERROR_SUCCESS;
4925 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4926 msiobj_release(&view->hdr);
4931 #define ENV_ACT_SETALWAYS 0x1
4932 #define ENV_ACT_SETABSENT 0x2
4933 #define ENV_ACT_REMOVE 0x4
4934 #define ENV_ACT_REMOVEMATCH 0x8
4936 #define ENV_MOD_MACHINE 0x20000000
4937 #define ENV_MOD_APPEND 0x40000000
4938 #define ENV_MOD_PREFIX 0x80000000
4939 #define ENV_MOD_MASK 0xC0000000
4941 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4943 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4945 LPCWSTR cptr = *name;
4946 LPCWSTR ptr = *value;
4948 static const WCHAR prefix[] = {'[','~',']',0};
4949 static const int prefix_len = 3;
4955 *flags |= ENV_ACT_SETALWAYS;
4956 else if (*cptr == '+')
4957 *flags |= ENV_ACT_SETABSENT;
4958 else if (*cptr == '-')
4959 *flags |= ENV_ACT_REMOVE;
4960 else if (*cptr == '!')
4961 *flags |= ENV_ACT_REMOVEMATCH;
4962 else if (*cptr == '*')
4963 *flags |= ENV_MOD_MACHINE;
4973 ERR("Missing environment variable\n");
4974 return ERROR_FUNCTION_FAILED;
4977 if (!strncmpW(ptr, prefix, prefix_len))
4979 *flags |= ENV_MOD_APPEND;
4980 *value += lstrlenW(prefix);
4982 else if (lstrlenW(*value) >= prefix_len)
4984 ptr += lstrlenW(ptr) - prefix_len;
4985 if (!lstrcmpW(ptr, prefix))
4987 *flags |= ENV_MOD_PREFIX;
4988 /* the "[~]" will be removed by deformat_string */;
4992 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4993 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4994 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4995 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4997 ERR("Invalid flags: %08x\n", *flags);
4998 return ERROR_FUNCTION_FAILED;
5002 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
5004 return ERROR_SUCCESS;
5007 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5009 MSIPACKAGE *package = param;
5010 LPCWSTR name, value;
5011 LPWSTR data = NULL, newval = NULL;
5012 LPWSTR deformatted = NULL, ptr;
5013 DWORD flags, type, size;
5015 HKEY env = NULL, root;
5016 LPCWSTR environment;
5018 static const WCHAR user_env[] =
5019 {'E','n','v','i','r','o','n','m','e','n','t',0};
5020 static const WCHAR machine_env[] =
5021 {'S','y','s','t','e','m','\\',
5022 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5023 'C','o','n','t','r','o','l','\\',
5024 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5025 'E','n','v','i','r','o','n','m','e','n','t',0};
5027 name = MSI_RecordGetString(rec, 2);
5028 value = MSI_RecordGetString(rec, 3);
5030 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
5032 res = env_set_flags(&name, &value, &flags);
5033 if (res != ERROR_SUCCESS)
5036 deformat_string(package, value, &deformatted);
5039 res = ERROR_OUTOFMEMORY;
5043 value = deformatted;
5045 if (flags & ENV_MOD_MACHINE)
5047 environment = machine_env;
5048 root = HKEY_LOCAL_MACHINE;
5052 environment = user_env;
5053 root = HKEY_CURRENT_USER;
5056 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5057 KEY_ALL_ACCESS, NULL, &env, NULL);
5058 if (res != ERROR_SUCCESS)
5061 if (flags & ENV_ACT_REMOVE)
5062 FIXME("Not removing environment variable on uninstall!\n");
5065 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5066 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5067 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5070 if (res != ERROR_FILE_NOT_FOUND)
5072 if (flags & ENV_ACT_SETABSENT)
5074 res = ERROR_SUCCESS;
5078 data = msi_alloc(size);
5082 return ERROR_OUTOFMEMORY;
5085 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5086 if (res != ERROR_SUCCESS)
5089 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5091 res = RegDeleteKeyW(env, name);
5095 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5096 newval = msi_alloc(size);
5100 res = ERROR_OUTOFMEMORY;
5104 if (!(flags & ENV_MOD_MASK))
5105 lstrcpyW(newval, value);
5108 if (flags & ENV_MOD_PREFIX)
5110 lstrcpyW(newval, value);
5111 lstrcatW(newval, szSemiColon);
5112 ptr = newval + lstrlenW(value) + 1;
5115 lstrcpyW(ptr, data);
5117 if (flags & ENV_MOD_APPEND)
5119 lstrcatW(newval, szSemiColon);
5120 lstrcatW(newval, value);
5126 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5127 newval = msi_alloc(size);
5130 res = ERROR_OUTOFMEMORY;
5134 lstrcpyW(newval, value);
5137 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5138 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5141 if (env) RegCloseKey(env);
5142 msi_free(deformatted);
5148 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5152 static const WCHAR ExecSeqQuery[] =
5153 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5154 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5155 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5156 if (rc != ERROR_SUCCESS)
5157 return ERROR_SUCCESS;
5159 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5160 msiobj_release(&view->hdr);
5165 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5176 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5180 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5181 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5183 WARN("Source or dest is directory, not moving\n");
5187 if (options == msidbMoveFileOptionsMove)
5189 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5190 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5193 WARN("MoveFile failed: %d\n", GetLastError());
5199 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5200 ret = CopyFileW(source, dest, FALSE);
5203 WARN("CopyFile failed: %d\n", GetLastError());
5211 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5214 DWORD dirlen, pathlen;
5216 ptr = strrchrW(wildcard, '\\');
5217 dirlen = ptr - wildcard + 1;
5219 pathlen = dirlen + lstrlenW(filename) + 1;
5220 path = msi_alloc(pathlen * sizeof(WCHAR));
5222 lstrcpynW(path, wildcard, dirlen + 1);
5223 lstrcatW(path, filename);
5228 static void free_file_entry(FILE_LIST *file)
5230 msi_free(file->source);
5231 msi_free(file->dest);
5235 static void free_list(FILE_LIST *list)
5237 while (!list_empty(&list->entry))
5239 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5241 list_remove(&file->entry);
5242 free_file_entry(file);
5246 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5248 FILE_LIST *new, *file;
5249 LPWSTR ptr, filename;
5252 new = msi_alloc_zero(sizeof(FILE_LIST));
5256 new->source = strdupW(source);
5257 ptr = strrchrW(dest, '\\') + 1;
5258 filename = strrchrW(new->source, '\\') + 1;
5260 new->sourcename = filename;
5263 new->destname = ptr;
5265 new->destname = new->sourcename;
5267 size = (ptr - dest) + lstrlenW(filename) + 1;
5268 new->dest = msi_alloc(size * sizeof(WCHAR));
5271 free_file_entry(new);
5275 lstrcpynW(new->dest, dest, ptr - dest + 1);
5276 lstrcatW(new->dest, filename);
5278 if (list_empty(&files->entry))
5280 list_add_head(&files->entry, &new->entry);
5284 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5286 if (lstrcmpW(source, file->source) < 0)
5288 list_add_before(&file->entry, &new->entry);
5293 list_add_after(&file->entry, &new->entry);
5297 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5299 WIN32_FIND_DATAW wfd;
5303 FILE_LIST files, *file;
5306 hfile = FindFirstFileW(source, &wfd);
5307 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5309 list_init(&files.entry);
5311 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5313 if (is_dot_dir(wfd.cFileName)) continue;
5315 path = wildcard_to_file(source, wfd.cFileName);
5322 add_wildcard(&files, path, dest);
5326 /* no files match the wildcard */
5327 if (list_empty(&files.entry))
5330 /* only the first wildcard match gets renamed to dest */
5331 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5332 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5333 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5340 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5342 while (!list_empty(&files.entry))
5344 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5346 msi_move_file(file->source, file->dest, options);
5348 list_remove(&file->entry);
5349 free_file_entry(file);
5360 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5362 MSIPACKAGE *package = param;
5365 LPWSTR destname = NULL;
5366 LPWSTR sourcedir = NULL, destdir = NULL;
5367 LPWSTR source = NULL, dest = NULL;
5370 BOOL ret, wildcards;
5372 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5373 if (!comp || !comp->Enabled ||
5374 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5376 TRACE("Component not set for install, not moving file\n");
5377 return ERROR_SUCCESS;
5380 sourcename = MSI_RecordGetString(rec, 3);
5381 options = MSI_RecordGetInteger(rec, 7);
5383 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5387 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5393 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5396 source = strdupW(sourcedir);
5402 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5403 source = msi_alloc(size * sizeof(WCHAR));
5407 lstrcpyW(source, sourcedir);
5408 if (source[lstrlenW(source) - 1] != '\\')
5409 lstrcatW(source, szBackSlash);
5410 lstrcatW(source, sourcename);
5413 wildcards = strchrW(source, '*') || strchrW(source, '?');
5415 if (MSI_RecordIsNull(rec, 4))
5419 destname = strdupW(sourcename);
5426 destname = strdupW(MSI_RecordGetString(rec, 4));
5428 reduce_to_longfilename(destname);
5433 size = lstrlenW(destname);
5435 size += lstrlenW(destdir) + 2;
5436 dest = msi_alloc(size * sizeof(WCHAR));
5440 lstrcpyW(dest, destdir);
5441 if (dest[lstrlenW(dest) - 1] != '\\')
5442 lstrcatW(dest, szBackSlash);
5445 lstrcatW(dest, destname);
5447 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5449 ret = CreateDirectoryW(destdir, NULL);
5452 WARN("CreateDirectory failed: %d\n", GetLastError());
5453 return ERROR_SUCCESS;
5458 msi_move_file(source, dest, options);
5460 move_files_wildcard(source, dest, options);
5463 msi_free(sourcedir);
5469 return ERROR_SUCCESS;
5472 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5477 static const WCHAR ExecSeqQuery[] =
5478 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5479 '`','M','o','v','e','F','i','l','e','`',0};
5481 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5482 if (rc != ERROR_SUCCESS)
5483 return ERROR_SUCCESS;
5485 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5486 msiobj_release(&view->hdr);
5491 typedef struct tagMSIASSEMBLY
5494 MSICOMPONENT *component;
5495 MSIFEATURE *feature;
5503 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5505 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5506 LPVOID pvReserved, HMODULE *phModDll);
5508 static BOOL init_functionpointers(void)
5514 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5516 hmscoree = LoadLibraryA("mscoree.dll");
5519 WARN("mscoree.dll not available\n");
5523 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5524 if (!pLoadLibraryShim)
5526 WARN("LoadLibraryShim not available\n");
5527 FreeLibrary(hmscoree);
5531 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5534 WARN("fusion.dll not available\n");
5535 FreeLibrary(hmscoree);
5539 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5541 FreeLibrary(hmscoree);
5545 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5548 IAssemblyCache *cache;
5550 UINT r = ERROR_FUNCTION_FAILED;
5552 TRACE("installing assembly: %s\n", debugstr_w(path));
5554 if (assembly->feature)
5555 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5557 if (assembly->manifest)
5558 FIXME("Manifest unhandled\n");
5560 if (assembly->application)
5562 FIXME("Assembly should be privately installed\n");
5563 return ERROR_SUCCESS;
5566 if (assembly->attributes == msidbAssemblyAttributesWin32)
5568 FIXME("Win32 assemblies not handled\n");
5569 return ERROR_SUCCESS;
5572 hr = pCreateAssemblyCache(&cache, 0);
5576 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5578 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5583 IAssemblyCache_Release(cache);
5587 typedef struct tagASSEMBLY_LIST
5589 MSIPACKAGE *package;
5590 IAssemblyCache *cache;
5591 struct list *assemblies;
5594 typedef struct tagASSEMBLY_NAME
5602 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
5604 ASSEMBLY_NAME *asmname = param;
5605 LPCWSTR name = MSI_RecordGetString(rec, 2);
5606 LPWSTR val = msi_dup_record_field(rec, 3);
5608 static const WCHAR Name[] = {'N','a','m','e',0};
5609 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
5610 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
5611 static const WCHAR PublicKeyToken[] = {
5612 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
5614 if (!strcmpiW(name, Name))
5615 asmname->name = val;
5616 else if (!strcmpiW(name, Version))
5617 asmname->version = val;
5618 else if (!strcmpiW(name, Culture))
5619 asmname->culture = val;
5620 else if (!strcmpiW(name, PublicKeyToken))
5621 asmname->pubkeytoken = val;
5625 return ERROR_SUCCESS;
5628 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
5632 *size = lstrlenW(append) + 1;
5633 *str = msi_alloc((*size) * sizeof(WCHAR));
5634 lstrcpyW(*str, append);
5638 (*size) += lstrlenW(append);
5639 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
5640 lstrcatW(*str, append);
5643 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
5646 ASSEMBLY_INFO asminfo;
5654 static const WCHAR separator[] = {',',' ',0};
5655 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
5656 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
5657 static const WCHAR PublicKeyToken[] = {
5658 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
5659 static const WCHAR query[] = {
5660 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5661 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
5662 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
5663 '=','\'','%','s','\'',0};
5667 ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
5668 ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
5670 r = MSI_OpenQuery(db, &view, query, comp->Component);
5671 if (r != ERROR_SUCCESS)
5672 return ERROR_SUCCESS;
5674 MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
5675 msiobj_release(&view->hdr);
5679 ERR("No assembly name specified!\n");
5683 append_str(&disp, &size, name.name);
5687 append_str(&disp, &size, separator);
5688 append_str(&disp, &size, Version);
5689 append_str(&disp, &size, name.version);
5694 append_str(&disp, &size, separator);
5695 append_str(&disp, &size, Culture);
5696 append_str(&disp, &size, name.culture);
5699 if (name.pubkeytoken)
5701 append_str(&disp, &size, separator);
5702 append_str(&disp, &size, PublicKeyToken);
5703 append_str(&disp, &size, name.pubkeytoken);
5706 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
5707 IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
5709 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
5713 msi_free(name.name);
5714 msi_free(name.version);
5715 msi_free(name.culture);
5716 msi_free(name.pubkeytoken);
5721 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
5723 ASSEMBLY_LIST *list = param;
5724 MSIASSEMBLY *assembly;
5726 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
5728 return ERROR_OUTOFMEMORY;
5730 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
5732 if (!assembly->component || !assembly->component->Enabled ||
5733 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5735 TRACE("Component not set for install, not publishing assembly\n");
5737 return ERROR_SUCCESS;
5740 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
5741 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
5743 if (!assembly->file)
5745 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
5746 return ERROR_FUNCTION_FAILED;
5749 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
5750 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
5751 assembly->attributes = MSI_RecordGetInteger(rec, 5);
5753 if (assembly->application)
5756 DWORD size = sizeof(version)/sizeof(WCHAR);
5758 /* FIXME: we should probably check the manifest file here */
5760 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
5761 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
5763 assembly->installed = TRUE;
5767 assembly->installed = check_assembly_installed(list->package->db,
5769 assembly->component);
5771 list_add_head(list->assemblies, &assembly->entry);
5772 return ERROR_SUCCESS;
5775 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
5777 IAssemblyCache *cache = NULL;
5783 static const WCHAR query[] =
5784 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5785 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5787 r = MSI_DatabaseOpenViewW(package->db, query, &view);
5788 if (r != ERROR_SUCCESS)
5789 return ERROR_SUCCESS;
5791 hr = pCreateAssemblyCache(&cache, 0);
5793 return ERROR_FUNCTION_FAILED;
5795 list.package = package;
5797 list.assemblies = assemblies;
5799 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
5800 msiobj_release(&view->hdr);
5802 IAssemblyCache_Release(cache);
5807 static void free_assemblies(struct list *assemblies)
5809 struct list *item, *cursor;
5811 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
5813 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
5815 list_remove(&assembly->entry);
5816 msi_free(assembly->application);
5817 msi_free(assembly->manifest);
5822 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
5824 MSIASSEMBLY *assembly;
5826 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
5828 if (!lstrcmpW(assembly->file->File, file))
5838 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
5839 LPWSTR *path, DWORD *attrs, PVOID user)
5841 MSIASSEMBLY *assembly;
5842 WCHAR temppath[MAX_PATH];
5843 struct list *assemblies = user;
5846 if (!find_assembly(assemblies, file, &assembly))
5849 GetTempPathW(MAX_PATH, temppath);
5850 PathAddBackslashW(temppath);
5851 lstrcatW(temppath, assembly->file->FileName);
5853 if (action == MSICABEXTRACT_BEGINEXTRACT)
5855 if (assembly->installed)
5858 *path = strdupW(temppath);
5859 *attrs = assembly->file->Attributes;
5861 else if (action == MSICABEXTRACT_FILEEXTRACTED)
5863 assembly->installed = TRUE;
5865 r = install_assembly(package, assembly, temppath);
5866 if (r != ERROR_SUCCESS)
5867 ERR("Failed to install assembly\n");
5873 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5876 struct list assemblies = LIST_INIT(assemblies);
5877 MSIASSEMBLY *assembly;
5880 if (!init_functionpointers() || !pCreateAssemblyCache)
5881 return ERROR_FUNCTION_FAILED;
5883 r = load_assemblies(package, &assemblies);
5884 if (r != ERROR_SUCCESS)
5887 if (list_empty(&assemblies))
5890 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
5893 r = ERROR_OUTOFMEMORY;
5897 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
5899 if (assembly->installed && !mi->is_continuous)
5902 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
5903 (assembly->file->IsCompressed && !mi->is_extracted))
5907 r = ready_media(package, assembly->file, mi);
5908 if (r != ERROR_SUCCESS)
5910 ERR("Failed to ready media\n");
5915 data.package = package;
5916 data.cb = installassembly_cb;
5917 data.user = &assemblies;
5919 if (assembly->file->IsCompressed &&
5920 !msi_cabextract(package, mi, &data))
5922 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
5923 r = ERROR_FUNCTION_FAILED;
5928 if (!assembly->file->IsCompressed)
5930 LPWSTR source = resolve_file_source(package, assembly->file);
5932 r = install_assembly(package, assembly, source);
5933 if (r != ERROR_SUCCESS)
5934 ERR("Failed to install assembly\n");
5939 /* FIXME: write Installer assembly reg values */
5943 free_assemblies(&assemblies);
5947 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5948 LPCSTR action, LPCWSTR table )
5950 static const WCHAR query[] = {
5951 'S','E','L','E','C','T',' ','*',' ',
5952 'F','R','O','M',' ','`','%','s','`',0 };
5953 MSIQUERY *view = NULL;
5957 r = MSI_OpenQuery( package->db, &view, query, table );
5958 if (r == ERROR_SUCCESS)
5960 r = MSI_IterateRecords(view, &count, NULL, package);
5961 msiobj_release(&view->hdr);
5965 FIXME("%s -> %u ignored %s table values\n",
5966 action, count, debugstr_w(table));
5968 return ERROR_SUCCESS;
5971 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5973 TRACE("%p\n", package);
5974 return ERROR_SUCCESS;
5977 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5979 static const WCHAR table[] =
5980 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5981 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5984 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5986 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5987 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5990 static UINT ACTION_BindImage( MSIPACKAGE *package )
5992 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5993 return msi_unimplemented_action_stub( package, "BindImage", table );
5996 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5998 static const WCHAR table[] = {
5999 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6000 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6003 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6005 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6006 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6009 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
6011 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
6012 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
6015 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6017 static const WCHAR table[] = {
6018 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6019 return msi_unimplemented_action_stub( package, "DeleteServices", table );
6021 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6023 static const WCHAR table[] = {
6024 'P','r','o','d','u','c','t','I','D',0 };
6025 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6028 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6030 static const WCHAR table[] = {
6031 'E','n','v','i','r','o','n','m','e','n','t',0 };
6032 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6035 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6037 static const WCHAR table[] = {
6038 'M','s','i','A','s','s','e','m','b','l','y',0 };
6039 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6042 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6044 static const WCHAR table[] = { 'F','o','n','t',0 };
6045 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6048 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6050 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6051 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6054 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6056 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6057 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6060 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6062 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6063 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6066 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6068 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6069 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6072 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6074 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6075 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6078 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6080 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6081 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6084 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
6086 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
6087 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
6090 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6092 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6093 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6096 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6098 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6099 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6102 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6104 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6105 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6108 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6110 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6111 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6114 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6116 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6117 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6120 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6122 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6123 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6126 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6128 static const WCHAR table[] = { 'M','I','M','E',0 };
6129 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6132 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6134 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6135 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6138 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6140 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6141 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6144 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
6148 const WCHAR *action;
6149 UINT (*handler)(MSIPACKAGE *);
6153 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6154 { szAppSearch, ACTION_AppSearch },
6155 { szBindImage, ACTION_BindImage },
6156 { szCCPSearch, ACTION_CCPSearch },
6157 { szCostFinalize, ACTION_CostFinalize },
6158 { szCostInitialize, ACTION_CostInitialize },
6159 { szCreateFolders, ACTION_CreateFolders },
6160 { szCreateShortcuts, ACTION_CreateShortcuts },
6161 { szDeleteServices, ACTION_DeleteServices },
6162 { szDisableRollback, NULL },
6163 { szDuplicateFiles, ACTION_DuplicateFiles },
6164 { szExecuteAction, ACTION_ExecuteAction },
6165 { szFileCost, ACTION_FileCost },
6166 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6167 { szForceReboot, ACTION_ForceReboot },
6168 { szInstallAdminPackage, NULL },
6169 { szInstallExecute, ACTION_InstallExecute },
6170 { szInstallExecuteAgain, ACTION_InstallExecute },
6171 { szInstallFiles, ACTION_InstallFiles},
6172 { szInstallFinalize, ACTION_InstallFinalize },
6173 { szInstallInitialize, ACTION_InstallInitialize },
6174 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6175 { szInstallValidate, ACTION_InstallValidate },
6176 { szIsolateComponents, ACTION_IsolateComponents },
6177 { szLaunchConditions, ACTION_LaunchConditions },
6178 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6179 { szMoveFiles, ACTION_MoveFiles },
6180 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6181 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6182 { szInstallODBC, ACTION_InstallODBC },
6183 { szInstallServices, ACTION_InstallServices },
6184 { szPatchFiles, ACTION_PatchFiles },
6185 { szProcessComponents, ACTION_ProcessComponents },
6186 { szPublishComponents, ACTION_PublishComponents },
6187 { szPublishFeatures, ACTION_PublishFeatures },
6188 { szPublishProduct, ACTION_PublishProduct },
6189 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6190 { szRegisterComPlus, ACTION_RegisterComPlus},
6191 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6192 { szRegisterFonts, ACTION_RegisterFonts },
6193 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6194 { szRegisterProduct, ACTION_RegisterProduct },
6195 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6196 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6197 { szRegisterUser, ACTION_RegisterUser },
6198 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6199 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6200 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6201 { szRemoveFiles, ACTION_RemoveFiles },
6202 { szRemoveFolders, ACTION_RemoveFolders },
6203 { szRemoveIniValues, ACTION_RemoveIniValues },
6204 { szRemoveODBC, ACTION_RemoveODBC },
6205 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6206 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6207 { szResolveSource, ACTION_ResolveSource },
6208 { szRMCCPSearch, ACTION_RMCCPSearch },
6209 { szScheduleReboot, NULL },
6210 { szSelfRegModules, ACTION_SelfRegModules },
6211 { szSelfUnregModules, ACTION_SelfUnregModules },
6212 { szSetODBCFolders, NULL },
6213 { szStartServices, ACTION_StartServices },
6214 { szStopServices, ACTION_StopServices },
6215 { szUnpublishComponents, ACTION_UnpublishComponents },
6216 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6217 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6218 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6219 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6220 { szUnregisterFonts, ACTION_UnregisterFonts },
6221 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6222 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6223 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6224 { szValidateProductID, ACTION_ValidateProductID },
6225 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6226 { szWriteIniValues, ACTION_WriteIniValues },
6227 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6231 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6232 UINT* rc, BOOL force )
6238 if (!run && !package->script->CurrentlyScripting)
6243 if (strcmpW(action,szInstallFinalize) == 0 ||
6244 strcmpW(action,szInstallExecute) == 0 ||
6245 strcmpW(action,szInstallExecuteAgain) == 0)
6250 while (StandardActions[i].action != NULL)
6252 if (strcmpW(StandardActions[i].action, action)==0)
6256 ui_actioninfo(package, action, TRUE, 0);
6257 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6258 ui_actioninfo(package, action, FALSE, *rc);
6262 ui_actionstart(package, action);
6263 if (StandardActions[i].handler)
6265 *rc = StandardActions[i].handler(package);
6269 FIXME("unhandled standard action %s\n",debugstr_w(action));
6270 *rc = ERROR_SUCCESS;
6281 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
6283 UINT rc = ERROR_SUCCESS;
6286 TRACE("Performing action (%s)\n", debugstr_w(action));
6288 handled = ACTION_HandleStandardAction(package, action, &rc, force);
6291 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
6295 WARN("unhandled msi action %s\n", debugstr_w(action));
6296 rc = ERROR_FUNCTION_NOT_CALLED;
6302 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
6304 UINT rc = ERROR_SUCCESS;
6305 BOOL handled = FALSE;
6307 TRACE("Performing action (%s)\n", debugstr_w(action));
6309 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
6312 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
6314 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
6319 WARN("unhandled msi action %s\n", debugstr_w(action));
6320 rc = ERROR_FUNCTION_NOT_CALLED;
6326 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
6328 UINT rc = ERROR_SUCCESS;
6331 static const WCHAR ExecSeqQuery[] =
6332 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6333 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
6334 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
6335 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
6336 static const WCHAR UISeqQuery[] =
6337 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6338 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
6339 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
6340 ' ', '=',' ','%','i',0};
6342 if (needs_ui_sequence(package))
6343 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
6345 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
6349 LPCWSTR action, cond;
6351 TRACE("Running the actions\n");
6353 /* check conditions */
6354 cond = MSI_RecordGetString(row, 2);
6356 /* this is a hack to skip errors in the condition code */
6357 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
6359 msiobj_release(&row->hdr);
6360 return ERROR_SUCCESS;
6363 action = MSI_RecordGetString(row, 1);
6366 ERR("failed to fetch action\n");
6367 msiobj_release(&row->hdr);
6368 return ERROR_FUNCTION_FAILED;
6371 if (needs_ui_sequence(package))
6372 rc = ACTION_PerformUIAction(package, action, -1);
6374 rc = ACTION_PerformAction(package, action, -1, FALSE);
6376 msiobj_release(&row->hdr);
6382 /****************************************************
6383 * TOP level entry points
6384 *****************************************************/
6386 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
6387 LPCWSTR szCommandLine )
6392 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
6393 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
6395 MSI_SetPropertyW(package, szAction, szInstall);
6397 package->script->InWhatSequence = SEQUENCE_INSTALL;
6404 dir = strdupW(szPackagePath);
6405 p = strrchrW(dir, '\\');
6409 file = szPackagePath + (p - dir);
6414 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
6415 GetCurrentDirectoryW(MAX_PATH, dir);
6416 lstrcatW(dir, szBackSlash);
6417 file = szPackagePath;
6420 msi_free( package->PackagePath );
6421 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
6422 if (!package->PackagePath)
6425 return ERROR_OUTOFMEMORY;
6428 lstrcpyW(package->PackagePath, dir);
6429 lstrcatW(package->PackagePath, file);
6432 msi_set_sourcedir_props(package, FALSE);
6435 msi_parse_command_line( package, szCommandLine, FALSE );
6437 msi_apply_transforms( package );
6438 msi_apply_patches( package );
6440 if (!szCommandLine && msi_get_property_int( package, szInstalled, 0 ))
6442 TRACE("setting reinstall property\n");
6443 MSI_SetPropertyW( package, szReinstall, szAll );
6446 /* properties may have been added by a transform */
6447 msi_clone_properties( package );
6448 msi_set_context( package );
6450 if (needs_ui_sequence( package))
6452 package->script->InWhatSequence |= SEQUENCE_UI;
6453 rc = ACTION_ProcessUISequence(package);
6454 ui_exists = ui_sequence_exists(package);
6455 if (rc == ERROR_SUCCESS || !ui_exists)
6457 package->script->InWhatSequence |= SEQUENCE_EXEC;
6458 rc = ACTION_ProcessExecSequence(package, ui_exists);
6462 rc = ACTION_ProcessExecSequence(package, FALSE);
6464 package->script->CurrentlyScripting = FALSE;
6466 /* process the ending type action */
6467 if (rc == ERROR_SUCCESS)
6468 ACTION_PerformActionSequence(package, -1);
6469 else if (rc == ERROR_INSTALL_USEREXIT)
6470 ACTION_PerformActionSequence(package, -2);
6471 else if (rc == ERROR_INSTALL_SUSPEND)
6472 ACTION_PerformActionSequence(package, -4);
6474 ACTION_PerformActionSequence(package, -3);
6476 /* finish up running custom actions */
6477 ACTION_FinishCustomActions(package);
6479 if (rc == ERROR_SUCCESS && package->need_reboot)
6480 return ERROR_SUCCESS_REBOOT_REQUIRED;