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 szRemoveEnvironmentStrings[] =
138 {'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};
139 static const WCHAR szRemoveExistingProducts[] =
140 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
141 static const WCHAR szRemoveFolders[] =
142 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
143 static const WCHAR szRemoveIniValues[] =
144 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
145 static const WCHAR szRemoveODBC[] =
146 {'R','e','m','o','v','e','O','D','B','C',0};
147 static const WCHAR szRemoveRegistryValues[] =
148 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
149 static const WCHAR szRemoveShortcuts[] =
150 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
151 static const WCHAR szRMCCPSearch[] =
152 {'R','M','C','C','P','S','e','a','r','c','h',0};
153 static const WCHAR szScheduleReboot[] =
154 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
155 static const WCHAR szSelfUnregModules[] =
156 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
157 static const WCHAR szSetODBCFolders[] =
158 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
159 static const WCHAR szStartServices[] =
160 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
161 static const WCHAR szStopServices[] =
162 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
163 static const WCHAR szUnpublishComponents[] =
164 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
165 static const WCHAR szUnpublishFeatures[] =
166 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
167 static const WCHAR szUnregisterClassInfo[] =
168 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
169 static const WCHAR szUnregisterComPlus[] =
170 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
171 static const WCHAR szUnregisterExtensionInfo[] =
172 {'U','n','r','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n','I','n','f','o',0};
173 static const WCHAR szUnregisterFonts[] =
174 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
175 static const WCHAR szUnregisterMIMEInfo[] =
176 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
177 static const WCHAR szUnregisterProgIdInfo[] =
178 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
179 static const WCHAR szUnregisterTypeLibraries[] =
180 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
181 static const WCHAR szValidateProductID[] =
182 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
183 static const WCHAR szWriteEnvironmentStrings[] =
184 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
186 /********************************************************
188 ********************************************************/
190 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
192 static const WCHAR Query_t[] =
193 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
194 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
195 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
196 ' ','\'','%','s','\'',0};
199 row = MSI_QueryGetRecord( package->db, Query_t, action );
202 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
203 msiobj_release(&row->hdr);
206 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
210 static const WCHAR template_s[]=
211 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
213 static const WCHAR template_e[]=
214 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
215 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
217 static const WCHAR format[] =
218 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
222 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
224 sprintfW(message,template_s,timet,action);
226 sprintfW(message,template_e,timet,action,rc);
228 row = MSI_CreateRecord(1);
229 MSI_RecordSetStringW(row,1,message);
231 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
232 msiobj_release(&row->hdr);
235 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
241 LPWSTR prop = NULL, val = NULL;
244 return ERROR_SUCCESS;
256 TRACE("Looking at %s\n",debugstr_w(ptr));
258 ptr2 = strchrW(ptr,'=');
261 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
268 prop = msi_alloc((len+1)*sizeof(WCHAR));
269 memcpy(prop,ptr,len*sizeof(WCHAR));
279 while (*ptr && (quote || (!quote && *ptr!=' ')))
292 val = msi_alloc((len+1)*sizeof(WCHAR));
293 memcpy(val,ptr2,len*sizeof(WCHAR));
296 if (lstrlenW(prop) > 0)
298 TRACE("Found commandline property (%s) = (%s)\n",
299 debugstr_w(prop), debugstr_w(val));
300 MSI_SetPropertyW(package,prop,val);
306 return ERROR_SUCCESS;
310 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
313 LPWSTR p, *ret = NULL;
319 /* count the number of substrings */
320 for ( pc = str, count = 0; pc; count++ )
322 pc = strchrW( pc, sep );
327 /* allocate space for an array of substring pointers and the substrings */
328 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
329 (lstrlenW(str)+1) * sizeof(WCHAR) );
333 /* copy the string and set the pointers */
334 p = (LPWSTR) &ret[count+1];
336 for( count = 0; (ret[count] = p); count++ )
338 p = strchrW( p, sep );
346 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
348 static const WCHAR szSystemLanguageID[] =
349 { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
351 LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
352 UINT ret = ERROR_FUNCTION_FAILED;
354 prod_code = msi_dup_property( package, szProductCode );
355 patch_product = msi_get_suminfo_product( patch );
357 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
359 if ( strstrW( patch_product, prod_code ) )
364 si = MSI_GetSummaryInformationW( patch, 0 );
367 ERR("no summary information!\n");
371 template = msi_suminfo_dup_string( si, PID_TEMPLATE );
374 ERR("no template property!\n");
375 msiobj_release( &si->hdr );
382 msiobj_release( &si->hdr );
386 langid = msi_dup_property( package, szSystemLanguageID );
389 msiobj_release( &si->hdr );
393 p = strchrW( template, ';' );
394 if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
396 TRACE("applicable transform\n");
400 /* FIXME: check platform */
402 msiobj_release( &si->hdr );
406 msi_free( patch_product );
407 msi_free( prod_code );
408 msi_free( template );
414 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
415 MSIDATABASE *patch_db, LPCWSTR name )
417 UINT ret = ERROR_FUNCTION_FAILED;
418 IStorage *stg = NULL;
421 TRACE("%p %s\n", package, debugstr_w(name) );
425 ERR("expected a colon in %s\n", debugstr_w(name));
426 return ERROR_FUNCTION_FAILED;
429 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
432 ret = msi_check_transform_applicable( package, stg );
433 if (ret == ERROR_SUCCESS)
434 msi_table_apply_transform( package->db, stg );
436 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
437 IStorage_Release( stg );
440 ERR("failed to open substorage %s\n", debugstr_w(name));
442 return ERROR_SUCCESS;
445 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
447 LPWSTR guid_list, *guids, product_code;
448 UINT i, ret = ERROR_FUNCTION_FAILED;
450 product_code = msi_dup_property( package, szProductCode );
453 /* FIXME: the property ProductCode should be written into the DB somewhere */
454 ERR("no product code to check\n");
455 return ERROR_SUCCESS;
458 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
459 guids = msi_split_string( guid_list, ';' );
460 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
462 if (!lstrcmpW( guids[i], product_code ))
466 msi_free( guid_list );
467 msi_free( product_code );
472 static UINT msi_set_media_source_prop(MSIPACKAGE *package)
475 MSIRECORD *rec = NULL;
480 static const WCHAR query[] = {'S','E','L','E','C','T',' ',
481 '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
482 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
483 '`','S','o','u','r','c','e','`',' ','I','S',' ',
484 'N','O','T',' ','N','U','L','L',0};
486 r = MSI_DatabaseOpenViewW(package->db, query, &view);
487 if (r != ERROR_SUCCESS)
490 r = MSI_ViewExecute(view, 0);
491 if (r != ERROR_SUCCESS)
494 if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
496 prop = MSI_RecordGetString(rec, 1);
497 patch = msi_dup_property(package, szPatch);
498 MSI_SetPropertyW(package, prop, patch);
503 if (rec) msiobj_release(&rec->hdr);
504 msiobj_release(&view->hdr);
509 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
512 LPWSTR str, *substorage;
513 UINT i, r = ERROR_SUCCESS;
515 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
517 return ERROR_FUNCTION_FAILED;
519 if (msi_check_patch_applicable( package, si ) != ERROR_SUCCESS)
521 TRACE("Patch not applicable\n");
522 return ERROR_SUCCESS;
525 package->patch = msi_alloc(sizeof(MSIPATCHINFO));
527 return ERROR_OUTOFMEMORY;
529 package->patch->patchcode = msi_suminfo_dup_string(si, PID_REVNUMBER);
530 if (!package->patch->patchcode)
531 return ERROR_OUTOFMEMORY;
533 /* enumerate the substorage */
534 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
535 package->patch->transforms = str;
537 substorage = msi_split_string( str, ';' );
538 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
539 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
541 msi_free( substorage );
542 msiobj_release( &si->hdr );
544 msi_set_media_source_prop(package);
549 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
551 MSIDATABASE *patch_db = NULL;
554 TRACE("%p %s\n", package, debugstr_w( file ) );
557 * We probably want to make sure we only open a patch collection here.
558 * Patch collections (.msp) and databases (.msi) have different GUIDs
559 * but currently MSI_OpenDatabaseW will accept both.
561 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
562 if ( r != ERROR_SUCCESS )
564 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
568 msi_parse_patch_summary( package, patch_db );
571 * There might be a CAB file in the patch package,
572 * so append it to the list of storage to search for streams.
574 append_storage_to_db( package->db, patch_db->storage );
576 msiobj_release( &patch_db->hdr );
578 return ERROR_SUCCESS;
581 /* get the PATCH property, and apply all the patches it specifies */
582 static UINT msi_apply_patches( MSIPACKAGE *package )
584 LPWSTR patch_list, *patches;
585 UINT i, r = ERROR_SUCCESS;
587 patch_list = msi_dup_property( package, szPatch );
589 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
591 patches = msi_split_string( patch_list, ';' );
592 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
593 r = msi_apply_patch_package( package, patches[i] );
596 msi_free( patch_list );
601 static UINT msi_apply_transforms( MSIPACKAGE *package )
603 static const WCHAR szTransforms[] = {
604 'T','R','A','N','S','F','O','R','M','S',0 };
605 LPWSTR xform_list, *xforms;
606 UINT i, r = ERROR_SUCCESS;
608 xform_list = msi_dup_property( package, szTransforms );
609 xforms = msi_split_string( xform_list, ';' );
611 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
613 if (xforms[i][0] == ':')
614 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
616 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
620 msi_free( xform_list );
625 static BOOL ui_sequence_exists( MSIPACKAGE *package )
630 static const WCHAR ExecSeqQuery [] =
631 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
632 '`','I','n','s','t','a','l','l',
633 'U','I','S','e','q','u','e','n','c','e','`',
634 ' ','W','H','E','R','E',' ',
635 '`','S','e','q','u','e','n','c','e','`',' ',
636 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
637 '`','S','e','q','u','e','n','c','e','`',0};
639 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
640 if (rc == ERROR_SUCCESS)
642 msiobj_release(&view->hdr);
649 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
652 LPWSTR source, check;
655 static const WCHAR szOriginalDatabase[] =
656 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
658 db = msi_dup_property( package, szOriginalDatabase );
660 return ERROR_OUTOFMEMORY;
662 p = strrchrW( db, '\\' );
665 p = strrchrW( db, '/' );
669 return ERROR_SUCCESS;
674 source = msi_alloc( len * sizeof(WCHAR) );
675 lstrcpynW( source, db, len );
677 check = msi_dup_property( package, cszSourceDir );
678 if (!check || replace)
679 MSI_SetPropertyW( package, cszSourceDir, source );
683 check = msi_dup_property( package, cszSOURCEDIR );
684 if (!check || replace)
685 MSI_SetPropertyW( package, cszSOURCEDIR, source );
691 return ERROR_SUCCESS;
694 static BOOL needs_ui_sequence(MSIPACKAGE *package)
696 INT level = msi_get_property_int(package, szUILevel, 0);
697 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
700 static UINT msi_set_context(MSIPACKAGE *package)
707 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
709 r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
710 if (r == ERROR_SUCCESS)
713 if (num == 1 || num == 2)
714 package->Context = MSIINSTALLCONTEXT_MACHINE;
717 return ERROR_SUCCESS;
720 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
723 LPCWSTR cond, action;
724 MSIPACKAGE *package = param;
726 action = MSI_RecordGetString(row,1);
729 ERR("Error is retrieving action name\n");
730 return ERROR_FUNCTION_FAILED;
733 /* check conditions */
734 cond = MSI_RecordGetString(row,2);
736 /* this is a hack to skip errors in the condition code */
737 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
739 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
740 return ERROR_SUCCESS;
743 if (needs_ui_sequence(package))
744 rc = ACTION_PerformUIAction(package, action, -1);
746 rc = ACTION_PerformAction(package, action, -1, FALSE);
748 msi_dialog_check_messages( NULL );
750 if (package->CurrentInstallState != ERROR_SUCCESS)
751 rc = package->CurrentInstallState;
753 if (rc == ERROR_FUNCTION_NOT_CALLED)
756 if (rc != ERROR_SUCCESS)
757 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
762 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
766 static const WCHAR query[] =
767 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
769 ' ','W','H','E','R','E',' ',
770 '`','S','e','q','u','e','n','c','e','`',' ',
771 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
772 '`','S','e','q','u','e','n','c','e','`',0};
774 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
776 r = MSI_OpenQuery( package->db, &view, query, szTable );
777 if (r == ERROR_SUCCESS)
779 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
780 msiobj_release(&view->hdr);
786 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
790 static const WCHAR ExecSeqQuery[] =
791 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
792 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
793 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
794 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
795 'O','R','D','E','R',' ', 'B','Y',' ',
796 '`','S','e','q','u','e','n','c','e','`',0 };
797 static const WCHAR IVQuery[] =
798 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
799 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
800 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
801 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
802 ' ','\'', 'I','n','s','t','a','l','l',
803 'V','a','l','i','d','a','t','e','\'', 0};
806 if (package->script->ExecuteSequenceRun)
808 TRACE("Execute Sequence already Run\n");
809 return ERROR_SUCCESS;
812 package->script->ExecuteSequenceRun = TRUE;
814 /* get the sequence number */
817 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
819 return ERROR_FUNCTION_FAILED;
820 seq = MSI_RecordGetInteger(row,1);
821 msiobj_release(&row->hdr);
824 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
825 if (rc == ERROR_SUCCESS)
827 TRACE("Running the actions\n");
829 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
830 msiobj_release(&view->hdr);
836 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
840 static const WCHAR ExecSeqQuery [] =
841 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
842 '`','I','n','s','t','a','l','l',
843 'U','I','S','e','q','u','e','n','c','e','`',
844 ' ','W','H','E','R','E',' ',
845 '`','S','e','q','u','e','n','c','e','`',' ',
846 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
847 '`','S','e','q','u','e','n','c','e','`',0};
849 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
850 if (rc == ERROR_SUCCESS)
852 TRACE("Running the actions\n");
854 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
855 msiobj_release(&view->hdr);
861 /********************************************************
862 * ACTION helper functions and functions that perform the actions
863 *******************************************************/
864 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
865 UINT* rc, UINT script, BOOL force )
870 arc = ACTION_CustomAction(package, action, script, force);
872 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
881 * Actual Action Handlers
884 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
886 MSIPACKAGE *package = param;
887 LPCWSTR dir, component;
893 component = MSI_RecordGetString(row, 2);
894 comp = get_loaded_component(package, component);
896 return ERROR_SUCCESS;
898 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
900 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
901 comp->Action = comp->Installed;
902 return ERROR_SUCCESS;
904 comp->Action = INSTALLSTATE_LOCAL;
906 dir = MSI_RecordGetString(row,1);
909 ERR("Unable to get folder id\n");
910 return ERROR_SUCCESS;
913 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
916 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
917 return ERROR_SUCCESS;
920 TRACE("Folder is %s\n",debugstr_w(full_path));
923 uirow = MSI_CreateRecord(1);
924 MSI_RecordSetStringW(uirow,1,full_path);
925 ui_actiondata(package,szCreateFolders,uirow);
926 msiobj_release( &uirow->hdr );
928 if (folder->State == 0)
929 create_full_pathW(full_path);
934 return ERROR_SUCCESS;
937 /* FIXME: probably should merge this with the above function */
938 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
940 UINT rc = ERROR_SUCCESS;
944 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
946 return ERROR_FUNCTION_FAILED;
948 /* create the path */
949 if (folder->State == 0)
951 create_full_pathW(install_path);
954 msi_free(install_path);
959 UINT msi_create_component_directories( MSIPACKAGE *package )
963 /* create all the folders required by the components are going to install */
964 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
966 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
968 msi_create_directory( package, comp->Directory );
971 return ERROR_SUCCESS;
974 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
976 static const WCHAR ExecSeqQuery[] =
977 {'S','E','L','E','C','T',' ',
978 '`','D','i','r','e','c','t','o','r','y','_','`',
979 ' ','F','R','O','M',' ',
980 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
984 /* create all the empty folders specified in the CreateFolder table */
985 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
986 if (rc != ERROR_SUCCESS)
987 return ERROR_SUCCESS;
989 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
990 msiobj_release(&view->hdr);
995 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
997 MSIPACKAGE *package = param;
998 LPCWSTR dir, component;
1004 component = MSI_RecordGetString(row, 2);
1005 comp = get_loaded_component(package, component);
1007 return ERROR_SUCCESS;
1009 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
1011 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
1012 comp->Action = comp->Installed;
1013 return ERROR_SUCCESS;
1015 comp->Action = INSTALLSTATE_ABSENT;
1017 dir = MSI_RecordGetString( row, 1 );
1020 ERR("Unable to get folder id\n");
1021 return ERROR_SUCCESS;
1024 full_path = resolve_folder( package, dir, FALSE, FALSE, TRUE, &folder );
1027 ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1028 return ERROR_SUCCESS;
1031 TRACE("folder is %s\n", debugstr_w(full_path));
1033 uirow = MSI_CreateRecord( 1 );
1034 MSI_RecordSetStringW( uirow, 1, full_path );
1035 ui_actiondata( package, szRemoveFolders, uirow );
1036 msiobj_release( &uirow->hdr );
1038 RemoveDirectoryW( full_path );
1041 msi_free( full_path );
1042 return ERROR_SUCCESS;
1045 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1047 static const WCHAR query[] =
1048 {'S','E','L','E','C','T',' ', '`','D','i','r','e','c','t','o','r','y','_','`',
1049 ' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1054 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1055 if (rc != ERROR_SUCCESS)
1056 return ERROR_SUCCESS;
1058 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1059 msiobj_release( &view->hdr );
1064 static UINT load_component( MSIRECORD *row, LPVOID param )
1066 MSIPACKAGE *package = param;
1069 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1071 return ERROR_FUNCTION_FAILED;
1073 list_add_tail( &package->components, &comp->entry );
1075 /* fill in the data */
1076 comp->Component = msi_dup_record_field( row, 1 );
1078 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1080 comp->ComponentId = msi_dup_record_field( row, 2 );
1081 comp->Directory = msi_dup_record_field( row, 3 );
1082 comp->Attributes = MSI_RecordGetInteger(row,4);
1083 comp->Condition = msi_dup_record_field( row, 5 );
1084 comp->KeyPath = msi_dup_record_field( row, 6 );
1086 comp->Installed = INSTALLSTATE_UNKNOWN;
1087 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1089 return ERROR_SUCCESS;
1092 static UINT load_all_components( MSIPACKAGE *package )
1094 static const WCHAR query[] = {
1095 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1096 '`','C','o','m','p','o','n','e','n','t','`',0 };
1100 if (!list_empty(&package->components))
1101 return ERROR_SUCCESS;
1103 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1104 if (r != ERROR_SUCCESS)
1107 r = MSI_IterateRecords(view, NULL, load_component, package);
1108 msiobj_release(&view->hdr);
1113 MSIPACKAGE *package;
1114 MSIFEATURE *feature;
1117 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1121 cl = msi_alloc( sizeof (*cl) );
1123 return ERROR_NOT_ENOUGH_MEMORY;
1124 cl->component = comp;
1125 list_add_tail( &feature->Components, &cl->entry );
1127 return ERROR_SUCCESS;
1130 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1134 fl = msi_alloc( sizeof(*fl) );
1136 return ERROR_NOT_ENOUGH_MEMORY;
1137 fl->feature = child;
1138 list_add_tail( &parent->Children, &fl->entry );
1140 return ERROR_SUCCESS;
1143 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1145 _ilfs* ilfs = param;
1149 component = MSI_RecordGetString(row,1);
1151 /* check to see if the component is already loaded */
1152 comp = get_loaded_component( ilfs->package, component );
1155 ERR("unknown component %s\n", debugstr_w(component));
1156 return ERROR_FUNCTION_FAILED;
1159 add_feature_component( ilfs->feature, comp );
1160 comp->Enabled = TRUE;
1162 return ERROR_SUCCESS;
1165 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1167 MSIFEATURE *feature;
1172 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1174 if ( !lstrcmpW( feature->Feature, name ) )
1181 static UINT load_feature(MSIRECORD * row, LPVOID param)
1183 MSIPACKAGE* package = param;
1184 MSIFEATURE* feature;
1185 static const WCHAR Query1[] =
1186 {'S','E','L','E','C','T',' ',
1187 '`','C','o','m','p','o','n','e','n','t','_','`',
1188 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1189 'C','o','m','p','o','n','e','n','t','s','`',' ',
1190 'W','H','E','R','E',' ',
1191 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1196 /* fill in the data */
1198 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1200 return ERROR_NOT_ENOUGH_MEMORY;
1202 list_init( &feature->Children );
1203 list_init( &feature->Components );
1205 feature->Feature = msi_dup_record_field( row, 1 );
1207 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1209 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1210 feature->Title = msi_dup_record_field( row, 3 );
1211 feature->Description = msi_dup_record_field( row, 4 );
1213 if (!MSI_RecordIsNull(row,5))
1214 feature->Display = MSI_RecordGetInteger(row,5);
1216 feature->Level= MSI_RecordGetInteger(row,6);
1217 feature->Directory = msi_dup_record_field( row, 7 );
1218 feature->Attributes = MSI_RecordGetInteger(row,8);
1220 feature->Installed = INSTALLSTATE_UNKNOWN;
1221 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1223 list_add_tail( &package->features, &feature->entry );
1225 /* load feature components */
1227 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1228 if (rc != ERROR_SUCCESS)
1229 return ERROR_SUCCESS;
1231 ilfs.package = package;
1232 ilfs.feature = feature;
1234 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1235 msiobj_release(&view->hdr);
1237 return ERROR_SUCCESS;
1240 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1242 MSIPACKAGE* package = param;
1243 MSIFEATURE *parent, *child;
1245 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1247 return ERROR_FUNCTION_FAILED;
1249 if (!child->Feature_Parent)
1250 return ERROR_SUCCESS;
1252 parent = find_feature_by_name( package, child->Feature_Parent );
1254 return ERROR_FUNCTION_FAILED;
1256 add_feature_child( parent, child );
1257 return ERROR_SUCCESS;
1260 static UINT load_all_features( MSIPACKAGE *package )
1262 static const WCHAR query[] = {
1263 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1264 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1265 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1269 if (!list_empty(&package->features))
1270 return ERROR_SUCCESS;
1272 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1273 if (r != ERROR_SUCCESS)
1276 r = MSI_IterateRecords( view, NULL, load_feature, package );
1277 if (r != ERROR_SUCCESS)
1280 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1281 msiobj_release( &view->hdr );
1286 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1297 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1299 static const WCHAR query[] = {
1300 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1301 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1302 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1303 MSIQUERY *view = NULL;
1304 MSIRECORD *row = NULL;
1307 TRACE("%s\n", debugstr_w(file->File));
1309 r = MSI_OpenQuery(package->db, &view, query, file->File);
1310 if (r != ERROR_SUCCESS)
1313 r = MSI_ViewExecute(view, NULL);
1314 if (r != ERROR_SUCCESS)
1317 r = MSI_ViewFetch(view, &row);
1318 if (r != ERROR_SUCCESS)
1321 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1322 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1323 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1324 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1325 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1328 if (view) msiobj_release(&view->hdr);
1329 if (row) msiobj_release(&row->hdr);
1333 static UINT load_file(MSIRECORD *row, LPVOID param)
1335 MSIPACKAGE* package = param;
1339 /* fill in the data */
1341 file = msi_alloc_zero( sizeof (MSIFILE) );
1343 return ERROR_NOT_ENOUGH_MEMORY;
1345 file->File = msi_dup_record_field( row, 1 );
1347 component = MSI_RecordGetString( row, 2 );
1348 file->Component = get_loaded_component( package, component );
1350 if (!file->Component)
1352 WARN("Component not found: %s\n", debugstr_w(component));
1353 msi_free(file->File);
1355 return ERROR_SUCCESS;
1358 file->FileName = msi_dup_record_field( row, 3 );
1359 reduce_to_longfilename( file->FileName );
1361 file->ShortName = msi_dup_record_field( row, 3 );
1362 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1364 file->FileSize = MSI_RecordGetInteger( row, 4 );
1365 file->Version = msi_dup_record_field( row, 5 );
1366 file->Language = msi_dup_record_field( row, 6 );
1367 file->Attributes = MSI_RecordGetInteger( row, 7 );
1368 file->Sequence = MSI_RecordGetInteger( row, 8 );
1370 file->state = msifs_invalid;
1372 /* if the compressed bits are not set in the file attributes,
1373 * then read the information from the package word count property
1375 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1377 file->IsCompressed = FALSE;
1379 else if (file->Attributes &
1380 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1382 file->IsCompressed = TRUE;
1384 else if (file->Attributes & msidbFileAttributesNoncompressed)
1386 file->IsCompressed = FALSE;
1390 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1393 load_file_hash(package, file);
1395 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1397 list_add_tail( &package->files, &file->entry );
1399 return ERROR_SUCCESS;
1402 static UINT load_all_files(MSIPACKAGE *package)
1406 static const WCHAR Query[] =
1407 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1408 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1409 '`','S','e','q','u','e','n','c','e','`', 0};
1411 if (!list_empty(&package->files))
1412 return ERROR_SUCCESS;
1414 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1415 if (rc != ERROR_SUCCESS)
1416 return ERROR_SUCCESS;
1418 rc = MSI_IterateRecords(view, NULL, load_file, package);
1419 msiobj_release(&view->hdr);
1421 return ERROR_SUCCESS;
1424 static UINT load_folder( MSIRECORD *row, LPVOID param )
1426 MSIPACKAGE *package = param;
1427 static WCHAR szEmpty[] = { 0 };
1428 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1431 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1433 return ERROR_NOT_ENOUGH_MEMORY;
1435 folder->Directory = msi_dup_record_field( row, 1 );
1437 TRACE("%s\n", debugstr_w(folder->Directory));
1439 p = msi_dup_record_field(row, 3);
1441 /* split src and target dir */
1443 src_short = folder_split_path( p, ':' );
1445 /* split the long and short paths */
1446 tgt_long = folder_split_path( tgt_short, '|' );
1447 src_long = folder_split_path( src_short, '|' );
1449 /* check for no-op dirs */
1450 if (!lstrcmpW(szDot, tgt_short))
1451 tgt_short = szEmpty;
1452 if (!lstrcmpW(szDot, src_short))
1453 src_short = szEmpty;
1456 tgt_long = tgt_short;
1459 src_short = tgt_short;
1460 src_long = tgt_long;
1464 src_long = src_short;
1466 /* FIXME: use the target short path too */
1467 folder->TargetDefault = strdupW(tgt_long);
1468 folder->SourceShortPath = strdupW(src_short);
1469 folder->SourceLongPath = strdupW(src_long);
1472 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1473 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1474 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1476 folder->Parent = msi_dup_record_field( row, 2 );
1478 folder->Property = msi_dup_property( package, folder->Directory );
1480 list_add_tail( &package->folders, &folder->entry );
1482 TRACE("returning %p\n", folder);
1484 return ERROR_SUCCESS;
1487 static UINT load_all_folders( MSIPACKAGE *package )
1489 static const WCHAR query[] = {
1490 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1491 '`','D','i','r','e','c','t','o','r','y','`',0 };
1495 if (!list_empty(&package->folders))
1496 return ERROR_SUCCESS;
1498 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1499 if (r != ERROR_SUCCESS)
1502 r = MSI_IterateRecords(view, NULL, load_folder, package);
1503 msiobj_release(&view->hdr);
1508 * I am not doing any of the costing functionality yet.
1509 * Mostly looking at doing the Component and Feature loading
1511 * The native MSI does A LOT of modification to tables here. Mostly adding
1512 * a lot of temporary columns to the Feature and Component tables.
1514 * note: Native msi also tracks the short filename. But I am only going to
1515 * track the long ones. Also looking at this directory table
1516 * it appears that the directory table does not get the parents
1517 * resolved base on property only based on their entries in the
1520 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1522 static const WCHAR szCosting[] =
1523 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1525 MSI_SetPropertyW(package, szCosting, szZero);
1526 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1528 load_all_folders( package );
1529 load_all_components( package );
1530 load_all_features( package );
1531 load_all_files( package );
1533 return ERROR_SUCCESS;
1536 static UINT execute_script(MSIPACKAGE *package, UINT script )
1539 UINT rc = ERROR_SUCCESS;
1541 TRACE("Executing Script %i\n",script);
1543 if (!package->script)
1545 ERR("no script!\n");
1546 return ERROR_FUNCTION_FAILED;
1549 for (i = 0; i < package->script->ActionCount[script]; i++)
1552 action = package->script->Actions[script][i];
1553 ui_actionstart(package, action);
1554 TRACE("Executing Action (%s)\n",debugstr_w(action));
1555 rc = ACTION_PerformAction(package, action, script, TRUE);
1556 if (rc != ERROR_SUCCESS)
1559 msi_free_action_script(package, script);
1563 static UINT ACTION_FileCost(MSIPACKAGE *package)
1565 return ERROR_SUCCESS;
1568 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1574 state = MsiQueryProductStateW(package->ProductCode);
1576 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1578 if (!comp->ComponentId)
1581 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1582 comp->Installed = INSTALLSTATE_ABSENT;
1585 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1586 package->Context, comp->ComponentId,
1588 if (r != ERROR_SUCCESS)
1589 comp->Installed = INSTALLSTATE_ABSENT;
1594 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1596 MSIFEATURE *feature;
1599 state = MsiQueryProductStateW(package->ProductCode);
1601 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1603 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1604 feature->Installed = INSTALLSTATE_ABSENT;
1607 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1613 static BOOL process_state_property(MSIPACKAGE* package, int level,
1614 LPCWSTR property, INSTALLSTATE state)
1617 MSIFEATURE *feature;
1619 override = msi_dup_property( package, property );
1623 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1625 if (lstrcmpW(property, szRemove) &&
1626 (feature->Level <= 0 || feature->Level > level))
1629 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1631 if (strcmpiW(override, szAll)==0)
1632 msi_feature_set_state(package, feature, state);
1635 LPWSTR ptr = override;
1636 LPWSTR ptr2 = strchrW(override,',');
1640 int len = ptr2 - ptr;
1642 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1643 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1645 msi_feature_set_state(package, feature, state);
1651 ptr2 = strchrW(ptr,',');
1663 static BOOL process_overrides( MSIPACKAGE *package, int level )
1665 static const WCHAR szAddLocal[] =
1666 {'A','D','D','L','O','C','A','L',0};
1667 static const WCHAR szAddSource[] =
1668 {'A','D','D','S','O','U','R','C','E',0};
1669 static const WCHAR szAdvertise[] =
1670 {'A','D','V','E','R','T','I','S','E',0};
1673 /* all these activation/deactivation things happen in order and things
1674 * later on the list override things earlier on the list.
1676 * 0 INSTALLLEVEL processing
1689 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1690 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1691 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1692 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1693 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1696 MSI_SetPropertyW( package, szPreselected, szOne );
1701 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1704 static const WCHAR szlevel[] =
1705 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1706 MSICOMPONENT* component;
1707 MSIFEATURE *feature;
1709 TRACE("Checking Install Level\n");
1711 level = msi_get_property_int(package, szlevel, 1);
1713 if (!msi_get_property_int( package, szPreselected, 0 ))
1715 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1717 BOOL feature_state = ((feature->Level > 0) &&
1718 (feature->Level <= level));
1720 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1722 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1723 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1724 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1725 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1727 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1731 /* disable child features of unselected parent features */
1732 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1736 if (feature->Level > 0 && feature->Level <= level)
1739 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1740 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1745 * now we want to enable or disable components base on feature
1748 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1752 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1753 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1755 if (!feature->Level)
1758 /* features with components that have compressed files are made local */
1759 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1761 if (cl->component->Enabled &&
1762 cl->component->ForceLocalState &&
1763 feature->Action == INSTALLSTATE_SOURCE)
1765 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1770 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1772 component = cl->component;
1774 if (!component->Enabled)
1777 switch (feature->Action)
1779 case INSTALLSTATE_ABSENT:
1780 component->anyAbsent = 1;
1782 case INSTALLSTATE_ADVERTISED:
1783 component->hasAdvertiseFeature = 1;
1785 case INSTALLSTATE_SOURCE:
1786 component->hasSourceFeature = 1;
1788 case INSTALLSTATE_LOCAL:
1789 component->hasLocalFeature = 1;
1791 case INSTALLSTATE_DEFAULT:
1792 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1793 component->hasAdvertiseFeature = 1;
1794 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1795 component->hasSourceFeature = 1;
1797 component->hasLocalFeature = 1;
1805 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1807 /* if the component isn't enabled, leave it alone */
1808 if (!component->Enabled)
1811 /* check if it's local or source */
1812 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1813 (component->hasLocalFeature || component->hasSourceFeature))
1815 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1816 !component->ForceLocalState)
1817 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1819 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1823 /* if any feature is local, the component must be local too */
1824 if (component->hasLocalFeature)
1826 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1830 if (component->hasSourceFeature)
1832 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1836 if (component->hasAdvertiseFeature)
1838 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1842 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1843 if (component->anyAbsent)
1844 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1847 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1849 if (component->Action == INSTALLSTATE_DEFAULT)
1851 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1852 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1855 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1856 debugstr_w(component->Component), component->Installed, component->Action);
1860 return ERROR_SUCCESS;
1863 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1865 MSIPACKAGE *package = param;
1870 name = MSI_RecordGetString(row,1);
1872 f = get_loaded_folder(package, name);
1873 if (!f) return ERROR_SUCCESS;
1875 /* reset the ResolvedTarget */
1876 msi_free(f->ResolvedTarget);
1877 f->ResolvedTarget = NULL;
1879 /* This helper function now does ALL the work */
1880 TRACE("Dir %s ...\n",debugstr_w(name));
1881 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1882 TRACE("resolves to %s\n",debugstr_w(path));
1885 return ERROR_SUCCESS;
1888 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1890 MSIPACKAGE *package = param;
1892 MSIFEATURE *feature;
1894 name = MSI_RecordGetString( row, 1 );
1896 feature = get_loaded_feature( package, name );
1898 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1902 Condition = MSI_RecordGetString(row,3);
1904 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1906 int level = MSI_RecordGetInteger(row,2);
1907 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1908 feature->Level = level;
1911 return ERROR_SUCCESS;
1914 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1916 static const WCHAR name_fmt[] =
1917 {'%','u','.','%','u','.','%','u','.','%','u',0};
1918 static const WCHAR name[] = {'\\',0};
1919 VS_FIXEDFILEINFO *lpVer;
1920 WCHAR filever[0x100];
1926 TRACE("%s\n", debugstr_w(filename));
1928 versize = GetFileVersionInfoSizeW( filename, &handle );
1932 version = msi_alloc( versize );
1933 GetFileVersionInfoW( filename, 0, versize, version );
1935 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
1937 msi_free( version );
1941 sprintfW( filever, name_fmt,
1942 HIWORD(lpVer->dwFileVersionMS),
1943 LOWORD(lpVer->dwFileVersionMS),
1944 HIWORD(lpVer->dwFileVersionLS),
1945 LOWORD(lpVer->dwFileVersionLS));
1947 msi_free( version );
1949 return strdupW( filever );
1952 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1954 LPWSTR file_version;
1957 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1959 MSICOMPONENT* comp = file->Component;
1965 if (file->IsCompressed)
1966 comp->ForceLocalState = TRUE;
1968 /* calculate target */
1969 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
1971 msi_free(file->TargetPath);
1973 TRACE("file %s is named %s\n",
1974 debugstr_w(file->File), debugstr_w(file->FileName));
1976 file->TargetPath = build_directory_name(2, p, file->FileName);
1980 TRACE("file %s resolves to %s\n",
1981 debugstr_w(file->File), debugstr_w(file->TargetPath));
1983 /* don't check files of components that aren't installed */
1984 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
1985 comp->Installed == INSTALLSTATE_ABSENT)
1987 file->state = msifs_missing; /* assume files are missing */
1991 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1993 file->state = msifs_missing;
1994 comp->Cost += file->FileSize;
1998 if (file->Version &&
1999 (file_version = msi_get_disk_file_version( file->TargetPath )))
2001 TRACE("new %s old %s\n", debugstr_w(file->Version),
2002 debugstr_w(file_version));
2003 /* FIXME: seems like a bad way to compare version numbers */
2004 if (lstrcmpiW(file_version, file->Version)<0)
2006 file->state = msifs_overwrite;
2007 comp->Cost += file->FileSize;
2010 file->state = msifs_present;
2011 msi_free( file_version );
2014 file->state = msifs_present;
2017 return ERROR_SUCCESS;
2021 * A lot is done in this function aside from just the costing.
2022 * The costing needs to be implemented at some point but for now I am going
2023 * to focus on the directory building
2026 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2028 static const WCHAR ExecSeqQuery[] =
2029 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2030 '`','D','i','r','e','c','t','o','r','y','`',0};
2031 static const WCHAR ConditionQuery[] =
2032 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2033 '`','C','o','n','d','i','t','i','o','n','`',0};
2034 static const WCHAR szCosting[] =
2035 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2036 static const WCHAR szlevel[] =
2037 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2038 static const WCHAR szOutOfDiskSpace[] =
2039 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2041 UINT rc = ERROR_SUCCESS;
2045 TRACE("Building Directory properties\n");
2047 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2048 if (rc == ERROR_SUCCESS)
2050 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2052 msiobj_release(&view->hdr);
2055 /* read components states from the registry */
2056 ACTION_GetComponentInstallStates(package);
2057 ACTION_GetFeatureInstallStates(package);
2059 TRACE("File calculations\n");
2060 msi_check_file_install_states( package );
2062 if (!process_overrides( package, msi_get_property_int( package, szlevel, 1 ) ))
2064 TRACE("Evaluating Condition Table\n");
2066 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2067 if (rc == ERROR_SUCCESS)
2069 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2070 msiobj_release( &view->hdr );
2073 TRACE("Enabling or Disabling Components\n");
2074 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2076 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2078 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2079 comp->Enabled = FALSE;
2082 comp->Enabled = TRUE;
2086 MSI_SetPropertyW(package,szCosting,szOne);
2087 /* set default run level if not set */
2088 level = msi_dup_property( package, szlevel );
2090 MSI_SetPropertyW(package,szlevel, szOne);
2093 /* FIXME: check volume disk space */
2094 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2096 return MSI_SetFeatureStates(package);
2099 /* OK this value is "interpreted" and then formatted based on the
2100 first few characters */
2101 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2106 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2112 LPWSTR deformated = NULL;
2115 deformat_string(package, &value[2], &deformated);
2117 /* binary value type */
2121 *size = (strlenW(ptr)/2)+1;
2123 *size = strlenW(ptr)/2;
2125 data = msi_alloc(*size);
2131 /* if uneven pad with a zero in front */
2137 data[count] = (BYTE)strtol(byte,NULL,0);
2139 TRACE("Uneven byte count\n");
2147 data[count] = (BYTE)strtol(byte,NULL,0);
2150 msi_free(deformated);
2152 TRACE("Data %i bytes(%i)\n",*size,count);
2159 deformat_string(package, &value[1], &deformated);
2162 *size = sizeof(DWORD);
2163 data = msi_alloc(*size);
2169 if ( (*p < '0') || (*p > '9') )
2175 if (deformated[0] == '-')
2178 TRACE("DWORD %i\n",*(LPDWORD)data);
2180 msi_free(deformated);
2185 static const WCHAR szMulti[] = {'[','~',']',0};
2194 *type=REG_EXPAND_SZ;
2202 if (strstrW(value,szMulti))
2203 *type = REG_MULTI_SZ;
2205 /* remove initial delimiter */
2206 if (!strncmpW(value, szMulti, 3))
2209 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2211 /* add double NULL terminator */
2212 if (*type == REG_MULTI_SZ)
2214 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2215 data = msi_realloc_zero(data, *size);
2221 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2223 MSIPACKAGE *package = param;
2224 static const WCHAR szHCR[] =
2225 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2226 'R','O','O','T','\\',0};
2227 static const WCHAR szHCU[] =
2228 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2229 'U','S','E','R','\\',0};
2230 static const WCHAR szHLM[] =
2231 {'H','K','E','Y','_','L','O','C','A','L','_',
2232 'M','A','C','H','I','N','E','\\',0};
2233 static const WCHAR szHU[] =
2234 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2236 LPSTR value_data = NULL;
2237 HKEY root_key, hkey;
2240 LPCWSTR szRoot, component, name, key, value;
2245 BOOL check_first = FALSE;
2248 ui_progress(package,2,0,0,0);
2255 component = MSI_RecordGetString(row, 6);
2256 comp = get_loaded_component(package,component);
2258 return ERROR_SUCCESS;
2260 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2262 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2263 comp->Action = comp->Installed;
2264 return ERROR_SUCCESS;
2266 comp->Action = INSTALLSTATE_LOCAL;
2268 name = MSI_RecordGetString(row, 4);
2269 if( MSI_RecordIsNull(row,5) && name )
2271 /* null values can have special meanings */
2272 if (name[0]=='-' && name[1] == 0)
2273 return ERROR_SUCCESS;
2274 else if ((name[0]=='+' && name[1] == 0) ||
2275 (name[0] == '*' && name[1] == 0))
2280 root = MSI_RecordGetInteger(row,2);
2281 key = MSI_RecordGetString(row, 3);
2283 /* get the root key */
2288 LPWSTR all_users = msi_dup_property( package, szAllUsers );
2289 if (all_users && all_users[0] == '1')
2291 root_key = HKEY_LOCAL_MACHINE;
2296 root_key = HKEY_CURRENT_USER;
2299 msi_free(all_users);
2302 case 0: root_key = HKEY_CLASSES_ROOT;
2305 case 1: root_key = HKEY_CURRENT_USER;
2308 case 2: root_key = HKEY_LOCAL_MACHINE;
2311 case 3: root_key = HKEY_USERS;
2315 ERR("Unknown root %i\n",root);
2321 return ERROR_SUCCESS;
2323 deformat_string(package, key , &deformated);
2324 size = strlenW(deformated) + strlenW(szRoot) + 1;
2325 uikey = msi_alloc(size*sizeof(WCHAR));
2326 strcpyW(uikey,szRoot);
2327 strcatW(uikey,deformated);
2329 if (RegCreateKeyW( root_key, deformated, &hkey))
2331 ERR("Could not create key %s\n",debugstr_w(deformated));
2332 msi_free(deformated);
2334 return ERROR_SUCCESS;
2336 msi_free(deformated);
2338 value = MSI_RecordGetString(row,5);
2340 value_data = parse_value(package, value, &type, &size);
2343 value_data = (LPSTR)strdupW(szEmpty);
2344 size = sizeof(szEmpty);
2348 deformat_string(package, name, &deformated);
2352 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2354 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2359 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2360 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2362 TRACE("value %s of %s checked already exists\n",
2363 debugstr_w(deformated), debugstr_w(uikey));
2367 TRACE("Checked and setting value %s of %s\n",
2368 debugstr_w(deformated), debugstr_w(uikey));
2369 if (deformated || size)
2370 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2375 uirow = MSI_CreateRecord(3);
2376 MSI_RecordSetStringW(uirow,2,deformated);
2377 MSI_RecordSetStringW(uirow,1,uikey);
2380 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2382 MSI_RecordSetStringW(uirow,3,value);
2384 ui_actiondata(package,szWriteRegistryValues,uirow);
2385 msiobj_release( &uirow->hdr );
2387 msi_free(value_data);
2388 msi_free(deformated);
2391 return ERROR_SUCCESS;
2394 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2398 static const WCHAR ExecSeqQuery[] =
2399 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2400 '`','R','e','g','i','s','t','r','y','`',0 };
2402 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2403 if (rc != ERROR_SUCCESS)
2404 return ERROR_SUCCESS;
2406 /* increment progress bar each time action data is sent */
2407 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2409 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2411 msiobj_release(&view->hdr);
2415 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2417 package->script->CurrentlyScripting = TRUE;
2419 return ERROR_SUCCESS;
2423 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2428 static const WCHAR q1[]=
2429 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2430 '`','R','e','g','i','s','t','r','y','`',0};
2433 MSIFEATURE *feature;
2436 TRACE("InstallValidate\n");
2438 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2439 if (rc == ERROR_SUCCESS)
2441 MSI_IterateRecords( view, &progress, NULL, package );
2442 msiobj_release( &view->hdr );
2443 total += progress * REG_PROGRESS_VALUE;
2446 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2447 total += COMPONENT_PROGRESS_VALUE;
2449 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2450 total += file->FileSize;
2452 ui_progress(package,0,total,0,0);
2454 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2456 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2457 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2458 feature->ActionRequest);
2461 return ERROR_SUCCESS;
2464 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2466 MSIPACKAGE* package = param;
2467 LPCWSTR cond = NULL;
2468 LPCWSTR message = NULL;
2471 static const WCHAR title[]=
2472 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2474 cond = MSI_RecordGetString(row,1);
2476 r = MSI_EvaluateConditionW(package,cond);
2477 if (r == MSICONDITION_FALSE)
2479 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2482 message = MSI_RecordGetString(row,2);
2483 deformat_string(package,message,&deformated);
2484 MessageBoxW(NULL,deformated,title,MB_OK);
2485 msi_free(deformated);
2488 return ERROR_INSTALL_FAILURE;
2491 return ERROR_SUCCESS;
2494 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2497 MSIQUERY * view = NULL;
2498 static const WCHAR ExecSeqQuery[] =
2499 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2500 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2502 TRACE("Checking launch conditions\n");
2504 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2505 if (rc != ERROR_SUCCESS)
2506 return ERROR_SUCCESS;
2508 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2509 msiobj_release(&view->hdr);
2514 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2518 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2520 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2522 MSIRECORD * row = 0;
2524 LPWSTR deformated,buffer,deformated_name;
2526 static const WCHAR ExecSeqQuery[] =
2527 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2528 '`','R','e','g','i','s','t','r','y','`',' ',
2529 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2530 ' ','=',' ' ,'\'','%','s','\'',0 };
2531 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2532 static const WCHAR fmt2[]=
2533 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2535 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2539 root = MSI_RecordGetInteger(row,2);
2540 key = MSI_RecordGetString(row, 3);
2541 name = MSI_RecordGetString(row, 4);
2542 deformat_string(package, key , &deformated);
2543 deformat_string(package, name, &deformated_name);
2545 len = strlenW(deformated) + 6;
2546 if (deformated_name)
2547 len+=strlenW(deformated_name);
2549 buffer = msi_alloc( len *sizeof(WCHAR));
2551 if (deformated_name)
2552 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2554 sprintfW(buffer,fmt,root,deformated);
2556 msi_free(deformated);
2557 msi_free(deformated_name);
2558 msiobj_release(&row->hdr);
2562 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2564 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2569 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2572 return strdupW( file->TargetPath );
2577 static HKEY openSharedDLLsKey(void)
2580 static const WCHAR path[] =
2581 {'S','o','f','t','w','a','r','e','\\',
2582 'M','i','c','r','o','s','o','f','t','\\',
2583 'W','i','n','d','o','w','s','\\',
2584 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2585 'S','h','a','r','e','d','D','L','L','s',0};
2587 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2591 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2596 DWORD sz = sizeof(count);
2599 hkey = openSharedDLLsKey();
2600 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2601 if (rc != ERROR_SUCCESS)
2607 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2611 hkey = openSharedDLLsKey();
2613 msi_reg_set_val_dword( hkey, path, count );
2615 RegDeleteValueW(hkey,path);
2621 * Return TRUE if the count should be written out and FALSE if not
2623 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2625 MSIFEATURE *feature;
2629 /* only refcount DLLs */
2630 if (comp->KeyPath == NULL ||
2631 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2632 comp->Attributes & msidbComponentAttributesODBCDataSource)
2636 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2637 write = (count > 0);
2639 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2643 /* increment counts */
2644 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2648 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
2651 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2653 if ( cl->component == comp )
2658 /* decrement counts */
2659 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2663 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
2666 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2668 if ( cl->component == comp )
2673 /* ref count all the files in the component */
2678 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2680 if (file->Component == comp)
2681 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2685 /* add a count for permanent */
2686 if (comp->Attributes & msidbComponentAttributesPermanent)
2689 comp->RefCount = count;
2692 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2695 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2697 WCHAR squished_pc[GUID_SIZE];
2698 WCHAR squished_cc[GUID_SIZE];
2705 squash_guid(package->ProductCode,squished_pc);
2706 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2708 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2712 ui_progress(package,2,0,0,0);
2713 if (!comp->ComponentId)
2716 squash_guid(comp->ComponentId,squished_cc);
2718 msi_free(comp->FullKeypath);
2719 comp->FullKeypath = resolve_keypath( package, comp );
2721 ACTION_RefCountComponent( package, comp );
2723 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2724 debugstr_w(comp->Component),
2725 debugstr_w(squished_cc),
2726 debugstr_w(comp->FullKeypath),
2729 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
2730 comp->ActionRequest == INSTALLSTATE_SOURCE)
2732 if (!comp->FullKeypath)
2735 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2736 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2739 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2742 if (rc != ERROR_SUCCESS)
2745 if (comp->Attributes & msidbComponentAttributesPermanent)
2747 static const WCHAR szPermKey[] =
2748 { '0','0','0','0','0','0','0','0','0','0','0','0',
2749 '0','0','0','0','0','0','0','0','0','0','0','0',
2750 '0','0','0','0','0','0','0','0',0 };
2752 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2755 if (comp->Action == INSTALLSTATE_LOCAL)
2756 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2762 WCHAR source[MAX_PATH];
2763 WCHAR base[MAX_PATH];
2766 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2767 static const WCHAR query[] = {
2768 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2769 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2770 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2771 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2772 '`','D','i','s','k','I','d','`',0};
2774 file = get_loaded_file(package, comp->KeyPath);
2778 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2779 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2780 ptr2 = strrchrW(source, '\\') + 1;
2781 msiobj_release(&row->hdr);
2783 lstrcpyW(base, package->PackagePath);
2784 ptr = strrchrW(base, '\\');
2787 sourcepath = resolve_file_source(package, file);
2788 ptr = sourcepath + lstrlenW(base);
2789 lstrcpyW(ptr2, ptr);
2790 msi_free(sourcepath);
2792 msi_reg_set_val_str(hkey, squished_pc, source);
2796 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
2798 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2799 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
2801 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
2805 uirow = MSI_CreateRecord(3);
2806 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2807 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2808 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2809 ui_actiondata(package,szProcessComponents,uirow);
2810 msiobj_release( &uirow->hdr );
2813 return ERROR_SUCCESS;
2824 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2825 LPWSTR lpszName, LONG_PTR lParam)
2828 typelib_struct *tl_struct = (typelib_struct*) lParam;
2829 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2833 if (!IS_INTRESOURCE(lpszName))
2835 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2839 sz = strlenW(tl_struct->source)+4;
2840 sz *= sizeof(WCHAR);
2842 if ((INT_PTR)lpszName == 1)
2843 tl_struct->path = strdupW(tl_struct->source);
2846 tl_struct->path = msi_alloc(sz);
2847 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2850 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2851 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2854 msi_free(tl_struct->path);
2855 tl_struct->path = NULL;
2860 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2861 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2863 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2867 msi_free(tl_struct->path);
2868 tl_struct->path = NULL;
2870 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2871 ITypeLib_Release(tl_struct->ptLib);
2876 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2878 MSIPACKAGE* package = param;
2882 typelib_struct tl_struct;
2887 component = MSI_RecordGetString(row,3);
2888 comp = get_loaded_component(package,component);
2890 return ERROR_SUCCESS;
2892 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2894 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2895 comp->Action = comp->Installed;
2896 return ERROR_SUCCESS;
2898 comp->Action = INSTALLSTATE_LOCAL;
2900 file = get_loaded_file( package, comp->KeyPath );
2902 return ERROR_SUCCESS;
2904 ui_actiondata( package, szRegisterTypeLibraries, row );
2906 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2910 guid = MSI_RecordGetString(row,1);
2911 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2912 tl_struct.source = strdupW( file->TargetPath );
2913 tl_struct.path = NULL;
2915 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2916 (LONG_PTR)&tl_struct);
2924 helpid = MSI_RecordGetString(row,6);
2927 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2928 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2932 ERR("Failed to register type library %s\n",
2933 debugstr_w(tl_struct.path));
2935 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2937 ITypeLib_Release(tl_struct.ptLib);
2938 msi_free(tl_struct.path);
2941 ERR("Failed to load type library %s\n",
2942 debugstr_w(tl_struct.source));
2944 FreeLibrary(module);
2945 msi_free(tl_struct.source);
2949 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
2952 ERR("Failed to load type library: %08x\n", hr);
2953 return ERROR_INSTALL_FAILURE;
2956 ITypeLib_Release(tlib);
2959 return ERROR_SUCCESS;
2962 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2965 * OK this is a bit confusing.. I am given a _Component key and I believe
2966 * that the file that is being registered as a type library is the "key file
2967 * of that component" which I interpret to mean "The file in the KeyPath of
2972 static const WCHAR Query[] =
2973 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2974 '`','T','y','p','e','L','i','b','`',0};
2976 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2977 if (rc != ERROR_SUCCESS)
2978 return ERROR_SUCCESS;
2980 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2981 msiobj_release(&view->hdr);
2985 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
2987 MSIPACKAGE *package = param;
2988 LPCWSTR component, guid;
2996 component = MSI_RecordGetString( row, 3 );
2997 comp = get_loaded_component( package, component );
2999 return ERROR_SUCCESS;
3001 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3003 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3004 comp->Action = comp->Installed;
3005 return ERROR_SUCCESS;
3007 comp->Action = INSTALLSTATE_ABSENT;
3009 ui_actiondata( package, szUnregisterTypeLibraries, row );
3011 guid = MSI_RecordGetString( row, 1 );
3012 CLSIDFromString( (LPWSTR)guid, &libid );
3013 version = MSI_RecordGetInteger( row, 4 );
3014 language = MSI_RecordGetInteger( row, 2 );
3017 syskind = SYS_WIN64;
3019 syskind = SYS_WIN32;
3022 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3025 WARN("Failed to unregister typelib: %08x\n", hr);
3028 return ERROR_SUCCESS;
3031 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3035 static const WCHAR query[] =
3036 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3037 '`','T','y','p','e','L','i','b','`',0};
3039 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3040 if (rc != ERROR_SUCCESS)
3041 return ERROR_SUCCESS;
3043 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3044 msiobj_release( &view->hdr );
3048 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3050 static const WCHAR szlnk[] = {'.','l','n','k',0};
3051 LPCWSTR directory, extension;
3052 LPWSTR link_folder, link_file, filename;
3054 directory = MSI_RecordGetString( row, 2 );
3055 link_folder = resolve_folder( package, directory, FALSE, FALSE, TRUE, NULL );
3057 /* may be needed because of a bug somewhere else */
3058 create_full_pathW( link_folder );
3060 filename = msi_dup_record_field( row, 3 );
3061 reduce_to_longfilename( filename );
3063 extension = strchrW( filename, '.' );
3064 if (!extension || strcmpiW( extension, szlnk ))
3066 int len = strlenW( filename );
3067 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3068 memcpy( filename + len, szlnk, sizeof(szlnk) );
3070 link_file = build_directory_name( 2, link_folder, filename );
3071 msi_free( link_folder );
3072 msi_free( filename );
3077 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3079 MSIPACKAGE *package = param;
3080 LPWSTR link_file, deformated, path;
3081 LPCWSTR component, target;
3083 IShellLinkW *sl = NULL;
3084 IPersistFile *pf = NULL;
3087 component = MSI_RecordGetString(row, 4);
3088 comp = get_loaded_component(package, component);
3090 return ERROR_SUCCESS;
3092 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3094 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3095 comp->Action = comp->Installed;
3096 return ERROR_SUCCESS;
3098 comp->Action = INSTALLSTATE_LOCAL;
3100 ui_actiondata(package,szCreateShortcuts,row);
3102 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3103 &IID_IShellLinkW, (LPVOID *) &sl );
3107 ERR("CLSID_ShellLink not available\n");
3111 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3114 ERR("QueryInterface(IID_IPersistFile) failed\n");
3118 target = MSI_RecordGetString(row, 5);
3119 if (strchrW(target, '['))
3121 deformat_string(package, target, &deformated);
3122 IShellLinkW_SetPath(sl,deformated);
3123 msi_free(deformated);
3127 FIXME("poorly handled shortcut format, advertised shortcut\n");
3128 IShellLinkW_SetPath(sl,comp->FullKeypath);
3131 if (!MSI_RecordIsNull(row,6))
3133 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3134 deformat_string(package, arguments, &deformated);
3135 IShellLinkW_SetArguments(sl,deformated);
3136 msi_free(deformated);
3139 if (!MSI_RecordIsNull(row,7))
3141 LPCWSTR description = MSI_RecordGetString(row, 7);
3142 IShellLinkW_SetDescription(sl, description);
3145 if (!MSI_RecordIsNull(row,8))
3146 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3148 if (!MSI_RecordIsNull(row,9))
3151 LPCWSTR icon = MSI_RecordGetString(row, 9);
3153 path = build_icon_path(package, icon);
3154 index = MSI_RecordGetInteger(row,10);
3156 /* no value means 0 */
3157 if (index == MSI_NULL_INTEGER)
3160 IShellLinkW_SetIconLocation(sl, path, index);
3164 if (!MSI_RecordIsNull(row,11))
3165 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3167 if (!MSI_RecordIsNull(row,12))
3169 LPCWSTR wkdir = MSI_RecordGetString(row, 12);
3170 path = resolve_folder(package, wkdir, FALSE, FALSE, TRUE, NULL);
3172 IShellLinkW_SetWorkingDirectory(sl, path);
3176 link_file = get_link_file(package, row);
3178 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3179 IPersistFile_Save(pf, link_file, FALSE);
3181 msi_free(link_file);
3185 IPersistFile_Release( pf );
3187 IShellLinkW_Release( sl );
3189 return ERROR_SUCCESS;
3192 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3197 static const WCHAR Query[] =
3198 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3199 '`','S','h','o','r','t','c','u','t','`',0};
3201 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3202 if (rc != ERROR_SUCCESS)
3203 return ERROR_SUCCESS;
3205 res = CoInitialize( NULL );
3207 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3208 msiobj_release(&view->hdr);
3216 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3218 MSIPACKAGE *package = param;
3223 component = MSI_RecordGetString( row, 4 );
3224 comp = get_loaded_component( package, component );
3226 return ERROR_SUCCESS;
3228 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3230 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3231 comp->Action = comp->Installed;
3232 return ERROR_SUCCESS;
3234 comp->Action = INSTALLSTATE_ABSENT;
3236 ui_actiondata( package, szRemoveShortcuts, row );
3238 link_file = get_link_file( package, row );
3240 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3241 if (!DeleteFileW( link_file ))
3243 WARN("Failed to remove shortcut file %u\n", GetLastError());
3245 msi_free( link_file );
3247 return ERROR_SUCCESS;
3250 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3254 static const WCHAR query[] =
3255 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3256 '`','S','h','o','r','t','c','u','t','`',0};
3258 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3259 if (rc != ERROR_SUCCESS)
3260 return ERROR_SUCCESS;
3262 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3263 msiobj_release( &view->hdr );
3268 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3270 MSIPACKAGE* package = param;
3279 FileName = MSI_RecordGetString(row,1);
3282 ERR("Unable to get FileName\n");
3283 return ERROR_SUCCESS;
3286 FilePath = build_icon_path(package,FileName);
3288 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3290 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3291 FILE_ATTRIBUTE_NORMAL, NULL);
3293 if (the_file == INVALID_HANDLE_VALUE)
3295 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3297 return ERROR_SUCCESS;
3304 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3305 if (rc != ERROR_SUCCESS)
3307 ERR("Failed to get stream\n");
3308 CloseHandle(the_file);
3309 DeleteFileW(FilePath);
3312 WriteFile(the_file,buffer,sz,&write,NULL);
3313 } while (sz == 1024);
3317 CloseHandle(the_file);
3319 uirow = MSI_CreateRecord(1);
3320 MSI_RecordSetStringW(uirow,1,FileName);
3321 ui_actiondata(package,szPublishProduct,uirow);
3322 msiobj_release( &uirow->hdr );
3324 return ERROR_SUCCESS;
3327 static UINT msi_publish_icons(MSIPACKAGE *package)
3332 static const WCHAR query[]= {
3333 'S','E','L','E','C','T',' ','*',' ',
3334 'F','R','O','M',' ','`','I','c','o','n','`',0};
3336 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3337 if (r == ERROR_SUCCESS)
3339 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3340 msiobj_release(&view->hdr);
3343 return ERROR_SUCCESS;
3346 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3352 MSISOURCELISTINFO *info;
3354 r = RegCreateKeyW(hkey, szSourceList, &source);
3355 if (r != ERROR_SUCCESS)
3358 RegCloseKey(source);
3360 buffer = strrchrW(package->PackagePath, '\\') + 1;
3361 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3362 package->Context, MSICODE_PRODUCT,
3363 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3364 if (r != ERROR_SUCCESS)
3367 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3368 package->Context, MSICODE_PRODUCT,
3369 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3370 if (r != ERROR_SUCCESS)
3373 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3374 package->Context, MSICODE_PRODUCT,
3375 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3376 if (r != ERROR_SUCCESS)
3379 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3381 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3382 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3383 info->options, info->value);
3385 MsiSourceListSetInfoW(package->ProductCode, NULL,
3386 info->context, info->options,
3387 info->property, info->value);
3390 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3392 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3393 disk->context, disk->options,
3394 disk->disk_id, disk->volume_label, disk->disk_prompt);
3397 return ERROR_SUCCESS;
3400 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3402 MSIHANDLE hdb, suminfo;
3403 WCHAR guids[MAX_PATH];
3404 WCHAR packcode[SQUISH_GUID_SIZE];
3411 static const WCHAR szProductLanguage[] =
3412 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3413 static const WCHAR szARPProductIcon[] =
3414 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3415 static const WCHAR szProductVersion[] =
3416 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3417 static const WCHAR szAssignment[] =
3418 {'A','s','s','i','g','n','m','e','n','t',0};
3419 static const WCHAR szAdvertiseFlags[] =
3420 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3421 static const WCHAR szClients[] =
3422 {'C','l','i','e','n','t','s',0};
3423 static const WCHAR szColon[] = {':',0};
3425 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3426 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3429 langid = msi_get_property_int(package, szProductLanguage, 0);
3430 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3433 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3435 buffer = msi_dup_property(package, szARPProductIcon);
3438 LPWSTR path = build_icon_path(package,buffer);
3439 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3444 buffer = msi_dup_property(package, szProductVersion);
3447 DWORD verdword = msi_version_str_to_dword(buffer);
3448 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3452 msi_reg_set_val_dword(hkey, szAssignment, 0);
3453 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3454 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3455 msi_reg_set_val_str(hkey, szClients, szColon);
3457 hdb = alloc_msihandle(&package->db->hdr);
3459 return ERROR_NOT_ENOUGH_MEMORY;
3461 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3462 MsiCloseHandle(hdb);
3463 if (r != ERROR_SUCCESS)
3467 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3468 NULL, guids, &size);
3469 if (r != ERROR_SUCCESS)
3472 ptr = strchrW(guids, ';');
3474 squash_guid(guids, packcode);
3475 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3478 MsiCloseHandle(suminfo);
3479 return ERROR_SUCCESS;
3482 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3487 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3489 static const WCHAR szUpgradeCode[] =
3490 {'U','p','g','r','a','d','e','C','o','d','e',0};
3492 upgrade = msi_dup_property(package, szUpgradeCode);
3494 return ERROR_SUCCESS;
3496 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3498 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3499 if (r != ERROR_SUCCESS)
3504 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3505 if (r != ERROR_SUCCESS)
3509 squash_guid(package->ProductCode, squashed_pc);
3510 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3519 static BOOL msi_check_publish(MSIPACKAGE *package)
3521 MSIFEATURE *feature;
3523 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3525 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3532 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3534 MSIFEATURE *feature;
3536 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3538 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3545 static UINT msi_publish_patch(MSIPACKAGE *package, HKEY prodkey, HKEY hudkey)
3547 WCHAR patch_squashed[GUID_SIZE];
3550 UINT r = ERROR_FUNCTION_FAILED;
3552 res = RegCreateKeyExW(prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
3554 if (res != ERROR_SUCCESS)
3555 return ERROR_FUNCTION_FAILED;
3557 squash_guid(package->patch->patchcode, patch_squashed);
3559 res = RegSetValueExW(patches, szPatches, 0, REG_MULTI_SZ,
3560 (const BYTE *)patch_squashed,
3561 (lstrlenW(patch_squashed) + 1) * sizeof(WCHAR));
3562 if (res != ERROR_SUCCESS)
3565 res = RegSetValueExW(patches, patch_squashed, 0, REG_SZ,
3566 (const BYTE *)package->patch->transforms,
3567 (lstrlenW(package->patch->transforms) + 1) * sizeof(WCHAR));
3568 if (res == ERROR_SUCCESS)
3572 RegCloseKey(patches);
3577 * 99% of the work done here is only done for
3578 * advertised installs. However this is where the
3579 * Icon table is processed and written out
3580 * so that is what I am going to do here.
3582 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3588 /* FIXME: also need to publish if the product is in advertise mode */
3589 if (!msi_check_publish(package))
3590 return ERROR_SUCCESS;
3592 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3594 if (rc != ERROR_SUCCESS)
3597 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3598 NULL, &hudkey, TRUE);
3599 if (rc != ERROR_SUCCESS)
3602 rc = msi_publish_upgrade_code(package);
3603 if (rc != ERROR_SUCCESS)
3608 rc = msi_publish_patch(package, hukey, hudkey);
3609 if (rc != ERROR_SUCCESS)
3613 rc = msi_publish_product_properties(package, hukey);
3614 if (rc != ERROR_SUCCESS)
3617 rc = msi_publish_sourcelist(package, hukey);
3618 if (rc != ERROR_SUCCESS)
3621 rc = msi_publish_icons(package);
3625 RegCloseKey(hudkey);
3630 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3632 MSIPACKAGE *package = param;
3633 LPCWSTR component, section, key, value, identifier, dirproperty;
3634 LPWSTR deformated_section, deformated_key, deformated_value;
3635 LPWSTR folder, filename, fullname = NULL;
3636 LPCWSTR filenameptr;
3640 static const WCHAR szWindowsFolder[] =
3641 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3643 component = MSI_RecordGetString(row, 8);
3644 comp = get_loaded_component(package,component);
3646 return ERROR_SUCCESS;
3648 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3650 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3651 comp->Action = comp->Installed;
3652 return ERROR_SUCCESS;
3654 comp->Action = INSTALLSTATE_LOCAL;
3656 identifier = MSI_RecordGetString(row,1);
3657 dirproperty = MSI_RecordGetString(row,3);
3658 section = MSI_RecordGetString(row,4);
3659 key = MSI_RecordGetString(row,5);
3660 value = MSI_RecordGetString(row,6);
3661 action = MSI_RecordGetInteger(row,7);
3663 deformat_string(package,section,&deformated_section);
3664 deformat_string(package,key,&deformated_key);
3665 deformat_string(package,value,&deformated_value);
3667 filename = msi_dup_record_field(row, 2);
3668 if (filename && (filenameptr = strchrW(filename, '|')))
3671 filenameptr = filename;
3675 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3677 folder = msi_dup_property( package, dirproperty );
3680 folder = msi_dup_property( package, szWindowsFolder );
3684 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3688 fullname = build_directory_name(2, folder, filenameptr);
3692 TRACE("Adding value %s to section %s in %s\n",
3693 debugstr_w(deformated_key), debugstr_w(deformated_section),
3694 debugstr_w(fullname));
3695 WritePrivateProfileStringW(deformated_section, deformated_key,
3696 deformated_value, fullname);
3698 else if (action == 1)
3701 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3702 returned, 10, fullname);
3703 if (returned[0] == 0)
3705 TRACE("Adding value %s to section %s in %s\n",
3706 debugstr_w(deformated_key), debugstr_w(deformated_section),
3707 debugstr_w(fullname));
3709 WritePrivateProfileStringW(deformated_section, deformated_key,
3710 deformated_value, fullname);
3713 else if (action == 3)
3714 FIXME("Append to existing section not yet implemented\n");
3716 uirow = MSI_CreateRecord(4);
3717 MSI_RecordSetStringW(uirow,1,identifier);
3718 MSI_RecordSetStringW(uirow,2,deformated_section);
3719 MSI_RecordSetStringW(uirow,3,deformated_key);
3720 MSI_RecordSetStringW(uirow,4,deformated_value);
3721 ui_actiondata(package,szWriteIniValues,uirow);
3722 msiobj_release( &uirow->hdr );
3728 msi_free(deformated_key);
3729 msi_free(deformated_value);
3730 msi_free(deformated_section);
3731 return ERROR_SUCCESS;
3734 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3738 static const WCHAR ExecSeqQuery[] =
3739 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3740 '`','I','n','i','F','i','l','e','`',0};
3742 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3743 if (rc != ERROR_SUCCESS)
3745 TRACE("no IniFile table\n");
3746 return ERROR_SUCCESS;
3749 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3750 msiobj_release(&view->hdr);
3754 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3756 MSIPACKAGE *package = param;
3761 static const WCHAR ExeStr[] =
3762 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3763 static const WCHAR close[] = {'\"',0};
3765 PROCESS_INFORMATION info;
3770 memset(&si,0,sizeof(STARTUPINFOW));
3772 filename = MSI_RecordGetString(row,1);
3773 file = get_loaded_file( package, filename );
3777 ERR("Unable to find file id %s\n",debugstr_w(filename));
3778 return ERROR_SUCCESS;
3781 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3783 FullName = msi_alloc(len*sizeof(WCHAR));
3784 strcpyW(FullName,ExeStr);
3785 strcatW( FullName, file->TargetPath );
3786 strcatW(FullName,close);
3788 TRACE("Registering %s\n",debugstr_w(FullName));
3789 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3794 CloseHandle(info.hThread);
3795 msi_dialog_check_messages(info.hProcess);
3796 CloseHandle(info.hProcess);
3802 uirow = MSI_CreateRecord( 2 );
3803 uipath = strdupW( file->TargetPath );
3804 p = strrchrW(uipath,'\\');
3807 MSI_RecordSetStringW( uirow, 1, &p[1] );
3808 MSI_RecordSetStringW( uirow, 2, uipath);
3809 ui_actiondata( package, szSelfRegModules, uirow);
3810 msiobj_release( &uirow->hdr );
3812 /* FIXME: call ui_progress? */
3814 return ERROR_SUCCESS;
3817 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3821 static const WCHAR ExecSeqQuery[] =
3822 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3823 '`','S','e','l','f','R','e','g','`',0};
3825 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3826 if (rc != ERROR_SUCCESS)
3828 TRACE("no SelfReg table\n");
3829 return ERROR_SUCCESS;
3832 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3833 msiobj_release(&view->hdr);
3835 return ERROR_SUCCESS;
3838 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
3840 static const WCHAR regsvr32[] =
3841 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"',0};
3842 static const WCHAR close[] = {'\"',0};
3843 MSIPACKAGE *package = param;
3849 PROCESS_INFORMATION pi;
3854 memset( &si, 0, sizeof(STARTUPINFOW) );
3856 filename = MSI_RecordGetString( row, 1 );
3857 file = get_loaded_file( package, filename );
3861 ERR("Unable to find file id %s\n", debugstr_w(filename));
3862 return ERROR_SUCCESS;
3865 len = strlenW( regsvr32 ) + strlenW( file->TargetPath ) + 2;
3867 cmdline = msi_alloc( len * sizeof(WCHAR) );
3868 strcpyW( cmdline, regsvr32 );
3869 strcatW( cmdline, file->TargetPath );
3870 strcatW( cmdline, close );
3872 TRACE("Unregistering %s\n", debugstr_w(cmdline));
3874 ret = CreateProcessW( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, c_colon, &si, &pi );
3877 CloseHandle( pi.hThread );
3878 msi_dialog_check_messages( pi.hProcess );
3879 CloseHandle( pi.hProcess );
3882 msi_free( cmdline );
3884 uirow = MSI_CreateRecord( 2 );
3885 uipath = strdupW( file->TargetPath );
3886 if ((p = strrchrW( uipath, '\\' )))
3889 MSI_RecordSetStringW( uirow, 1, ++p );
3891 MSI_RecordSetStringW( uirow, 2, uipath );
3892 ui_actiondata( package, szSelfUnregModules, uirow );
3893 msiobj_release( &uirow->hdr );
3895 /* FIXME call ui_progress? */
3897 return ERROR_SUCCESS;
3900 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
3904 static const WCHAR query[] =
3905 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3906 '`','S','e','l','f','R','e','g','`',0};
3908 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3909 if (rc != ERROR_SUCCESS)
3911 TRACE("no SelfReg table\n");
3912 return ERROR_SUCCESS;
3915 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
3916 msiobj_release( &view->hdr );
3918 return ERROR_SUCCESS;
3921 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3923 MSIFEATURE *feature;
3926 HKEY userdata = NULL;
3928 if (!msi_check_publish(package))
3929 return ERROR_SUCCESS;
3931 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3933 if (rc != ERROR_SUCCESS)
3936 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3938 if (rc != ERROR_SUCCESS)
3941 /* here the guids are base 85 encoded */
3942 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3948 BOOL absent = FALSE;
3951 if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
3952 feature->ActionRequest != INSTALLSTATE_SOURCE &&
3953 feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
3956 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3960 if (feature->Feature_Parent)
3961 size += strlenW( feature->Feature_Parent )+2;
3963 data = msi_alloc(size * sizeof(WCHAR));
3966 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3968 MSICOMPONENT* component = cl->component;
3972 if (component->ComponentId)
3974 TRACE("From %s\n",debugstr_w(component->ComponentId));
3975 CLSIDFromString(component->ComponentId, &clsid);
3976 encode_base85_guid(&clsid,buf);
3977 TRACE("to %s\n",debugstr_w(buf));
3982 if (feature->Feature_Parent)
3984 static const WCHAR sep[] = {'\2',0};
3986 strcatW(data,feature->Feature_Parent);
3989 msi_reg_set_val_str( userdata, feature->Feature, data );
3993 if (feature->Feature_Parent)
3994 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3997 size += sizeof(WCHAR);
3998 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3999 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4003 size += 2*sizeof(WCHAR);
4004 data = msi_alloc(size);
4007 if (feature->Feature_Parent)
4008 strcpyW( &data[1], feature->Feature_Parent );
4009 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4015 uirow = MSI_CreateRecord( 1 );
4016 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4017 ui_actiondata( package, szPublishFeatures, uirow);
4018 msiobj_release( &uirow->hdr );
4019 /* FIXME: call ui_progress? */
4024 RegCloseKey(userdata);
4028 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4033 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4035 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4037 if (r == ERROR_SUCCESS)
4039 RegDeleteValueW(hkey, feature->Feature);
4043 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4045 if (r == ERROR_SUCCESS)
4047 RegDeleteValueW(hkey, feature->Feature);
4051 return ERROR_SUCCESS;
4054 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4056 MSIFEATURE *feature;
4058 if (!msi_check_unpublish(package))
4059 return ERROR_SUCCESS;
4061 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4063 msi_unpublish_feature(package, feature);
4066 return ERROR_SUCCESS;
4069 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4071 LPWSTR prop, val, key;
4077 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4078 static const WCHAR szWindowsInstaller[] =
4079 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4080 static const WCHAR modpath_fmt[] =
4081 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4082 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4083 static const WCHAR szModifyPath[] =
4084 {'M','o','d','i','f','y','P','a','t','h',0};
4085 static const WCHAR szUninstallString[] =
4086 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4087 static const WCHAR szEstimatedSize[] =
4088 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4089 static const WCHAR szProductLanguage[] =
4090 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4091 static const WCHAR szProductVersion[] =
4092 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4093 static const WCHAR szProductName[] =
4094 {'P','r','o','d','u','c','t','N','a','m','e',0};
4095 static const WCHAR szDisplayName[] =
4096 {'D','i','s','p','l','a','y','N','a','m','e',0};
4097 static const WCHAR szDisplayVersion[] =
4098 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4099 static const WCHAR szManufacturer[] =
4100 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4102 static const LPCSTR propval[] = {
4103 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4104 "ARPCONTACT", "Contact",
4105 "ARPCOMMENTS", "Comments",
4106 "ProductName", "DisplayName",
4107 "ProductVersion", "DisplayVersion",
4108 "ARPHELPLINK", "HelpLink",
4109 "ARPHELPTELEPHONE", "HelpTelephone",
4110 "ARPINSTALLLOCATION", "InstallLocation",
4111 "SourceDir", "InstallSource",
4112 "Manufacturer", "Publisher",
4113 "ARPREADME", "Readme",
4115 "ARPURLINFOABOUT", "URLInfoAbout",
4116 "ARPURLUPDATEINFO", "URLUpdateInfo",
4119 const LPCSTR *p = propval;
4123 prop = strdupAtoW(*p++);
4124 key = strdupAtoW(*p++);
4125 val = msi_dup_property(package, prop);
4126 msi_reg_set_val_str(hkey, key, val);
4132 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4134 size = deformat_string(package, modpath_fmt, &buffer);
4135 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4136 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4139 /* FIXME: Write real Estimated Size when we have it */
4140 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4142 buffer = msi_dup_property(package, szProductName);
4143 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4146 buffer = msi_dup_property(package, cszSourceDir);
4147 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4150 buffer = msi_dup_property(package, szManufacturer);
4151 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4154 GetLocalTime(&systime);
4155 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4156 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4158 langid = msi_get_property_int(package, szProductLanguage, 0);
4159 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4161 buffer = msi_dup_property(package, szProductVersion);
4162 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4165 DWORD verdword = msi_version_str_to_dword(buffer);
4167 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4168 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4169 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4173 return ERROR_SUCCESS;
4176 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4178 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4179 LPWSTR upgrade_code;
4184 static const WCHAR szUpgradeCode[] = {
4185 'U','p','g','r','a','d','e','C','o','d','e',0};
4187 /* FIXME: also need to publish if the product is in advertise mode */
4188 if (!msi_check_publish(package))
4189 return ERROR_SUCCESS;
4191 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4192 if (rc != ERROR_SUCCESS)
4195 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4196 NULL, &props, TRUE);
4197 if (rc != ERROR_SUCCESS)
4200 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4201 msi_free( package->db->localfile );
4202 package->db->localfile = NULL;
4204 rc = msi_publish_install_properties(package, hkey);
4205 if (rc != ERROR_SUCCESS)
4208 rc = msi_publish_install_properties(package, props);
4209 if (rc != ERROR_SUCCESS)
4212 upgrade_code = msi_dup_property(package, szUpgradeCode);
4215 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4216 squash_guid(package->ProductCode, squashed_pc);
4217 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4218 RegCloseKey(upgrade);
4219 msi_free(upgrade_code);
4225 return ERROR_SUCCESS;
4228 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4230 return execute_script(package,INSTALL_SCRIPT);
4233 static UINT msi_unpublish_product(MSIPACKAGE *package)
4236 LPWSTR remove = NULL;
4237 LPWSTR *features = NULL;
4238 BOOL full_uninstall = TRUE;
4239 MSIFEATURE *feature;
4241 static const WCHAR szUpgradeCode[] =
4242 {'U','p','g','r','a','d','e','C','o','d','e',0};
4244 remove = msi_dup_property(package, szRemove);
4246 return ERROR_SUCCESS;
4248 features = msi_split_string(remove, ',');
4252 ERR("REMOVE feature list is empty!\n");
4253 return ERROR_FUNCTION_FAILED;
4256 if (!lstrcmpW(features[0], szAll))
4257 full_uninstall = TRUE;
4260 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4262 if (feature->Action != INSTALLSTATE_ABSENT)
4263 full_uninstall = FALSE;
4267 if (!full_uninstall)
4270 MSIREG_DeleteProductKey(package->ProductCode);
4271 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4272 MSIREG_DeleteUninstallKey(package->ProductCode);
4274 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4276 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4277 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4281 MSIREG_DeleteUserProductKey(package->ProductCode);
4282 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4285 upgrade = msi_dup_property(package, szUpgradeCode);
4288 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4295 return ERROR_SUCCESS;
4298 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4302 rc = msi_unpublish_product(package);
4303 if (rc != ERROR_SUCCESS)
4306 /* turn off scheduling */
4307 package->script->CurrentlyScripting= FALSE;
4309 /* first do the same as an InstallExecute */
4310 rc = ACTION_InstallExecute(package);
4311 if (rc != ERROR_SUCCESS)
4314 /* then handle Commit Actions */
4315 rc = execute_script(package,COMMIT_SCRIPT);
4320 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4322 static const WCHAR RunOnce[] = {
4323 'S','o','f','t','w','a','r','e','\\',
4324 'M','i','c','r','o','s','o','f','t','\\',
4325 'W','i','n','d','o','w','s','\\',
4326 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4327 'R','u','n','O','n','c','e',0};
4328 static const WCHAR InstallRunOnce[] = {
4329 'S','o','f','t','w','a','r','e','\\',
4330 'M','i','c','r','o','s','o','f','t','\\',
4331 'W','i','n','d','o','w','s','\\',
4332 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4333 'I','n','s','t','a','l','l','e','r','\\',
4334 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4336 static const WCHAR msiexec_fmt[] = {
4338 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4339 '\"','%','s','\"',0};
4340 static const WCHAR install_fmt[] = {
4341 '/','I',' ','\"','%','s','\"',' ',
4342 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4343 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4344 WCHAR buffer[256], sysdir[MAX_PATH];
4346 WCHAR squished_pc[100];
4348 squash_guid(package->ProductCode,squished_pc);
4350 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4351 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4352 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4355 msi_reg_set_val_str( hkey, squished_pc, buffer );
4358 TRACE("Reboot command %s\n",debugstr_w(buffer));
4360 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4361 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4363 msi_reg_set_val_str( hkey, squished_pc, buffer );
4366 return ERROR_INSTALL_SUSPEND;
4369 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4375 * We are currently doing what should be done here in the top level Install
4376 * however for Administrative and uninstalls this step will be needed
4378 if (!package->PackagePath)
4379 return ERROR_SUCCESS;
4381 msi_set_sourcedir_props(package, TRUE);
4383 attrib = GetFileAttributesW(package->db->path);
4384 if (attrib == INVALID_FILE_ATTRIBUTES)
4390 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4391 package->Context, MSICODE_PRODUCT,
4392 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4393 if (rc == ERROR_MORE_DATA)
4395 prompt = msi_alloc(size * sizeof(WCHAR));
4396 MsiSourceListGetInfoW(package->ProductCode, NULL,
4397 package->Context, MSICODE_PRODUCT,
4398 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4401 prompt = strdupW(package->db->path);
4403 msg = generate_error_string(package,1302,1,prompt);
4404 while(attrib == INVALID_FILE_ATTRIBUTES)
4406 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4409 rc = ERROR_INSTALL_USEREXIT;
4412 attrib = GetFileAttributesW(package->db->path);
4418 return ERROR_SUCCESS;
4423 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4430 static const WCHAR szPropKeys[][80] =
4432 {'P','r','o','d','u','c','t','I','D',0},
4433 {'U','S','E','R','N','A','M','E',0},
4434 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4438 static const WCHAR szRegKeys[][80] =
4440 {'P','r','o','d','u','c','t','I','D',0},
4441 {'R','e','g','O','w','n','e','r',0},
4442 {'R','e','g','C','o','m','p','a','n','y',0},
4446 if (msi_check_unpublish(package))
4448 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4449 return ERROR_SUCCESS;
4452 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4454 return ERROR_SUCCESS;
4456 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4458 if (rc != ERROR_SUCCESS)
4461 for( i = 0; szPropKeys[i][0]; i++ )
4463 buffer = msi_dup_property( package, szPropKeys[i] );
4464 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4469 msi_free(productid);
4472 /* FIXME: call ui_actiondata */
4478 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4482 package->script->InWhatSequence |= SEQUENCE_EXEC;
4483 rc = ACTION_ProcessExecSequence(package,FALSE);
4488 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4490 MSIPACKAGE *package = param;
4491 LPCWSTR compgroupid, component, feature, qualifier, text;
4492 LPWSTR advertise = NULL, output = NULL;
4500 feature = MSI_RecordGetString(rec, 5);
4501 feat = get_loaded_feature(package, feature);
4503 return ERROR_SUCCESS;
4505 if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
4506 feat->ActionRequest != INSTALLSTATE_SOURCE &&
4507 feat->ActionRequest != INSTALLSTATE_ADVERTISED)
4509 TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
4510 feat->Action = feat->Installed;
4511 return ERROR_SUCCESS;
4514 component = MSI_RecordGetString(rec, 3);
4515 comp = get_loaded_component(package, component);
4517 return ERROR_SUCCESS;
4519 compgroupid = MSI_RecordGetString(rec,1);
4520 qualifier = MSI_RecordGetString(rec,2);
4522 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4523 if (rc != ERROR_SUCCESS)
4526 text = MSI_RecordGetString(rec,4);
4527 advertise = create_component_advertise_string(package, comp, feature);
4529 sz = strlenW(advertise);
4532 sz += lstrlenW(text);
4535 sz *= sizeof(WCHAR);
4537 output = msi_alloc_zero(sz);
4538 strcpyW(output,advertise);
4539 msi_free(advertise);
4542 strcatW(output,text);
4544 msi_reg_set_val_multi_str( hkey, qualifier, output );
4551 uirow = MSI_CreateRecord( 2 );
4552 MSI_RecordSetStringW( uirow, 1, compgroupid );
4553 MSI_RecordSetStringW( uirow, 2, qualifier);
4554 ui_actiondata( package, szPublishComponents, uirow);
4555 msiobj_release( &uirow->hdr );
4556 /* FIXME: call ui_progress? */
4562 * At present I am ignorning the advertised components part of this and only
4563 * focusing on the qualified component sets
4565 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4569 static const WCHAR ExecSeqQuery[] =
4570 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4571 '`','P','u','b','l','i','s','h',
4572 'C','o','m','p','o','n','e','n','t','`',0};
4574 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4575 if (rc != ERROR_SUCCESS)
4576 return ERROR_SUCCESS;
4578 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4579 msiobj_release(&view->hdr);
4584 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
4586 static const WCHAR szInstallerComponents[] = {
4587 'S','o','f','t','w','a','r','e','\\',
4588 'M','i','c','r','o','s','o','f','t','\\',
4589 'I','n','s','t','a','l','l','e','r','\\',
4590 'C','o','m','p','o','n','e','n','t','s','\\',0};
4592 MSIPACKAGE *package = param;
4593 LPCWSTR compgroupid, component, feature, qualifier;
4597 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
4600 feature = MSI_RecordGetString( rec, 5 );
4601 feat = get_loaded_feature( package, feature );
4603 return ERROR_SUCCESS;
4605 if (feat->ActionRequest != INSTALLSTATE_ABSENT)
4607 TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
4608 feat->Action = feat->Installed;
4609 return ERROR_SUCCESS;
4612 component = MSI_RecordGetString( rec, 3 );
4613 comp = get_loaded_component( package, component );
4615 return ERROR_SUCCESS;
4617 compgroupid = MSI_RecordGetString( rec, 1 );
4618 qualifier = MSI_RecordGetString( rec, 2 );
4620 squash_guid( compgroupid, squashed );
4621 strcpyW( keypath, szInstallerComponents );
4622 strcatW( keypath, squashed );
4624 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
4625 if (res != ERROR_SUCCESS)
4627 WARN("Unable to delete component key %d\n", res);
4630 uirow = MSI_CreateRecord( 2 );
4631 MSI_RecordSetStringW( uirow, 1, compgroupid );
4632 MSI_RecordSetStringW( uirow, 2, qualifier );
4633 ui_actiondata( package, szUnpublishComponents, uirow );
4634 msiobj_release( &uirow->hdr );
4636 return ERROR_SUCCESS;
4639 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
4643 static const WCHAR query[] =
4644 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4645 '`','P','u','b','l','i','s','h',
4646 'C','o','m','p','o','n','e','n','t','`',0};
4648 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4649 if (rc != ERROR_SUCCESS)
4650 return ERROR_SUCCESS;
4652 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
4653 msiobj_release( &view->hdr );
4658 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4660 MSIPACKAGE *package = param;
4663 SC_HANDLE hscm, service = NULL;
4664 LPCWSTR comp, depends, pass;
4665 LPWSTR name = NULL, disp = NULL;
4666 LPCWSTR load_order, serv_name, key;
4667 DWORD serv_type, start_type;
4670 static const WCHAR query[] =
4671 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4672 '`','C','o','m','p','o','n','e','n','t','`',' ',
4673 'W','H','E','R','E',' ',
4674 '`','C','o','m','p','o','n','e','n','t','`',' ',
4675 '=','\'','%','s','\'',0};
4677 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4680 ERR("Failed to open the SC Manager!\n");
4684 start_type = MSI_RecordGetInteger(rec, 5);
4685 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4688 depends = MSI_RecordGetString(rec, 8);
4689 if (depends && *depends)
4690 FIXME("Dependency list unhandled!\n");
4692 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4693 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4694 serv_type = MSI_RecordGetInteger(rec, 4);
4695 err_control = MSI_RecordGetInteger(rec, 6);
4696 load_order = MSI_RecordGetString(rec, 7);
4697 serv_name = MSI_RecordGetString(rec, 9);
4698 pass = MSI_RecordGetString(rec, 10);
4699 comp = MSI_RecordGetString(rec, 12);
4701 /* fetch the service path */
4702 row = MSI_QueryGetRecord(package->db, query, comp);
4705 ERR("Control query failed!\n");
4709 key = MSI_RecordGetString(row, 6);
4711 file = get_loaded_file(package, key);
4712 msiobj_release(&row->hdr);
4715 ERR("Failed to load the service file\n");
4719 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4720 start_type, err_control, file->TargetPath,
4721 load_order, NULL, NULL, serv_name, pass);
4724 if (GetLastError() != ERROR_SERVICE_EXISTS)
4725 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4729 CloseServiceHandle(service);
4730 CloseServiceHandle(hscm);
4734 return ERROR_SUCCESS;
4737 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4741 static const WCHAR ExecSeqQuery[] =
4742 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4743 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4745 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4746 if (rc != ERROR_SUCCESS)
4747 return ERROR_SUCCESS;
4749 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4750 msiobj_release(&view->hdr);
4755 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4756 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4758 LPCWSTR *vector, *temp_vector;
4762 static const WCHAR separator[] = {'[','~',']',0};
4765 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4770 vector = msi_alloc(sizeof(LPWSTR));
4778 vector[*numargs - 1] = p;
4780 if ((q = strstrW(p, separator)))
4784 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4790 vector = temp_vector;
4799 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4801 MSIPACKAGE *package = param;
4803 SC_HANDLE scm = NULL, service = NULL;
4804 LPCWSTR *vector = NULL;
4806 DWORD event, numargs;
4807 UINT r = ERROR_FUNCTION_FAILED;
4809 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4810 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4811 return ERROR_SUCCESS;
4813 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4814 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4815 event = MSI_RecordGetInteger(rec, 3);
4817 if (!(event & msidbServiceControlEventStart))
4823 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4826 ERR("Failed to open the service control manager\n");
4830 service = OpenServiceW(scm, name, SERVICE_START);
4833 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
4837 vector = msi_service_args_to_vector(args, &numargs);
4839 if (!StartServiceW(service, numargs, vector) &&
4840 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
4842 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
4849 CloseServiceHandle(service);
4850 CloseServiceHandle(scm);
4858 static UINT ACTION_StartServices( MSIPACKAGE *package )
4863 static const WCHAR query[] = {
4864 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4865 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4867 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4868 if (rc != ERROR_SUCCESS)
4869 return ERROR_SUCCESS;
4871 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4872 msiobj_release(&view->hdr);
4877 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4879 DWORD i, needed, count;
4880 ENUM_SERVICE_STATUSW *dependencies;
4884 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4885 0, &needed, &count))
4888 if (GetLastError() != ERROR_MORE_DATA)
4891 dependencies = msi_alloc(needed);
4895 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4896 needed, &needed, &count))
4899 for (i = 0; i < count; i++)
4901 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4902 SERVICE_STOP | SERVICE_QUERY_STATUS);
4906 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4913 msi_free(dependencies);
4917 static UINT stop_service( LPCWSTR name )
4919 SC_HANDLE scm = NULL, service = NULL;
4920 SERVICE_STATUS status;
4921 SERVICE_STATUS_PROCESS ssp;
4924 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4927 WARN("Failed to open the SCM: %d\n", GetLastError());
4931 service = OpenServiceW(scm, name,
4933 SERVICE_QUERY_STATUS |
4934 SERVICE_ENUMERATE_DEPENDENTS);
4937 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
4941 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4942 sizeof(SERVICE_STATUS_PROCESS), &needed))
4944 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
4948 if (ssp.dwCurrentState == SERVICE_STOPPED)
4951 stop_service_dependents(scm, service);
4953 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4954 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4957 CloseServiceHandle(service);
4958 CloseServiceHandle(scm);
4960 return ERROR_SUCCESS;
4963 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
4965 MSIPACKAGE *package = param;
4970 event = MSI_RecordGetInteger( rec, 3 );
4971 if (!(event & msidbServiceControlEventStop))
4972 return ERROR_SUCCESS;
4974 comp = get_loaded_component( package, MSI_RecordGetString( rec, 6 ) );
4975 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4976 return ERROR_SUCCESS;
4978 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
4979 stop_service( name );
4982 return ERROR_SUCCESS;
4985 static UINT ACTION_StopServices( MSIPACKAGE *package )
4990 static const WCHAR query[] = {
4991 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4992 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4994 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4995 if (rc != ERROR_SUCCESS)
4996 return ERROR_SUCCESS;
4998 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4999 msiobj_release(&view->hdr);
5004 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5006 MSIPACKAGE *package = param;
5010 SC_HANDLE scm = NULL, service = NULL;
5012 event = MSI_RecordGetInteger( rec, 3 );
5013 if (!(event & msidbServiceControlEventDelete))
5014 return ERROR_SUCCESS;
5016 comp = get_loaded_component( package, MSI_RecordGetString(rec, 6) );
5017 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
5018 return ERROR_SUCCESS;
5020 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5021 stop_service( name );
5023 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5026 WARN("Failed to open the SCM: %d\n", GetLastError());
5030 service = OpenServiceW( scm, name, DELETE );
5033 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5037 if (!DeleteService( service ))
5038 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5041 CloseServiceHandle( service );
5042 CloseServiceHandle( scm );
5045 return ERROR_SUCCESS;
5048 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5053 static const WCHAR query[] = {
5054 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5055 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5057 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5058 if (rc != ERROR_SUCCESS)
5059 return ERROR_SUCCESS;
5061 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5062 msiobj_release( &view->hdr );
5067 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
5071 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
5073 if (!lstrcmpW(file->File, filename))
5080 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5082 MSIPACKAGE *package = param;
5083 LPWSTR driver, driver_path, ptr;
5084 WCHAR outpath[MAX_PATH];
5085 MSIFILE *driver_file, *setup_file;
5088 UINT r = ERROR_SUCCESS;
5090 static const WCHAR driver_fmt[] = {
5091 'D','r','i','v','e','r','=','%','s',0};
5092 static const WCHAR setup_fmt[] = {
5093 'S','e','t','u','p','=','%','s',0};
5094 static const WCHAR usage_fmt[] = {
5095 'F','i','l','e','U','s','a','g','e','=','1',0};
5097 desc = MSI_RecordGetString(rec, 3);
5099 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5100 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5104 ERR("ODBC Driver entry not found!\n");
5105 return ERROR_FUNCTION_FAILED;
5108 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
5110 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5111 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
5113 driver = msi_alloc(len * sizeof(WCHAR));
5115 return ERROR_OUTOFMEMORY;
5118 lstrcpyW(ptr, desc);
5119 ptr += lstrlenW(ptr) + 1;
5121 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
5126 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5130 lstrcpyW(ptr, usage_fmt);
5131 ptr += lstrlenW(ptr) + 1;
5134 driver_path = strdupW(driver_file->TargetPath);
5135 ptr = strrchrW(driver_path, '\\');
5136 if (ptr) *ptr = '\0';
5138 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5139 NULL, ODBC_INSTALL_COMPLETE, &usage))
5141 ERR("Failed to install SQL driver!\n");
5142 r = ERROR_FUNCTION_FAILED;
5146 msi_free(driver_path);
5151 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5153 MSIPACKAGE *package = param;
5154 LPWSTR translator, translator_path, ptr;
5155 WCHAR outpath[MAX_PATH];
5156 MSIFILE *translator_file, *setup_file;
5159 UINT r = ERROR_SUCCESS;
5161 static const WCHAR translator_fmt[] = {
5162 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5163 static const WCHAR setup_fmt[] = {
5164 'S','e','t','u','p','=','%','s',0};
5166 desc = MSI_RecordGetString(rec, 3);
5168 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5169 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5171 if (!translator_file)
5173 ERR("ODBC Translator entry not found!\n");
5174 return ERROR_FUNCTION_FAILED;
5177 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
5179 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5181 translator = msi_alloc(len * sizeof(WCHAR));
5183 return ERROR_OUTOFMEMORY;
5186 lstrcpyW(ptr, desc);
5187 ptr += lstrlenW(ptr) + 1;
5189 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
5194 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5199 translator_path = strdupW(translator_file->TargetPath);
5200 ptr = strrchrW(translator_path, '\\');
5201 if (ptr) *ptr = '\0';
5203 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5204 NULL, ODBC_INSTALL_COMPLETE, &usage))
5206 ERR("Failed to install SQL translator!\n");
5207 r = ERROR_FUNCTION_FAILED;
5210 msi_free(translator);
5211 msi_free(translator_path);
5216 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5219 LPCWSTR desc, driver;
5220 WORD request = ODBC_ADD_SYS_DSN;
5223 UINT r = ERROR_SUCCESS;
5225 static const WCHAR attrs_fmt[] = {
5226 'D','S','N','=','%','s',0 };
5228 desc = MSI_RecordGetString(rec, 3);
5229 driver = MSI_RecordGetString(rec, 4);
5230 registration = MSI_RecordGetInteger(rec, 5);
5232 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5233 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5235 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
5236 attrs = msi_alloc(len * sizeof(WCHAR));
5238 return ERROR_OUTOFMEMORY;
5240 len = sprintfW(attrs, attrs_fmt, desc);
5243 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5245 ERR("Failed to install SQL data source!\n");
5246 r = ERROR_FUNCTION_FAILED;
5254 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5259 static const WCHAR driver_query[] = {
5260 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5261 'O','D','B','C','D','r','i','v','e','r',0 };
5263 static const WCHAR translator_query[] = {
5264 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5265 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5267 static const WCHAR source_query[] = {
5268 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5269 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5271 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5272 if (rc != ERROR_SUCCESS)
5273 return ERROR_SUCCESS;
5275 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5276 msiobj_release(&view->hdr);
5278 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5279 if (rc != ERROR_SUCCESS)
5280 return ERROR_SUCCESS;
5282 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5283 msiobj_release(&view->hdr);
5285 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5286 if (rc != ERROR_SUCCESS)
5287 return ERROR_SUCCESS;
5289 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5290 msiobj_release(&view->hdr);
5295 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
5300 desc = MSI_RecordGetString( rec, 3 );
5301 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
5303 WARN("Failed to remove ODBC driver\n");
5307 FIXME("Usage count reached 0\n");
5310 return ERROR_SUCCESS;
5313 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
5318 desc = MSI_RecordGetString( rec, 3 );
5319 if (!SQLRemoveTranslatorW( desc, &usage ))
5321 WARN("Failed to remove ODBC translator\n");
5325 FIXME("Usage count reached 0\n");
5328 return ERROR_SUCCESS;
5331 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
5334 LPCWSTR desc, driver;
5335 WORD request = ODBC_REMOVE_SYS_DSN;
5339 static const WCHAR attrs_fmt[] = {
5340 'D','S','N','=','%','s',0 };
5342 desc = MSI_RecordGetString( rec, 3 );
5343 driver = MSI_RecordGetString( rec, 4 );
5344 registration = MSI_RecordGetInteger( rec, 5 );
5346 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
5347 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
5349 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
5350 attrs = msi_alloc( len * sizeof(WCHAR) );
5352 return ERROR_OUTOFMEMORY;
5354 FIXME("Use ODBCSourceAttribute table\n");
5356 len = sprintfW( attrs, attrs_fmt, desc );
5359 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
5361 WARN("Failed to remove ODBC data source\n");
5365 return ERROR_SUCCESS;
5368 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5373 static const WCHAR driver_query[] = {
5374 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5375 'O','D','B','C','D','r','i','v','e','r',0 };
5377 static const WCHAR translator_query[] = {
5378 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5379 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5381 static const WCHAR source_query[] = {
5382 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5383 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5385 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
5386 if (rc != ERROR_SUCCESS)
5387 return ERROR_SUCCESS;
5389 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
5390 msiobj_release( &view->hdr );
5392 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
5393 if (rc != ERROR_SUCCESS)
5394 return ERROR_SUCCESS;
5396 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
5397 msiobj_release( &view->hdr );
5399 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
5400 if (rc != ERROR_SUCCESS)
5401 return ERROR_SUCCESS;
5403 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
5404 msiobj_release( &view->hdr );
5409 #define ENV_ACT_SETALWAYS 0x1
5410 #define ENV_ACT_SETABSENT 0x2
5411 #define ENV_ACT_REMOVE 0x4
5412 #define ENV_ACT_REMOVEMATCH 0x8
5414 #define ENV_MOD_MACHINE 0x20000000
5415 #define ENV_MOD_APPEND 0x40000000
5416 #define ENV_MOD_PREFIX 0x80000000
5417 #define ENV_MOD_MASK 0xC0000000
5419 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5421 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5423 LPCWSTR cptr = *name;
5425 static const WCHAR prefix[] = {'[','~',']',0};
5426 static const int prefix_len = 3;
5432 *flags |= ENV_ACT_SETALWAYS;
5433 else if (*cptr == '+')
5434 *flags |= ENV_ACT_SETABSENT;
5435 else if (*cptr == '-')
5436 *flags |= ENV_ACT_REMOVE;
5437 else if (*cptr == '!')
5438 *flags |= ENV_ACT_REMOVEMATCH;
5439 else if (*cptr == '*')
5440 *flags |= ENV_MOD_MACHINE;
5450 ERR("Missing environment variable\n");
5451 return ERROR_FUNCTION_FAILED;
5456 LPCWSTR ptr = *value;
5457 if (!strncmpW(ptr, prefix, prefix_len))
5459 if (ptr[prefix_len] == szSemiColon[0])
5461 *flags |= ENV_MOD_APPEND;
5462 *value += lstrlenW(prefix);
5469 else if (lstrlenW(*value) >= prefix_len)
5471 ptr += lstrlenW(ptr) - prefix_len;
5472 if (!lstrcmpW(ptr, prefix))
5474 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
5476 *flags |= ENV_MOD_PREFIX;
5477 /* the "[~]" will be removed by deformat_string */;
5487 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5488 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5489 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5490 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5492 ERR("Invalid flags: %08x\n", *flags);
5493 return ERROR_FUNCTION_FAILED;
5497 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
5499 return ERROR_SUCCESS;
5502 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5504 MSIPACKAGE *package = param;
5505 LPCWSTR name, value;
5506 LPWSTR data = NULL, newval = NULL;
5507 LPWSTR deformatted = NULL, ptr;
5508 DWORD flags, type, size;
5510 HKEY env = NULL, root;
5511 LPCWSTR environment;
5513 static const WCHAR user_env[] =
5514 {'E','n','v','i','r','o','n','m','e','n','t',0};
5515 static const WCHAR machine_env[] =
5516 {'S','y','s','t','e','m','\\',
5517 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5518 'C','o','n','t','r','o','l','\\',
5519 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5520 'E','n','v','i','r','o','n','m','e','n','t',0};
5522 name = MSI_RecordGetString(rec, 2);
5523 value = MSI_RecordGetString(rec, 3);
5525 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
5527 res = env_set_flags(&name, &value, &flags);
5528 if (res != ERROR_SUCCESS || !value)
5531 if (value && !deformat_string(package, value, &deformatted))
5533 res = ERROR_OUTOFMEMORY;
5537 value = deformatted;
5539 if (flags & ENV_MOD_MACHINE)
5541 environment = machine_env;
5542 root = HKEY_LOCAL_MACHINE;
5546 environment = user_env;
5547 root = HKEY_CURRENT_USER;
5550 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5551 KEY_ALL_ACCESS, NULL, &env, NULL);
5552 if (res != ERROR_SUCCESS)
5555 if (flags & ENV_ACT_REMOVE)
5556 FIXME("Not removing environment variable on uninstall!\n");
5560 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5561 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5562 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5565 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
5567 /* Nothing to do. */
5570 res = ERROR_SUCCESS;
5574 /* If we are appending but the string was empty, strip ; */
5575 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
5577 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5578 newval = strdupW(value);
5581 res = ERROR_OUTOFMEMORY;
5587 /* Contrary to MSDN, +-variable to [~];path works */
5588 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
5590 res = ERROR_SUCCESS;
5594 data = msi_alloc(size);
5598 return ERROR_OUTOFMEMORY;
5601 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5602 if (res != ERROR_SUCCESS)
5605 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5607 res = RegDeleteKeyW(env, name);
5611 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
5612 if (flags & ENV_MOD_MASK)
5616 if (flags & ENV_MOD_APPEND) multiplier++;
5617 if (flags & ENV_MOD_PREFIX) multiplier++;
5618 mod_size = lstrlenW(value) * multiplier;
5619 size += mod_size * sizeof(WCHAR);
5622 newval = msi_alloc(size);
5626 res = ERROR_OUTOFMEMORY;
5630 if (flags & ENV_MOD_PREFIX)
5632 lstrcpyW(newval, value);
5633 ptr = newval + lstrlenW(value);
5636 lstrcpyW(ptr, data);
5638 if (flags & ENV_MOD_APPEND)
5640 lstrcatW(newval, value);
5643 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5644 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5647 if (env) RegCloseKey(env);
5648 msi_free(deformatted);
5654 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5658 static const WCHAR ExecSeqQuery[] =
5659 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5660 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5661 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5662 if (rc != ERROR_SUCCESS)
5663 return ERROR_SUCCESS;
5665 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5666 msiobj_release(&view->hdr);
5671 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5682 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5686 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5687 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5689 WARN("Source or dest is directory, not moving\n");
5693 if (options == msidbMoveFileOptionsMove)
5695 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5696 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5699 WARN("MoveFile failed: %d\n", GetLastError());
5705 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5706 ret = CopyFileW(source, dest, FALSE);
5709 WARN("CopyFile failed: %d\n", GetLastError());
5717 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5720 DWORD dirlen, pathlen;
5722 ptr = strrchrW(wildcard, '\\');
5723 dirlen = ptr - wildcard + 1;
5725 pathlen = dirlen + lstrlenW(filename) + 1;
5726 path = msi_alloc(pathlen * sizeof(WCHAR));
5728 lstrcpynW(path, wildcard, dirlen + 1);
5729 lstrcatW(path, filename);
5734 static void free_file_entry(FILE_LIST *file)
5736 msi_free(file->source);
5737 msi_free(file->dest);
5741 static void free_list(FILE_LIST *list)
5743 while (!list_empty(&list->entry))
5745 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5747 list_remove(&file->entry);
5748 free_file_entry(file);
5752 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5754 FILE_LIST *new, *file;
5755 LPWSTR ptr, filename;
5758 new = msi_alloc_zero(sizeof(FILE_LIST));
5762 new->source = strdupW(source);
5763 ptr = strrchrW(dest, '\\') + 1;
5764 filename = strrchrW(new->source, '\\') + 1;
5766 new->sourcename = filename;
5769 new->destname = ptr;
5771 new->destname = new->sourcename;
5773 size = (ptr - dest) + lstrlenW(filename) + 1;
5774 new->dest = msi_alloc(size * sizeof(WCHAR));
5777 free_file_entry(new);
5781 lstrcpynW(new->dest, dest, ptr - dest + 1);
5782 lstrcatW(new->dest, filename);
5784 if (list_empty(&files->entry))
5786 list_add_head(&files->entry, &new->entry);
5790 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5792 if (lstrcmpW(source, file->source) < 0)
5794 list_add_before(&file->entry, &new->entry);
5799 list_add_after(&file->entry, &new->entry);
5803 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5805 WIN32_FIND_DATAW wfd;
5809 FILE_LIST files, *file;
5812 hfile = FindFirstFileW(source, &wfd);
5813 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5815 list_init(&files.entry);
5817 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5819 if (is_dot_dir(wfd.cFileName)) continue;
5821 path = wildcard_to_file(source, wfd.cFileName);
5828 add_wildcard(&files, path, dest);
5832 /* no files match the wildcard */
5833 if (list_empty(&files.entry))
5836 /* only the first wildcard match gets renamed to dest */
5837 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5838 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5839 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5846 /* file->dest may be shorter after the reallocation, so add a NULL
5847 * terminator. This is needed for the call to strrchrW, as there will no
5848 * longer be a NULL terminator within the bounds of the allocation in this case.
5850 file->dest[size - 1] = '\0';
5851 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5853 while (!list_empty(&files.entry))
5855 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5857 msi_move_file(file->source, file->dest, options);
5859 list_remove(&file->entry);
5860 free_file_entry(file);
5871 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5873 MSIPACKAGE *package = param;
5876 LPWSTR destname = NULL;
5877 LPWSTR sourcedir = NULL, destdir = NULL;
5878 LPWSTR source = NULL, dest = NULL;
5881 BOOL ret, wildcards;
5883 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5884 if (!comp || !comp->Enabled ||
5885 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5887 TRACE("Component not set for install, not moving file\n");
5888 return ERROR_SUCCESS;
5891 sourcename = MSI_RecordGetString(rec, 3);
5892 options = MSI_RecordGetInteger(rec, 7);
5894 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5898 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5904 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5907 source = strdupW(sourcedir);
5913 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5914 source = msi_alloc(size * sizeof(WCHAR));
5918 lstrcpyW(source, sourcedir);
5919 if (source[lstrlenW(source) - 1] != '\\')
5920 lstrcatW(source, szBackSlash);
5921 lstrcatW(source, sourcename);
5924 wildcards = strchrW(source, '*') || strchrW(source, '?');
5926 if (MSI_RecordIsNull(rec, 4))
5930 destname = strdupW(sourcename);
5937 destname = strdupW(MSI_RecordGetString(rec, 4));
5939 reduce_to_longfilename(destname);
5944 size = lstrlenW(destname);
5946 size += lstrlenW(destdir) + 2;
5947 dest = msi_alloc(size * sizeof(WCHAR));
5951 lstrcpyW(dest, destdir);
5952 if (dest[lstrlenW(dest) - 1] != '\\')
5953 lstrcatW(dest, szBackSlash);
5956 lstrcatW(dest, destname);
5958 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5960 ret = CreateDirectoryW(destdir, NULL);
5963 WARN("CreateDirectory failed: %d\n", GetLastError());
5964 return ERROR_SUCCESS;
5969 msi_move_file(source, dest, options);
5971 move_files_wildcard(source, dest, options);
5974 msi_free(sourcedir);
5980 return ERROR_SUCCESS;
5983 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5988 static const WCHAR ExecSeqQuery[] =
5989 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5990 '`','M','o','v','e','F','i','l','e','`',0};
5992 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5993 if (rc != ERROR_SUCCESS)
5994 return ERROR_SUCCESS;
5996 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5997 msiobj_release(&view->hdr);
6002 typedef struct tagMSIASSEMBLY
6005 MSICOMPONENT *component;
6006 MSIFEATURE *feature;
6014 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
6016 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
6017 LPVOID pvReserved, HMODULE *phModDll);
6019 static BOOL init_functionpointers(void)
6025 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
6027 hmscoree = LoadLibraryA("mscoree.dll");
6030 WARN("mscoree.dll not available\n");
6034 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
6035 if (!pLoadLibraryShim)
6037 WARN("LoadLibraryShim not available\n");
6038 FreeLibrary(hmscoree);
6042 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
6045 WARN("fusion.dll not available\n");
6046 FreeLibrary(hmscoree);
6050 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
6052 FreeLibrary(hmscoree);
6056 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
6059 IAssemblyCache *cache;
6061 UINT r = ERROR_FUNCTION_FAILED;
6063 TRACE("installing assembly: %s\n", debugstr_w(path));
6065 if (assembly->feature)
6066 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
6068 if (assembly->manifest)
6069 FIXME("Manifest unhandled\n");
6071 if (assembly->application)
6073 FIXME("Assembly should be privately installed\n");
6074 return ERROR_SUCCESS;
6077 if (assembly->attributes == msidbAssemblyAttributesWin32)
6079 FIXME("Win32 assemblies not handled\n");
6080 return ERROR_SUCCESS;
6083 hr = pCreateAssemblyCache(&cache, 0);
6087 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
6089 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
6094 IAssemblyCache_Release(cache);
6098 typedef struct tagASSEMBLY_LIST
6100 MSIPACKAGE *package;
6101 IAssemblyCache *cache;
6102 struct list *assemblies;
6105 typedef struct tagASSEMBLY_NAME
6113 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
6115 ASSEMBLY_NAME *asmname = param;
6116 LPCWSTR name = MSI_RecordGetString(rec, 2);
6117 LPWSTR val = msi_dup_record_field(rec, 3);
6119 static const WCHAR Name[] = {'N','a','m','e',0};
6120 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
6121 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
6122 static const WCHAR PublicKeyToken[] = {
6123 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
6125 if (!strcmpiW(name, Name))
6126 asmname->name = val;
6127 else if (!strcmpiW(name, Version))
6128 asmname->version = val;
6129 else if (!strcmpiW(name, Culture))
6130 asmname->culture = val;
6131 else if (!strcmpiW(name, PublicKeyToken))
6132 asmname->pubkeytoken = val;
6136 return ERROR_SUCCESS;
6139 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
6143 *size = lstrlenW(append) + 1;
6144 *str = msi_alloc((*size) * sizeof(WCHAR));
6145 lstrcpyW(*str, append);
6149 (*size) += lstrlenW(append);
6150 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
6151 lstrcatW(*str, append);
6154 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
6157 ASSEMBLY_INFO asminfo;
6165 static const WCHAR separator[] = {',',' ',0};
6166 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
6167 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
6168 static const WCHAR PublicKeyToken[] = {
6169 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
6170 static const WCHAR query[] = {
6171 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6172 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
6173 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
6174 '=','\'','%','s','\'',0};
6178 ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
6179 ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
6181 r = MSI_OpenQuery(db, &view, query, comp->Component);
6182 if (r != ERROR_SUCCESS)
6183 return ERROR_SUCCESS;
6185 MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
6186 msiobj_release(&view->hdr);
6190 ERR("No assembly name specified!\n");
6194 append_str(&disp, &size, name.name);
6198 append_str(&disp, &size, separator);
6199 append_str(&disp, &size, Version);
6200 append_str(&disp, &size, name.version);
6205 append_str(&disp, &size, separator);
6206 append_str(&disp, &size, Culture);
6207 append_str(&disp, &size, name.culture);
6210 if (name.pubkeytoken)
6212 append_str(&disp, &size, separator);
6213 append_str(&disp, &size, PublicKeyToken);
6214 append_str(&disp, &size, name.pubkeytoken);
6217 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6218 IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
6220 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6224 msi_free(name.name);
6225 msi_free(name.version);
6226 msi_free(name.culture);
6227 msi_free(name.pubkeytoken);
6232 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6234 ASSEMBLY_LIST *list = param;
6235 MSIASSEMBLY *assembly;
6237 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6239 return ERROR_OUTOFMEMORY;
6241 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
6243 if (!assembly->component || !assembly->component->Enabled ||
6244 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
6246 TRACE("Component not set for install, not publishing assembly\n");
6248 return ERROR_SUCCESS;
6251 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6252 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6254 if (!assembly->file)
6256 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6257 return ERROR_FUNCTION_FAILED;
6260 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6261 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6262 assembly->attributes = MSI_RecordGetInteger(rec, 5);
6264 if (assembly->application)
6267 DWORD size = sizeof(version)/sizeof(WCHAR);
6269 /* FIXME: we should probably check the manifest file here */
6271 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6272 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6274 assembly->installed = TRUE;
6278 assembly->installed = check_assembly_installed(list->package->db,
6280 assembly->component);
6282 list_add_head(list->assemblies, &assembly->entry);
6283 return ERROR_SUCCESS;
6286 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6288 IAssemblyCache *cache = NULL;
6294 static const WCHAR query[] =
6295 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6296 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6298 r = MSI_DatabaseOpenViewW(package->db, query, &view);
6299 if (r != ERROR_SUCCESS)
6300 return ERROR_SUCCESS;
6302 hr = pCreateAssemblyCache(&cache, 0);
6304 return ERROR_FUNCTION_FAILED;
6306 list.package = package;
6308 list.assemblies = assemblies;
6310 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6311 msiobj_release(&view->hdr);
6313 IAssemblyCache_Release(cache);
6318 static void free_assemblies(struct list *assemblies)
6320 struct list *item, *cursor;
6322 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6324 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6326 list_remove(&assembly->entry);
6327 msi_free(assembly->application);
6328 msi_free(assembly->manifest);
6333 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6335 MSIASSEMBLY *assembly;
6337 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6339 if (!lstrcmpW(assembly->file->File, file))
6349 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6350 LPWSTR *path, DWORD *attrs, PVOID user)
6352 MSIASSEMBLY *assembly;
6353 WCHAR temppath[MAX_PATH];
6354 struct list *assemblies = user;
6357 if (!find_assembly(assemblies, file, &assembly))
6360 GetTempPathW(MAX_PATH, temppath);
6361 PathAddBackslashW(temppath);
6362 lstrcatW(temppath, assembly->file->FileName);
6364 if (action == MSICABEXTRACT_BEGINEXTRACT)
6366 if (assembly->installed)
6369 *path = strdupW(temppath);
6370 *attrs = assembly->file->Attributes;
6372 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6374 assembly->installed = TRUE;
6376 r = install_assembly(package, assembly, temppath);
6377 if (r != ERROR_SUCCESS)
6378 ERR("Failed to install assembly\n");
6384 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6387 struct list assemblies = LIST_INIT(assemblies);
6388 MSIASSEMBLY *assembly;
6391 if (!init_functionpointers() || !pCreateAssemblyCache)
6392 return ERROR_FUNCTION_FAILED;
6394 r = load_assemblies(package, &assemblies);
6395 if (r != ERROR_SUCCESS)
6398 if (list_empty(&assemblies))
6401 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6404 r = ERROR_OUTOFMEMORY;
6408 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6410 if (assembly->installed && !mi->is_continuous)
6413 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6414 (assembly->file->IsCompressed && !mi->is_extracted))
6418 r = ready_media(package, assembly->file, mi);
6419 if (r != ERROR_SUCCESS)
6421 ERR("Failed to ready media\n");
6426 data.package = package;
6427 data.cb = installassembly_cb;
6428 data.user = &assemblies;
6430 if (assembly->file->IsCompressed &&
6431 !msi_cabextract(package, mi, &data))
6433 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6434 r = ERROR_FUNCTION_FAILED;
6439 if (!assembly->file->IsCompressed)
6441 LPWSTR source = resolve_file_source(package, assembly->file);
6443 r = install_assembly(package, assembly, source);
6444 if (r != ERROR_SUCCESS)
6445 ERR("Failed to install assembly\n");
6450 /* FIXME: write Installer assembly reg values */
6454 free_assemblies(&assemblies);
6458 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6460 LPWSTR key, template, id;
6461 UINT r = ERROR_SUCCESS;
6463 id = msi_dup_property( package, szProductID );
6467 return ERROR_SUCCESS;
6469 template = msi_dup_property( package, szPIDTemplate );
6470 key = msi_dup_property( package, szPIDKEY );
6472 if (key && template)
6474 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6475 r = MSI_SetPropertyW( package, szProductID, key );
6477 msi_free( template );
6482 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6485 package->need_reboot = 1;
6486 return ERROR_SUCCESS;
6489 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6491 TRACE("%p\n", package);
6492 return ERROR_SUCCESS;
6495 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6497 FIXME("%p\n", package);
6498 return ERROR_SUCCESS;
6501 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6503 FIXME("%p\n", package);
6504 return ERROR_SUCCESS;
6507 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6508 LPCSTR action, LPCWSTR table )
6510 static const WCHAR query[] = {
6511 'S','E','L','E','C','T',' ','*',' ',
6512 'F','R','O','M',' ','`','%','s','`',0 };
6513 MSIQUERY *view = NULL;
6517 r = MSI_OpenQuery( package->db, &view, query, table );
6518 if (r == ERROR_SUCCESS)
6520 r = MSI_IterateRecords(view, &count, NULL, package);
6521 msiobj_release(&view->hdr);
6525 FIXME("%s -> %u ignored %s table values\n",
6526 action, count, debugstr_w(table));
6528 return ERROR_SUCCESS;
6531 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6533 static const WCHAR table[] =
6534 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6535 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6538 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6540 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6541 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6544 static UINT ACTION_BindImage( MSIPACKAGE *package )
6546 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6547 return msi_unimplemented_action_stub( package, "BindImage", table );
6550 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6552 static const WCHAR table[] = {
6553 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6554 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6557 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6559 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6560 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6563 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6565 static const WCHAR table[] = {
6566 'E','n','v','i','r','o','n','m','e','n','t',0 };
6567 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6570 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6572 static const WCHAR table[] = {
6573 'M','s','i','A','s','s','e','m','b','l','y',0 };
6574 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6577 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6579 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6580 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6583 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6585 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6586 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6589 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6591 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6592 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6595 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6597 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6598 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6601 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6603 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6604 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6607 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6609 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6610 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6613 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6615 static const WCHAR table[] = { 'D','i','r','e','c','t','o','r','y',0 };
6616 return msi_unimplemented_action_stub( package, "SetODBCFolders", table );
6619 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6621 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6622 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6625 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6627 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6628 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6631 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6633 static const WCHAR table[] = { 'M','I','M','E',0 };
6634 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6637 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6639 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6640 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6643 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
6647 const WCHAR *action;
6648 UINT (*handler)(MSIPACKAGE *);
6652 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6653 { szAppSearch, ACTION_AppSearch },
6654 { szBindImage, ACTION_BindImage },
6655 { szCCPSearch, ACTION_CCPSearch },
6656 { szCostFinalize, ACTION_CostFinalize },
6657 { szCostInitialize, ACTION_CostInitialize },
6658 { szCreateFolders, ACTION_CreateFolders },
6659 { szCreateShortcuts, ACTION_CreateShortcuts },
6660 { szDeleteServices, ACTION_DeleteServices },
6661 { szDisableRollback, ACTION_DisableRollback },
6662 { szDuplicateFiles, ACTION_DuplicateFiles },
6663 { szExecuteAction, ACTION_ExecuteAction },
6664 { szFileCost, ACTION_FileCost },
6665 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6666 { szForceReboot, ACTION_ForceReboot },
6667 { szInstallAdminPackage, ACTION_InstallAdminPackage },
6668 { szInstallExecute, ACTION_InstallExecute },
6669 { szInstallExecuteAgain, ACTION_InstallExecute },
6670 { szInstallFiles, ACTION_InstallFiles},
6671 { szInstallFinalize, ACTION_InstallFinalize },
6672 { szInstallInitialize, ACTION_InstallInitialize },
6673 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6674 { szInstallValidate, ACTION_InstallValidate },
6675 { szIsolateComponents, ACTION_IsolateComponents },
6676 { szLaunchConditions, ACTION_LaunchConditions },
6677 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6678 { szMoveFiles, ACTION_MoveFiles },
6679 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6680 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6681 { szInstallODBC, ACTION_InstallODBC },
6682 { szInstallServices, ACTION_InstallServices },
6683 { szPatchFiles, ACTION_PatchFiles },
6684 { szProcessComponents, ACTION_ProcessComponents },
6685 { szPublishComponents, ACTION_PublishComponents },
6686 { szPublishFeatures, ACTION_PublishFeatures },
6687 { szPublishProduct, ACTION_PublishProduct },
6688 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6689 { szRegisterComPlus, ACTION_RegisterComPlus},
6690 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6691 { szRegisterFonts, ACTION_RegisterFonts },
6692 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6693 { szRegisterProduct, ACTION_RegisterProduct },
6694 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6695 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6696 { szRegisterUser, ACTION_RegisterUser },
6697 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6698 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6699 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6700 { szRemoveFiles, ACTION_RemoveFiles },
6701 { szRemoveFolders, ACTION_RemoveFolders },
6702 { szRemoveIniValues, ACTION_RemoveIniValues },
6703 { szRemoveODBC, ACTION_RemoveODBC },
6704 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6705 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6706 { szResolveSource, ACTION_ResolveSource },
6707 { szRMCCPSearch, ACTION_RMCCPSearch },
6708 { szScheduleReboot, ACTION_ScheduleReboot },
6709 { szSelfRegModules, ACTION_SelfRegModules },
6710 { szSelfUnregModules, ACTION_SelfUnregModules },
6711 { szSetODBCFolders, ACTION_SetODBCFolders },
6712 { szStartServices, ACTION_StartServices },
6713 { szStopServices, ACTION_StopServices },
6714 { szUnpublishComponents, ACTION_UnpublishComponents },
6715 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6716 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6717 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6718 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6719 { szUnregisterFonts, ACTION_UnregisterFonts },
6720 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6721 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6722 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6723 { szValidateProductID, ACTION_ValidateProductID },
6724 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6725 { szWriteIniValues, ACTION_WriteIniValues },
6726 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6730 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6731 UINT* rc, BOOL force )
6737 if (!run && !package->script->CurrentlyScripting)
6742 if (strcmpW(action,szInstallFinalize) == 0 ||
6743 strcmpW(action,szInstallExecute) == 0 ||
6744 strcmpW(action,szInstallExecuteAgain) == 0)
6749 while (StandardActions[i].action != NULL)
6751 if (strcmpW(StandardActions[i].action, action)==0)
6755 ui_actioninfo(package, action, TRUE, 0);
6756 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6757 ui_actioninfo(package, action, FALSE, *rc);
6761 ui_actionstart(package, action);
6762 if (StandardActions[i].handler)
6764 *rc = StandardActions[i].handler(package);
6768 FIXME("unhandled standard action %s\n",debugstr_w(action));
6769 *rc = ERROR_SUCCESS;
6780 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
6782 UINT rc = ERROR_SUCCESS;
6785 TRACE("Performing action (%s)\n", debugstr_w(action));
6787 handled = ACTION_HandleStandardAction(package, action, &rc, force);
6790 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
6794 WARN("unhandled msi action %s\n", debugstr_w(action));
6795 rc = ERROR_FUNCTION_NOT_CALLED;
6801 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
6803 UINT rc = ERROR_SUCCESS;
6804 BOOL handled = FALSE;
6806 TRACE("Performing action (%s)\n", debugstr_w(action));
6808 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
6811 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
6813 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
6818 WARN("unhandled msi action %s\n", debugstr_w(action));
6819 rc = ERROR_FUNCTION_NOT_CALLED;
6825 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
6827 UINT rc = ERROR_SUCCESS;
6830 static const WCHAR ExecSeqQuery[] =
6831 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6832 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
6833 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
6834 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
6835 static const WCHAR UISeqQuery[] =
6836 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6837 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
6838 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
6839 ' ', '=',' ','%','i',0};
6841 if (needs_ui_sequence(package))
6842 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
6844 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
6848 LPCWSTR action, cond;
6850 TRACE("Running the actions\n");
6852 /* check conditions */
6853 cond = MSI_RecordGetString(row, 2);
6855 /* this is a hack to skip errors in the condition code */
6856 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
6858 msiobj_release(&row->hdr);
6859 return ERROR_SUCCESS;
6862 action = MSI_RecordGetString(row, 1);
6865 ERR("failed to fetch action\n");
6866 msiobj_release(&row->hdr);
6867 return ERROR_FUNCTION_FAILED;
6870 if (needs_ui_sequence(package))
6871 rc = ACTION_PerformUIAction(package, action, -1);
6873 rc = ACTION_PerformAction(package, action, -1, FALSE);
6875 msiobj_release(&row->hdr);
6881 /****************************************************
6882 * TOP level entry points
6883 *****************************************************/
6885 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
6886 LPCWSTR szCommandLine )
6891 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
6892 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
6894 MSI_SetPropertyW(package, szAction, szInstall);
6896 package->script->InWhatSequence = SEQUENCE_INSTALL;
6903 dir = strdupW(szPackagePath);
6904 p = strrchrW(dir, '\\');
6908 file = szPackagePath + (p - dir);
6913 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
6914 GetCurrentDirectoryW(MAX_PATH, dir);
6915 lstrcatW(dir, szBackSlash);
6916 file = szPackagePath;
6919 msi_free( package->PackagePath );
6920 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
6921 if (!package->PackagePath)
6924 return ERROR_OUTOFMEMORY;
6927 lstrcpyW(package->PackagePath, dir);
6928 lstrcatW(package->PackagePath, file);
6931 msi_set_sourcedir_props(package, FALSE);
6934 msi_parse_command_line( package, szCommandLine, FALSE );
6936 msi_apply_transforms( package );
6937 msi_apply_patches( package );
6939 if (!szCommandLine && msi_get_property_int( package, szInstalled, 0 ))
6941 TRACE("setting reinstall property\n");
6942 MSI_SetPropertyW( package, szReinstall, szAll );
6945 /* properties may have been added by a transform */
6946 msi_clone_properties( package );
6947 msi_set_context( package );
6949 if (needs_ui_sequence( package))
6951 package->script->InWhatSequence |= SEQUENCE_UI;
6952 rc = ACTION_ProcessUISequence(package);
6953 ui_exists = ui_sequence_exists(package);
6954 if (rc == ERROR_SUCCESS || !ui_exists)
6956 package->script->InWhatSequence |= SEQUENCE_EXEC;
6957 rc = ACTION_ProcessExecSequence(package, ui_exists);
6961 rc = ACTION_ProcessExecSequence(package, FALSE);
6963 package->script->CurrentlyScripting = FALSE;
6965 /* process the ending type action */
6966 if (rc == ERROR_SUCCESS)
6967 ACTION_PerformActionSequence(package, -1);
6968 else if (rc == ERROR_INSTALL_USEREXIT)
6969 ACTION_PerformActionSequence(package, -2);
6970 else if (rc == ERROR_INSTALL_SUSPEND)
6971 ACTION_PerformActionSequence(package, -4);
6973 ACTION_PerformActionSequence(package, -3);
6975 /* finish up running custom actions */
6976 ACTION_FinishCustomActions(package);
6978 if (rc == ERROR_SUCCESS && package->need_reboot)
6979 return ERROR_SUCCESS_REBOOT_REQUIRED;