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);
51 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
52 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
53 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
54 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action, UINT* rc, BOOL force);
57 * consts and values used
59 static const WCHAR c_colon[] = {'C',':','\\',0};
61 static const WCHAR szCreateFolders[] =
62 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
63 static const WCHAR szCostFinalize[] =
64 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
65 const WCHAR szInstallFiles[] =
66 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
67 const WCHAR szDuplicateFiles[] =
68 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
69 static const WCHAR szWriteRegistryValues[] =
70 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
71 'V','a','l','u','e','s',0};
72 static const WCHAR szCostInitialize[] =
73 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
74 static const WCHAR szFileCost[] =
75 {'F','i','l','e','C','o','s','t',0};
76 static const WCHAR szInstallInitialize[] =
77 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
78 static const WCHAR szInstallValidate[] =
79 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
80 static const WCHAR szLaunchConditions[] =
81 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
82 static const WCHAR szProcessComponents[] =
83 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
84 static const WCHAR szRegisterTypeLibraries[] =
85 {'R','e','g','i','s','t','e','r','T','y','p','e',
86 'L','i','b','r','a','r','i','e','s',0};
87 const WCHAR szRegisterClassInfo[] =
88 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
89 const WCHAR szRegisterProgIdInfo[] =
90 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
91 static const WCHAR szCreateShortcuts[] =
92 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
93 static const WCHAR szPublishProduct[] =
94 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
95 static const WCHAR szWriteIniValues[] =
96 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
97 static const WCHAR szSelfRegModules[] =
98 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
99 static const WCHAR szPublishFeatures[] =
100 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
101 static const WCHAR szRegisterProduct[] =
102 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
103 static const WCHAR szInstallExecute[] =
104 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
105 static const WCHAR szInstallExecuteAgain[] =
106 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
107 'A','g','a','i','n',0};
108 static const WCHAR szInstallFinalize[] =
109 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
110 static const WCHAR szForceReboot[] =
111 {'F','o','r','c','e','R','e','b','o','o','t',0};
112 static const WCHAR szResolveSource[] =
113 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
114 static const WCHAR szAppSearch[] =
115 {'A','p','p','S','e','a','r','c','h',0};
116 static const WCHAR szAllocateRegistrySpace[] =
117 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
118 'S','p','a','c','e',0};
119 static const WCHAR szBindImage[] =
120 {'B','i','n','d','I','m','a','g','e',0};
121 static const WCHAR szCCPSearch[] =
122 {'C','C','P','S','e','a','r','c','h',0};
123 static const WCHAR szDeleteServices[] =
124 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
125 static const WCHAR szDisableRollback[] =
126 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
127 static const WCHAR szExecuteAction[] =
128 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
129 const WCHAR szFindRelatedProducts[] =
130 {'F','i','n','d','R','e','l','a','t','e','d',
131 'P','r','o','d','u','c','t','s',0};
132 static const WCHAR szInstallAdminPackage[] =
133 {'I','n','s','t','a','l','l','A','d','m','i','n',
134 'P','a','c','k','a','g','e',0};
135 static const WCHAR szInstallSFPCatalogFile[] =
136 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
138 static const WCHAR szIsolateComponents[] =
139 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
140 const WCHAR szMigrateFeatureStates[] =
141 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
142 'S','t','a','t','e','s',0};
143 const WCHAR szMoveFiles[] =
144 {'M','o','v','e','F','i','l','e','s',0};
145 static const WCHAR szMsiPublishAssemblies[] =
146 {'M','s','i','P','u','b','l','i','s','h',
147 'A','s','s','e','m','b','l','i','e','s',0};
148 static const WCHAR szMsiUnpublishAssemblies[] =
149 {'M','s','i','U','n','p','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szInstallODBC[] =
152 {'I','n','s','t','a','l','l','O','D','B','C',0};
153 static const WCHAR szInstallServices[] =
154 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
155 const WCHAR szPatchFiles[] =
156 {'P','a','t','c','h','F','i','l','e','s',0};
157 static const WCHAR szPublishComponents[] =
158 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
159 static const WCHAR szRegisterComPlus[] =
160 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
161 const WCHAR szRegisterExtensionInfo[] =
162 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
164 static const WCHAR szRegisterFonts[] =
165 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
166 const WCHAR szRegisterMIMEInfo[] =
167 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
168 static const WCHAR szRegisterUser[] =
169 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
170 const WCHAR szRemoveDuplicateFiles[] =
171 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
172 'F','i','l','e','s',0};
173 static const WCHAR szRemoveEnvironmentStrings[] =
174 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
175 'S','t','r','i','n','g','s',0};
176 const WCHAR szRemoveExistingProducts[] =
177 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
178 'P','r','o','d','u','c','t','s',0};
179 const WCHAR szRemoveFiles[] =
180 {'R','e','m','o','v','e','F','i','l','e','s',0};
181 static const WCHAR szRemoveFolders[] =
182 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
183 static const WCHAR szRemoveIniValues[] =
184 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
185 static const WCHAR szRemoveODBC[] =
186 {'R','e','m','o','v','e','O','D','B','C',0};
187 static const WCHAR szRemoveRegistryValues[] =
188 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
189 'V','a','l','u','e','s',0};
190 static const WCHAR szRemoveShortcuts[] =
191 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
192 static const WCHAR szRMCCPSearch[] =
193 {'R','M','C','C','P','S','e','a','r','c','h',0};
194 static const WCHAR szScheduleReboot[] =
195 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
196 static const WCHAR szSelfUnregModules[] =
197 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
198 static const WCHAR szSetODBCFolders[] =
199 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
200 static const WCHAR szStartServices[] =
201 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
202 static const WCHAR szStopServices[] =
203 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
204 static const WCHAR szUnpublishComponents[] =
205 {'U','n','p','u','b','l','i','s','h',
206 'C','o','m','p','o','n','e','n','t','s',0};
207 static const WCHAR szUnpublishFeatures[] =
208 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
209 const WCHAR szUnregisterClassInfo[] =
210 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
212 static const WCHAR szUnregisterComPlus[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
214 const WCHAR szUnregisterExtensionInfo[] =
215 {'U','n','r','e','g','i','s','t','e','r',
216 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
217 static const WCHAR szUnregisterFonts[] =
218 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
219 const WCHAR szUnregisterMIMEInfo[] =
220 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
221 const WCHAR szUnregisterProgIdInfo[] =
222 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
224 static const WCHAR szUnregisterTypeLibraries[] =
225 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
226 'L','i','b','r','a','r','i','e','s',0};
227 static const WCHAR szValidateProductID[] =
228 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
229 static const WCHAR szWriteEnvironmentStrings[] =
230 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
231 'S','t','r','i','n','g','s',0};
233 /* action handlers */
234 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
238 STANDARDACTIONHANDLER handler;
242 /********************************************************
244 ********************************************************/
246 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
248 static const WCHAR Query_t[] =
249 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
250 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
251 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
252 ' ','\'','%','s','\'',0};
255 row = MSI_QueryGetRecord( package->db, Query_t, action );
258 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
259 msiobj_release(&row->hdr);
262 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
266 static const WCHAR template_s[]=
267 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
269 static const WCHAR template_e[]=
270 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
271 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
273 static const WCHAR format[] =
274 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
278 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
280 sprintfW(message,template_s,timet,action);
282 sprintfW(message,template_e,timet,action,rc);
284 row = MSI_CreateRecord(1);
285 MSI_RecordSetStringW(row,1,message);
287 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
288 msiobj_release(&row->hdr);
291 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
297 LPWSTR prop = NULL, val = NULL;
300 return ERROR_SUCCESS;
312 TRACE("Looking at %s\n",debugstr_w(ptr));
314 ptr2 = strchrW(ptr,'=');
317 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
324 prop = msi_alloc((len+1)*sizeof(WCHAR));
325 memcpy(prop,ptr,len*sizeof(WCHAR));
335 while (*ptr && (quote || (!quote && *ptr!=' ')))
348 val = msi_alloc((len+1)*sizeof(WCHAR));
349 memcpy(val,ptr2,len*sizeof(WCHAR));
352 if (lstrlenW(prop) > 0)
354 TRACE("Found commandline property (%s) = (%s)\n",
355 debugstr_w(prop), debugstr_w(val));
356 MSI_SetPropertyW(package,prop,val);
362 return ERROR_SUCCESS;
366 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
369 LPWSTR p, *ret = NULL;
375 /* count the number of substrings */
376 for ( pc = str, count = 0; pc; count++ )
378 pc = strchrW( pc, sep );
383 /* allocate space for an array of substring pointers and the substrings */
384 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
385 (lstrlenW(str)+1) * sizeof(WCHAR) );
389 /* copy the string and set the pointers */
390 p = (LPWSTR) &ret[count+1];
392 for( count = 0; (ret[count] = p); count++ )
394 p = strchrW( p, sep );
402 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
404 static const WCHAR szProductCode[] =
405 { 'P','r','o','d','u','c','t','C','o','d','e',0 };
406 static const WCHAR szSystemLanguageID[] =
407 { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
409 LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
410 UINT ret = ERROR_FUNCTION_FAILED;
412 prod_code = msi_dup_property( package, szProductCode );
413 patch_product = msi_get_suminfo_product( patch );
415 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
417 if ( strstrW( patch_product, prod_code ) )
419 static const WCHAR zero[] = {'0',0};
423 si = MSI_GetSummaryInformationW( patch, 0 );
426 ERR("no summary information!\n");
430 template = msi_suminfo_dup_string( si, PID_TEMPLATE );
433 ERR("no template property!\n");
434 msiobj_release( &si->hdr );
441 msiobj_release( &si->hdr );
445 langid = msi_dup_property( package, szSystemLanguageID );
448 msiobj_release( &si->hdr );
452 p = strchrW( template, ';' );
453 if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, zero )))
455 TRACE("applicable transform\n");
459 /* FIXME: check platform */
461 msiobj_release( &si->hdr );
465 msi_free( patch_product );
466 msi_free( prod_code );
467 msi_free( template );
473 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
474 MSIDATABASE *patch_db, LPCWSTR name )
476 UINT ret = ERROR_FUNCTION_FAILED;
477 IStorage *stg = NULL;
480 TRACE("%p %s\n", package, debugstr_w(name) );
484 ERR("expected a colon in %s\n", debugstr_w(name));
485 return ERROR_FUNCTION_FAILED;
488 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
491 ret = msi_check_transform_applicable( package, stg );
492 if (ret == ERROR_SUCCESS)
493 msi_table_apply_transform( package->db, stg );
495 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
496 IStorage_Release( stg );
499 ERR("failed to open substorage %s\n", debugstr_w(name));
501 return ERROR_SUCCESS;
504 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
506 static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
507 LPWSTR guid_list, *guids, product_code;
508 UINT i, ret = ERROR_FUNCTION_FAILED;
510 product_code = msi_dup_property( package, szProdCode );
513 /* FIXME: the property ProductCode should be written into the DB somewhere */
514 ERR("no product code to check\n");
515 return ERROR_SUCCESS;
518 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
519 guids = msi_split_string( guid_list, ';' );
520 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
522 if (!lstrcmpW( guids[i], product_code ))
526 msi_free( guid_list );
527 msi_free( product_code );
532 static UINT msi_set_media_source_prop(MSIPACKAGE *package)
535 MSIRECORD *rec = NULL;
540 static const WCHAR szPatch[] = {'P','A','T','C','H',0};
541 static const WCHAR query[] = {'S','E','L','E','C','T',' ',
542 '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
543 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
544 '`','S','o','u','r','c','e','`',' ','I','S',' ',
545 'N','O','T',' ','N','U','L','L',0};
547 r = MSI_DatabaseOpenViewW(package->db, query, &view);
548 if (r != ERROR_SUCCESS)
551 r = MSI_ViewExecute(view, 0);
552 if (r != ERROR_SUCCESS)
555 if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
557 prop = MSI_RecordGetString(rec, 1);
558 patch = msi_dup_property(package, szPatch);
559 MSI_SetPropertyW(package, prop, patch);
564 if (rec) msiobj_release(&rec->hdr);
565 msiobj_release(&view->hdr);
570 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
573 LPWSTR str, *substorage;
574 UINT i, r = ERROR_SUCCESS;
576 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
578 return ERROR_FUNCTION_FAILED;
580 if (msi_check_patch_applicable( package, si ) != ERROR_SUCCESS)
582 TRACE("Patch not applicable\n");
583 return ERROR_SUCCESS;
586 package->patch = msi_alloc(sizeof(MSIPATCHINFO));
588 return ERROR_OUTOFMEMORY;
590 package->patch->patchcode = msi_suminfo_dup_string(si, PID_REVNUMBER);
591 if (!package->patch->patchcode)
592 return ERROR_OUTOFMEMORY;
594 /* enumerate the substorage */
595 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
596 package->patch->transforms = str;
598 substorage = msi_split_string( str, ';' );
599 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
600 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
602 msi_free( substorage );
603 msiobj_release( &si->hdr );
605 msi_set_media_source_prop(package);
610 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
612 MSIDATABASE *patch_db = NULL;
615 TRACE("%p %s\n", package, debugstr_w( file ) );
618 * We probably want to make sure we only open a patch collection here.
619 * Patch collections (.msp) and databases (.msi) have different GUIDs
620 * but currently MSI_OpenDatabaseW will accept both.
622 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
623 if ( r != ERROR_SUCCESS )
625 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
629 msi_parse_patch_summary( package, patch_db );
632 * There might be a CAB file in the patch package,
633 * so append it to the list of storage to search for streams.
635 append_storage_to_db( package->db, patch_db->storage );
637 msiobj_release( &patch_db->hdr );
639 return ERROR_SUCCESS;
642 /* get the PATCH property, and apply all the patches it specifies */
643 static UINT msi_apply_patches( MSIPACKAGE *package )
645 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
646 LPWSTR patch_list, *patches;
647 UINT i, r = ERROR_SUCCESS;
649 patch_list = msi_dup_property( package, szPatch );
651 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
653 patches = msi_split_string( patch_list, ';' );
654 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
655 r = msi_apply_patch_package( package, patches[i] );
658 msi_free( patch_list );
663 static UINT msi_apply_transforms( MSIPACKAGE *package )
665 static const WCHAR szTransforms[] = {
666 'T','R','A','N','S','F','O','R','M','S',0 };
667 LPWSTR xform_list, *xforms;
668 UINT i, r = ERROR_SUCCESS;
670 xform_list = msi_dup_property( package, szTransforms );
671 xforms = msi_split_string( xform_list, ';' );
673 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
675 if (xforms[i][0] == ':')
676 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
678 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
682 msi_free( xform_list );
687 static BOOL ui_sequence_exists( MSIPACKAGE *package )
692 static const WCHAR ExecSeqQuery [] =
693 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
694 '`','I','n','s','t','a','l','l',
695 'U','I','S','e','q','u','e','n','c','e','`',
696 ' ','W','H','E','R','E',' ',
697 '`','S','e','q','u','e','n','c','e','`',' ',
698 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
699 '`','S','e','q','u','e','n','c','e','`',0};
701 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
702 if (rc == ERROR_SUCCESS)
704 msiobj_release(&view->hdr);
711 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
714 LPWSTR source, check;
717 static const WCHAR szOriginalDatabase[] =
718 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
720 db = msi_dup_property( package, szOriginalDatabase );
722 return ERROR_OUTOFMEMORY;
724 p = strrchrW( db, '\\' );
727 p = strrchrW( db, '/' );
731 return ERROR_SUCCESS;
736 source = msi_alloc( len * sizeof(WCHAR) );
737 lstrcpynW( source, db, len );
739 check = msi_dup_property( package, cszSourceDir );
740 if (!check || replace)
741 MSI_SetPropertyW( package, cszSourceDir, source );
745 check = msi_dup_property( package, cszSOURCEDIR );
746 if (!check || replace)
747 MSI_SetPropertyW( package, cszSOURCEDIR, source );
753 return ERROR_SUCCESS;
756 static UINT msi_set_context(MSIPACKAGE *package)
763 static const WCHAR szOne[] = {'1',0};
764 static const WCHAR szAllUsers[] = {'A','L','L','U','S','E','R','S',0};
766 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
768 r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
769 if (r == ERROR_SUCCESS)
772 if (num == 1 || num == 2)
773 package->Context = MSIINSTALLCONTEXT_MACHINE;
776 MSI_SetPropertyW(package, szAllUsers, szOne);
777 return ERROR_SUCCESS;
780 /****************************************************
781 * TOP level entry points
782 *****************************************************/
784 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
785 LPCWSTR szCommandLine )
788 BOOL ui = FALSE, ui_exists;
789 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
790 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
791 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
792 static const WCHAR szReinstall[] = {'R','E','I','N','S','T','A','L','L',0};
793 static const WCHAR szInstalled[] = {'I','n','s','t','a','l','l','e','d',0};
794 static const WCHAR szAll[] = {'A','L','L',0};
796 MSI_SetPropertyW(package, szAction, szInstall);
798 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
800 package->script->InWhatSequence = SEQUENCE_INSTALL;
807 dir = strdupW(szPackagePath);
808 p = strrchrW(dir, '\\');
812 file = szPackagePath + (p - dir);
817 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
818 GetCurrentDirectoryW(MAX_PATH, dir);
819 lstrcatW(dir, cszbs);
820 file = szPackagePath;
823 msi_free( package->PackagePath );
824 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
825 if (!package->PackagePath)
828 return ERROR_OUTOFMEMORY;
831 lstrcpyW(package->PackagePath, dir);
832 lstrcatW(package->PackagePath, file);
835 msi_set_sourcedir_props(package, FALSE);
838 msi_parse_command_line( package, szCommandLine, FALSE );
840 msi_apply_transforms( package );
841 msi_apply_patches( package );
843 if (!szCommandLine && msi_get_property_int( package, szInstalled, 0 ))
845 TRACE("setting reinstall property\n");
846 MSI_SetPropertyW( package, szReinstall, szAll );
849 /* properties may have been added by a transform */
850 msi_clone_properties( package );
851 msi_set_context( package );
853 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
855 package->script->InWhatSequence |= SEQUENCE_UI;
856 rc = ACTION_ProcessUISequence(package);
858 ui_exists = ui_sequence_exists(package);
859 if (rc == ERROR_SUCCESS || !ui_exists)
861 package->script->InWhatSequence |= SEQUENCE_EXEC;
862 rc = ACTION_ProcessExecSequence(package,ui_exists);
866 rc = ACTION_ProcessExecSequence(package,FALSE);
868 package->script->CurrentlyScripting= FALSE;
870 /* process the ending type action */
871 if (rc == ERROR_SUCCESS)
872 ACTION_PerformActionSequence(package,-1,ui);
873 else if (rc == ERROR_INSTALL_USEREXIT)
874 ACTION_PerformActionSequence(package,-2,ui);
875 else if (rc == ERROR_INSTALL_SUSPEND)
876 ACTION_PerformActionSequence(package,-4,ui);
878 ACTION_PerformActionSequence(package,-3,ui);
880 /* finish up running custom actions */
881 ACTION_FinishCustomActions(package);
883 if (rc == ERROR_SUCCESS && package->need_reboot)
884 return ERROR_SUCCESS_REBOOT_REQUIRED;
889 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
891 UINT rc = ERROR_SUCCESS;
893 static const WCHAR ExecSeqQuery[] =
894 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
895 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
896 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
897 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
899 static const WCHAR UISeqQuery[] =
900 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
901 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
902 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
903 ' ', '=',' ','%','i',0};
906 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
908 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
912 LPCWSTR action, cond;
914 TRACE("Running the actions\n");
916 /* check conditions */
917 cond = MSI_RecordGetString(row,2);
919 /* this is a hack to skip errors in the condition code */
920 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
923 action = MSI_RecordGetString(row,1);
926 ERR("failed to fetch action\n");
927 rc = ERROR_FUNCTION_FAILED;
932 rc = ACTION_PerformUIAction(package,action,-1);
934 rc = ACTION_PerformAction(package,action,-1,FALSE);
936 msiobj_release(&row->hdr);
947 } iterate_action_param;
949 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
951 iterate_action_param *iap = param;
953 LPCWSTR cond, action;
955 action = MSI_RecordGetString(row,1);
958 ERR("Error is retrieving action name\n");
959 return ERROR_FUNCTION_FAILED;
962 /* check conditions */
963 cond = MSI_RecordGetString(row,2);
965 /* this is a hack to skip errors in the condition code */
966 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
968 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
969 return ERROR_SUCCESS;
973 rc = ACTION_PerformUIAction(iap->package,action,-1);
975 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
977 msi_dialog_check_messages( NULL );
979 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
980 rc = iap->package->CurrentInstallState;
982 if (rc == ERROR_FUNCTION_NOT_CALLED)
985 if (rc != ERROR_SUCCESS)
986 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
991 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
995 static const WCHAR query[] =
996 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
998 ' ','W','H','E','R','E',' ',
999 '`','S','e','q','u','e','n','c','e','`',' ',
1000 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1001 '`','S','e','q','u','e','n','c','e','`',0};
1002 iterate_action_param iap;
1005 * FIXME: probably should be checking UILevel in the
1006 * ACTION_PerformUIAction/ACTION_PerformAction
1007 * rather than saving the UI level here. Those
1008 * two functions can be merged too.
1010 iap.package = package;
1013 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
1015 r = MSI_OpenQuery( package->db, &view, query, szTable );
1016 if (r == ERROR_SUCCESS)
1018 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
1019 msiobj_release(&view->hdr);
1025 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
1029 static const WCHAR ExecSeqQuery[] =
1030 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1031 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
1032 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
1033 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
1034 'O','R','D','E','R',' ', 'B','Y',' ',
1035 '`','S','e','q','u','e','n','c','e','`',0 };
1036 MSIRECORD * row = 0;
1037 static const WCHAR IVQuery[] =
1038 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
1039 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
1040 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
1041 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
1042 ' ','\'', 'I','n','s','t','a','l','l',
1043 'V','a','l','i','d','a','t','e','\'', 0};
1045 iterate_action_param iap;
1047 iap.package = package;
1050 if (package->script->ExecuteSequenceRun)
1052 TRACE("Execute Sequence already Run\n");
1053 return ERROR_SUCCESS;
1056 package->script->ExecuteSequenceRun = TRUE;
1058 /* get the sequence number */
1061 row = MSI_QueryGetRecord(package->db, IVQuery);
1063 return ERROR_FUNCTION_FAILED;
1064 seq = MSI_RecordGetInteger(row,1);
1065 msiobj_release(&row->hdr);
1068 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
1069 if (rc == ERROR_SUCCESS)
1071 TRACE("Running the actions\n");
1073 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
1074 msiobj_release(&view->hdr);
1080 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
1084 static const WCHAR ExecSeqQuery [] =
1085 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1086 '`','I','n','s','t','a','l','l',
1087 'U','I','S','e','q','u','e','n','c','e','`',
1088 ' ','W','H','E','R','E',' ',
1089 '`','S','e','q','u','e','n','c','e','`',' ',
1090 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1091 '`','S','e','q','u','e','n','c','e','`',0};
1092 iterate_action_param iap;
1094 iap.package = package;
1097 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1099 if (rc == ERROR_SUCCESS)
1101 TRACE("Running the actions\n");
1103 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
1104 msiobj_release(&view->hdr);
1110 /********************************************************
1111 * ACTION helper functions and functions that perform the actions
1112 *******************************************************/
1113 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1114 UINT* rc, UINT script, BOOL force )
1119 arc = ACTION_CustomAction(package, action, script, force);
1121 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1130 * A lot of actions are really important even if they don't do anything
1131 * explicit... Lots of properties are set at the beginning of the installation
1132 * CostFinalize does a bunch of work to translate the directories and such
1134 * But until I get write access to the database that is hard, so I am going to
1135 * hack it to see if I can get something to run.
1137 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1139 UINT rc = ERROR_SUCCESS;
1142 TRACE("Performing action (%s)\n",debugstr_w(action));
1144 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1147 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1151 WARN("unhandled msi action %s\n",debugstr_w(action));
1152 rc = ERROR_FUNCTION_NOT_CALLED;
1158 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1160 UINT rc = ERROR_SUCCESS;
1161 BOOL handled = FALSE;
1163 TRACE("Performing action (%s)\n",debugstr_w(action));
1165 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1168 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1170 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1175 WARN("unhandled msi action %s\n",debugstr_w(action));
1176 rc = ERROR_FUNCTION_NOT_CALLED;
1184 * Actual Action Handlers
1187 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1189 MSIPACKAGE *package = param;
1195 dir = MSI_RecordGetString(row,1);
1198 ERR("Unable to get folder id\n");
1199 return ERROR_SUCCESS;
1202 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1205 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1206 return ERROR_SUCCESS;
1209 TRACE("Folder is %s\n",debugstr_w(full_path));
1212 uirow = MSI_CreateRecord(1);
1213 MSI_RecordSetStringW(uirow,1,full_path);
1214 ui_actiondata(package,szCreateFolders,uirow);
1215 msiobj_release( &uirow->hdr );
1217 if (folder->State == 0)
1218 create_full_pathW(full_path);
1222 msi_free(full_path);
1223 return ERROR_SUCCESS;
1226 /* FIXME: probably should merge this with the above function */
1227 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1229 UINT rc = ERROR_SUCCESS;
1231 LPWSTR install_path;
1233 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1235 return ERROR_FUNCTION_FAILED;
1237 /* create the path */
1238 if (folder->State == 0)
1240 create_full_pathW(install_path);
1243 msi_free(install_path);
1248 UINT msi_create_component_directories( MSIPACKAGE *package )
1252 /* create all the folders required by the components are going to install */
1253 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1255 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1257 msi_create_directory( package, comp->Directory );
1260 return ERROR_SUCCESS;
1264 * Also we cannot enable/disable components either, so for now I am just going
1265 * to do all the directories for all the components.
1267 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1269 static const WCHAR ExecSeqQuery[] =
1270 {'S','E','L','E','C','T',' ',
1271 '`','D','i','r','e','c','t','o','r','y','_','`',
1272 ' ','F','R','O','M',' ',
1273 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1277 /* create all the empty folders specified in the CreateFolder table */
1278 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1279 if (rc != ERROR_SUCCESS)
1280 return ERROR_SUCCESS;
1282 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1283 msiobj_release(&view->hdr);
1285 msi_create_component_directories( package );
1290 static UINT load_component( MSIRECORD *row, LPVOID param )
1292 MSIPACKAGE *package = param;
1295 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1297 return ERROR_FUNCTION_FAILED;
1299 list_add_tail( &package->components, &comp->entry );
1301 /* fill in the data */
1302 comp->Component = msi_dup_record_field( row, 1 );
1304 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1306 comp->ComponentId = msi_dup_record_field( row, 2 );
1307 comp->Directory = msi_dup_record_field( row, 3 );
1308 comp->Attributes = MSI_RecordGetInteger(row,4);
1309 comp->Condition = msi_dup_record_field( row, 5 );
1310 comp->KeyPath = msi_dup_record_field( row, 6 );
1312 comp->Installed = INSTALLSTATE_UNKNOWN;
1313 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1315 return ERROR_SUCCESS;
1318 static UINT load_all_components( MSIPACKAGE *package )
1320 static const WCHAR query[] = {
1321 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1322 '`','C','o','m','p','o','n','e','n','t','`',0 };
1326 if (!list_empty(&package->components))
1327 return ERROR_SUCCESS;
1329 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1330 if (r != ERROR_SUCCESS)
1333 r = MSI_IterateRecords(view, NULL, load_component, package);
1334 msiobj_release(&view->hdr);
1339 MSIPACKAGE *package;
1340 MSIFEATURE *feature;
1343 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1347 cl = msi_alloc( sizeof (*cl) );
1349 return ERROR_NOT_ENOUGH_MEMORY;
1350 cl->component = comp;
1351 list_add_tail( &feature->Components, &cl->entry );
1353 return ERROR_SUCCESS;
1356 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1360 fl = msi_alloc( sizeof(*fl) );
1362 return ERROR_NOT_ENOUGH_MEMORY;
1363 fl->feature = child;
1364 list_add_tail( &parent->Children, &fl->entry );
1366 return ERROR_SUCCESS;
1369 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1371 _ilfs* ilfs = param;
1375 component = MSI_RecordGetString(row,1);
1377 /* check to see if the component is already loaded */
1378 comp = get_loaded_component( ilfs->package, component );
1381 ERR("unknown component %s\n", debugstr_w(component));
1382 return ERROR_FUNCTION_FAILED;
1385 add_feature_component( ilfs->feature, comp );
1386 comp->Enabled = TRUE;
1388 return ERROR_SUCCESS;
1391 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1393 MSIFEATURE *feature;
1398 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1400 if ( !lstrcmpW( feature->Feature, name ) )
1407 static UINT load_feature(MSIRECORD * row, LPVOID param)
1409 MSIPACKAGE* package = param;
1410 MSIFEATURE* feature;
1411 static const WCHAR Query1[] =
1412 {'S','E','L','E','C','T',' ',
1413 '`','C','o','m','p','o','n','e','n','t','_','`',
1414 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1415 'C','o','m','p','o','n','e','n','t','s','`',' ',
1416 'W','H','E','R','E',' ',
1417 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1422 /* fill in the data */
1424 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1426 return ERROR_NOT_ENOUGH_MEMORY;
1428 list_init( &feature->Children );
1429 list_init( &feature->Components );
1431 feature->Feature = msi_dup_record_field( row, 1 );
1433 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1435 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1436 feature->Title = msi_dup_record_field( row, 3 );
1437 feature->Description = msi_dup_record_field( row, 4 );
1439 if (!MSI_RecordIsNull(row,5))
1440 feature->Display = MSI_RecordGetInteger(row,5);
1442 feature->Level= MSI_RecordGetInteger(row,6);
1443 feature->Directory = msi_dup_record_field( row, 7 );
1444 feature->Attributes = MSI_RecordGetInteger(row,8);
1446 feature->Installed = INSTALLSTATE_UNKNOWN;
1447 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1449 list_add_tail( &package->features, &feature->entry );
1451 /* load feature components */
1453 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1454 if (rc != ERROR_SUCCESS)
1455 return ERROR_SUCCESS;
1457 ilfs.package = package;
1458 ilfs.feature = feature;
1460 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1461 msiobj_release(&view->hdr);
1463 return ERROR_SUCCESS;
1466 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1468 MSIPACKAGE* package = param;
1469 MSIFEATURE *parent, *child;
1471 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1473 return ERROR_FUNCTION_FAILED;
1475 if (!child->Feature_Parent)
1476 return ERROR_SUCCESS;
1478 parent = find_feature_by_name( package, child->Feature_Parent );
1480 return ERROR_FUNCTION_FAILED;
1482 add_feature_child( parent, child );
1483 return ERROR_SUCCESS;
1486 static UINT load_all_features( MSIPACKAGE *package )
1488 static const WCHAR query[] = {
1489 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1490 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1491 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1495 if (!list_empty(&package->features))
1496 return ERROR_SUCCESS;
1498 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1499 if (r != ERROR_SUCCESS)
1502 r = MSI_IterateRecords( view, NULL, load_feature, package );
1503 if (r != ERROR_SUCCESS)
1506 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1507 msiobj_release( &view->hdr );
1512 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1523 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1525 static const WCHAR query[] = {
1526 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1527 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1528 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1529 MSIQUERY *view = NULL;
1530 MSIRECORD *row = NULL;
1533 TRACE("%s\n", debugstr_w(file->File));
1535 r = MSI_OpenQuery(package->db, &view, query, file->File);
1536 if (r != ERROR_SUCCESS)
1539 r = MSI_ViewExecute(view, NULL);
1540 if (r != ERROR_SUCCESS)
1543 r = MSI_ViewFetch(view, &row);
1544 if (r != ERROR_SUCCESS)
1547 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1548 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1549 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1550 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1551 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1554 if (view) msiobj_release(&view->hdr);
1555 if (row) msiobj_release(&row->hdr);
1559 static UINT load_file(MSIRECORD *row, LPVOID param)
1561 MSIPACKAGE* package = param;
1565 /* fill in the data */
1567 file = msi_alloc_zero( sizeof (MSIFILE) );
1569 return ERROR_NOT_ENOUGH_MEMORY;
1571 file->File = msi_dup_record_field( row, 1 );
1573 component = MSI_RecordGetString( row, 2 );
1574 file->Component = get_loaded_component( package, component );
1576 if (!file->Component)
1578 WARN("Component not found: %s\n", debugstr_w(component));
1579 msi_free(file->File);
1581 return ERROR_SUCCESS;
1584 file->FileName = msi_dup_record_field( row, 3 );
1585 reduce_to_longfilename( file->FileName );
1587 file->ShortName = msi_dup_record_field( row, 3 );
1588 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1590 file->FileSize = MSI_RecordGetInteger( row, 4 );
1591 file->Version = msi_dup_record_field( row, 5 );
1592 file->Language = msi_dup_record_field( row, 6 );
1593 file->Attributes = MSI_RecordGetInteger( row, 7 );
1594 file->Sequence = MSI_RecordGetInteger( row, 8 );
1596 file->state = msifs_invalid;
1598 /* if the compressed bits are not set in the file attributes,
1599 * then read the information from the package word count property
1601 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1603 file->IsCompressed = FALSE;
1605 else if (file->Attributes &
1606 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1608 file->IsCompressed = TRUE;
1610 else if (file->Attributes & msidbFileAttributesNoncompressed)
1612 file->IsCompressed = FALSE;
1616 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1619 load_file_hash(package, file);
1621 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1623 list_add_tail( &package->files, &file->entry );
1625 return ERROR_SUCCESS;
1628 static UINT load_all_files(MSIPACKAGE *package)
1632 static const WCHAR Query[] =
1633 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1634 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1635 '`','S','e','q','u','e','n','c','e','`', 0};
1637 if (!list_empty(&package->files))
1638 return ERROR_SUCCESS;
1640 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1641 if (rc != ERROR_SUCCESS)
1642 return ERROR_SUCCESS;
1644 rc = MSI_IterateRecords(view, NULL, load_file, package);
1645 msiobj_release(&view->hdr);
1647 return ERROR_SUCCESS;
1650 static UINT load_folder( MSIRECORD *row, LPVOID param )
1652 MSIPACKAGE *package = param;
1653 static const WCHAR szDot[] = { '.',0 };
1654 static WCHAR szEmpty[] = { 0 };
1655 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1658 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1660 return ERROR_NOT_ENOUGH_MEMORY;
1662 folder->Directory = msi_dup_record_field( row, 1 );
1664 TRACE("%s\n", debugstr_w(folder->Directory));
1666 p = msi_dup_record_field(row, 3);
1668 /* split src and target dir */
1670 src_short = folder_split_path( p, ':' );
1672 /* split the long and short paths */
1673 tgt_long = folder_split_path( tgt_short, '|' );
1674 src_long = folder_split_path( src_short, '|' );
1676 /* check for no-op dirs */
1677 if (!lstrcmpW(szDot, tgt_short))
1678 tgt_short = szEmpty;
1679 if (!lstrcmpW(szDot, src_short))
1680 src_short = szEmpty;
1683 tgt_long = tgt_short;
1686 src_short = tgt_short;
1687 src_long = tgt_long;
1691 src_long = src_short;
1693 /* FIXME: use the target short path too */
1694 folder->TargetDefault = strdupW(tgt_long);
1695 folder->SourceShortPath = strdupW(src_short);
1696 folder->SourceLongPath = strdupW(src_long);
1699 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1700 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1701 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1703 folder->Parent = msi_dup_record_field( row, 2 );
1705 folder->Property = msi_dup_property( package, folder->Directory );
1707 list_add_tail( &package->folders, &folder->entry );
1709 TRACE("returning %p\n", folder);
1711 return ERROR_SUCCESS;
1714 static UINT load_all_folders( MSIPACKAGE *package )
1716 static const WCHAR query[] = {
1717 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1718 '`','D','i','r','e','c','t','o','r','y','`',0 };
1722 if (!list_empty(&package->folders))
1723 return ERROR_SUCCESS;
1725 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1726 if (r != ERROR_SUCCESS)
1729 r = MSI_IterateRecords(view, NULL, load_folder, package);
1730 msiobj_release(&view->hdr);
1735 * I am not doing any of the costing functionality yet.
1736 * Mostly looking at doing the Component and Feature loading
1738 * The native MSI does A LOT of modification to tables here. Mostly adding
1739 * a lot of temporary columns to the Feature and Component tables.
1741 * note: Native msi also tracks the short filename. But I am only going to
1742 * track the long ones. Also looking at this directory table
1743 * it appears that the directory table does not get the parents
1744 * resolved base on property only based on their entries in the
1747 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1749 static const WCHAR szCosting[] =
1750 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1751 static const WCHAR szZero[] = { '0', 0 };
1753 MSI_SetPropertyW(package, szCosting, szZero);
1754 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1756 load_all_folders( package );
1757 load_all_components( package );
1758 load_all_features( package );
1759 load_all_files( package );
1761 return ERROR_SUCCESS;
1764 static UINT execute_script(MSIPACKAGE *package, UINT script )
1767 UINT rc = ERROR_SUCCESS;
1769 TRACE("Executing Script %i\n",script);
1771 if (!package->script)
1773 ERR("no script!\n");
1774 return ERROR_FUNCTION_FAILED;
1777 for (i = 0; i < package->script->ActionCount[script]; i++)
1780 action = package->script->Actions[script][i];
1781 ui_actionstart(package, action);
1782 TRACE("Executing Action (%s)\n",debugstr_w(action));
1783 rc = ACTION_PerformAction(package, action, script, TRUE);
1784 if (rc != ERROR_SUCCESS)
1787 msi_free_action_script(package, script);
1791 static UINT ACTION_FileCost(MSIPACKAGE *package)
1793 return ERROR_SUCCESS;
1796 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1802 state = MsiQueryProductStateW(package->ProductCode);
1804 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1806 if (!comp->ComponentId)
1809 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1810 comp->Installed = INSTALLSTATE_ABSENT;
1813 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1814 package->Context, comp->ComponentId,
1816 if (r != ERROR_SUCCESS)
1817 comp->Installed = INSTALLSTATE_ABSENT;
1822 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1824 MSIFEATURE *feature;
1827 state = MsiQueryProductStateW(package->ProductCode);
1829 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1831 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1832 feature->Installed = INSTALLSTATE_ABSENT;
1835 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1841 static BOOL process_state_property(MSIPACKAGE* package, int level,
1842 LPCWSTR property, INSTALLSTATE state)
1844 static const WCHAR all[]={'A','L','L',0};
1845 static const WCHAR remove[] = {'R','E','M','O','V','E',0};
1846 static const WCHAR reinstall[] = {'R','E','I','N','S','T','A','L','L',0};
1848 MSIFEATURE *feature;
1850 override = msi_dup_property( package, property );
1854 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1856 if (lstrcmpW(property, remove) &&
1857 (feature->Level <= 0 || feature->Level > level))
1860 if (!strcmpW(property, reinstall)) state = feature->Installed;
1862 if (strcmpiW(override,all)==0)
1863 msi_feature_set_state(package, feature, state);
1866 LPWSTR ptr = override;
1867 LPWSTR ptr2 = strchrW(override,',');
1871 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1872 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1874 msi_feature_set_state(package, feature, state);
1880 ptr2 = strchrW(ptr,',');
1892 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1895 static const WCHAR szlevel[] =
1896 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1897 static const WCHAR szAddLocal[] =
1898 {'A','D','D','L','O','C','A','L',0};
1899 static const WCHAR szAddSource[] =
1900 {'A','D','D','S','O','U','R','C','E',0};
1901 static const WCHAR szRemove[] =
1902 {'R','E','M','O','V','E',0};
1903 static const WCHAR szReinstall[] =
1904 {'R','E','I','N','S','T','A','L','L',0};
1905 static const WCHAR szAdvertise[] =
1906 {'A','D','V','E','R','T','I','S','E',0};
1907 BOOL override = FALSE;
1908 MSICOMPONENT* component;
1909 MSIFEATURE *feature;
1912 /* I do not know if this is where it should happen.. but */
1914 TRACE("Checking Install Level\n");
1916 level = msi_get_property_int(package, szlevel, 1);
1918 /* ok here is the _real_ rub
1919 * all these activation/deactivation things happen in order and things
1920 * later on the list override things earlier on the list.
1921 * 0) INSTALLLEVEL processing
1932 * 11) FILEADDDEFAULT
1934 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1935 * REMOVE are the big ones, since we don't handle administrative installs
1938 override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
1939 override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
1940 override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
1941 override |= process_state_property(package, level, szReinstall, INSTALLSTATE_UNKNOWN);
1942 override |= process_state_property(package, level, szAdvertise, INSTALLSTATE_ADVERTISED);
1946 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1948 BOOL feature_state = ((feature->Level > 0) &&
1949 (feature->Level <= level));
1951 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1953 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1954 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1955 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1956 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1958 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1962 /* disable child features of unselected parent features */
1963 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1967 if (feature->Level > 0 && feature->Level <= level)
1970 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1971 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1976 /* set the Preselected Property */
1977 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1978 static const WCHAR szOne[] = { '1', 0 };
1980 MSI_SetPropertyW(package,szPreselected,szOne);
1984 * now we want to enable or disable components base on feature
1987 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1991 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1992 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1994 if (!feature->Level)
1997 /* features with components that have compressed files are made local */
1998 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2000 if (cl->component->Enabled &&
2001 cl->component->ForceLocalState &&
2002 feature->Action == INSTALLSTATE_SOURCE)
2004 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
2009 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2011 component = cl->component;
2013 if (!component->Enabled)
2016 switch (feature->Action)
2018 case INSTALLSTATE_ABSENT:
2019 component->anyAbsent = 1;
2021 case INSTALLSTATE_ADVERTISED:
2022 component->hasAdvertiseFeature = 1;
2024 case INSTALLSTATE_SOURCE:
2025 component->hasSourceFeature = 1;
2027 case INSTALLSTATE_LOCAL:
2028 component->hasLocalFeature = 1;
2030 case INSTALLSTATE_DEFAULT:
2031 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
2032 component->hasAdvertiseFeature = 1;
2033 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
2034 component->hasSourceFeature = 1;
2036 component->hasLocalFeature = 1;
2044 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2046 /* if the component isn't enabled, leave it alone */
2047 if (!component->Enabled)
2050 /* check if it's local or source */
2051 if (!(component->Attributes & msidbComponentAttributesOptional) &&
2052 (component->hasLocalFeature || component->hasSourceFeature))
2054 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
2055 !component->ForceLocalState)
2056 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
2058 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
2062 /* if any feature is local, the component must be local too */
2063 if (component->hasLocalFeature)
2065 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
2069 if (component->hasSourceFeature)
2071 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
2075 if (component->hasAdvertiseFeature)
2077 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
2081 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
2082 if (component->anyAbsent)
2083 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
2086 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2088 if (component->Action == INSTALLSTATE_DEFAULT)
2090 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
2091 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
2094 TRACE("Result: Component %s (Installed %i, Action %i)\n",
2095 debugstr_w(component->Component), component->Installed, component->Action);
2099 return ERROR_SUCCESS;
2102 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
2104 MSIPACKAGE *package = param;
2109 name = MSI_RecordGetString(row,1);
2111 f = get_loaded_folder(package, name);
2112 if (!f) return ERROR_SUCCESS;
2114 /* reset the ResolvedTarget */
2115 msi_free(f->ResolvedTarget);
2116 f->ResolvedTarget = NULL;
2118 /* This helper function now does ALL the work */
2119 TRACE("Dir %s ...\n",debugstr_w(name));
2120 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2121 TRACE("resolves to %s\n",debugstr_w(path));
2124 return ERROR_SUCCESS;
2127 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2129 MSIPACKAGE *package = param;
2131 MSIFEATURE *feature;
2133 name = MSI_RecordGetString( row, 1 );
2135 feature = get_loaded_feature( package, name );
2137 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2141 Condition = MSI_RecordGetString(row,3);
2143 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2145 int level = MSI_RecordGetInteger(row,2);
2146 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2147 feature->Level = level;
2150 return ERROR_SUCCESS;
2153 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2155 static const WCHAR name_fmt[] =
2156 {'%','u','.','%','u','.','%','u','.','%','u',0};
2157 static const WCHAR name[] = {'\\',0};
2158 VS_FIXEDFILEINFO *lpVer;
2159 WCHAR filever[0x100];
2165 TRACE("%s\n", debugstr_w(filename));
2167 versize = GetFileVersionInfoSizeW( filename, &handle );
2171 version = msi_alloc( versize );
2172 GetFileVersionInfoW( filename, 0, versize, version );
2174 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2176 msi_free( version );
2180 sprintfW( filever, name_fmt,
2181 HIWORD(lpVer->dwFileVersionMS),
2182 LOWORD(lpVer->dwFileVersionMS),
2183 HIWORD(lpVer->dwFileVersionLS),
2184 LOWORD(lpVer->dwFileVersionLS));
2186 msi_free( version );
2188 return strdupW( filever );
2191 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2193 LPWSTR file_version;
2196 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2198 MSICOMPONENT* comp = file->Component;
2204 if (file->IsCompressed)
2205 comp->ForceLocalState = TRUE;
2207 /* calculate target */
2208 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2210 msi_free(file->TargetPath);
2212 TRACE("file %s is named %s\n",
2213 debugstr_w(file->File), debugstr_w(file->FileName));
2215 file->TargetPath = build_directory_name(2, p, file->FileName);
2219 TRACE("file %s resolves to %s\n",
2220 debugstr_w(file->File), debugstr_w(file->TargetPath));
2222 /* don't check files of components that aren't installed */
2223 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2224 comp->Installed == INSTALLSTATE_ABSENT)
2226 file->state = msifs_missing; /* assume files are missing */
2230 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2232 file->state = msifs_missing;
2233 comp->Cost += file->FileSize;
2237 if (file->Version &&
2238 (file_version = msi_get_disk_file_version( file->TargetPath )))
2240 TRACE("new %s old %s\n", debugstr_w(file->Version),
2241 debugstr_w(file_version));
2242 /* FIXME: seems like a bad way to compare version numbers */
2243 if (lstrcmpiW(file_version, file->Version)<0)
2245 file->state = msifs_overwrite;
2246 comp->Cost += file->FileSize;
2249 file->state = msifs_present;
2250 msi_free( file_version );
2253 file->state = msifs_present;
2256 return ERROR_SUCCESS;
2260 * A lot is done in this function aside from just the costing.
2261 * The costing needs to be implemented at some point but for now I am going
2262 * to focus on the directory building
2265 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2267 static const WCHAR ExecSeqQuery[] =
2268 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2269 '`','D','i','r','e','c','t','o','r','y','`',0};
2270 static const WCHAR ConditionQuery[] =
2271 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2272 '`','C','o','n','d','i','t','i','o','n','`',0};
2273 static const WCHAR szCosting[] =
2274 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2275 static const WCHAR szlevel[] =
2276 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2277 static const WCHAR szOutOfDiskSpace[] =
2278 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2279 static const WCHAR szOne[] = { '1', 0 };
2280 static const WCHAR szZero[] = { '0', 0 };
2286 TRACE("Building Directory properties\n");
2288 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2289 if (rc == ERROR_SUCCESS)
2291 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2293 msiobj_release(&view->hdr);
2296 /* read components states from the registry */
2297 ACTION_GetComponentInstallStates(package);
2298 ACTION_GetFeatureInstallStates(package);
2300 TRACE("File calculations\n");
2301 msi_check_file_install_states( package );
2303 TRACE("Evaluating Condition Table\n");
2305 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2306 if (rc == ERROR_SUCCESS)
2308 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2310 msiobj_release(&view->hdr);
2313 TRACE("Enabling or Disabling Components\n");
2314 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2316 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2318 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2319 comp->Enabled = FALSE;
2322 comp->Enabled = TRUE;
2325 MSI_SetPropertyW(package,szCosting,szOne);
2326 /* set default run level if not set */
2327 level = msi_dup_property( package, szlevel );
2329 MSI_SetPropertyW(package,szlevel, szOne);
2332 /* FIXME: check volume disk space */
2333 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2335 return MSI_SetFeatureStates(package);
2338 /* OK this value is "interpreted" and then formatted based on the
2339 first few characters */
2340 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2345 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2351 LPWSTR deformated = NULL;
2354 deformat_string(package, &value[2], &deformated);
2356 /* binary value type */
2360 *size = (strlenW(ptr)/2)+1;
2362 *size = strlenW(ptr)/2;
2364 data = msi_alloc(*size);
2370 /* if uneven pad with a zero in front */
2376 data[count] = (BYTE)strtol(byte,NULL,0);
2378 TRACE("Uneven byte count\n");
2386 data[count] = (BYTE)strtol(byte,NULL,0);
2389 msi_free(deformated);
2391 TRACE("Data %i bytes(%i)\n",*size,count);
2398 deformat_string(package, &value[1], &deformated);
2401 *size = sizeof(DWORD);
2402 data = msi_alloc(*size);
2408 if ( (*p < '0') || (*p > '9') )
2414 if (deformated[0] == '-')
2417 TRACE("DWORD %i\n",*(LPDWORD)data);
2419 msi_free(deformated);
2424 static const WCHAR szMulti[] = {'[','~',']',0};
2433 *type=REG_EXPAND_SZ;
2441 if (strstrW(value,szMulti))
2442 *type = REG_MULTI_SZ;
2444 /* remove initial delimiter */
2445 if (!strncmpW(value, szMulti, 3))
2448 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2450 /* add double NULL terminator */
2451 if (*type == REG_MULTI_SZ)
2453 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2454 data = msi_realloc_zero(data, *size);
2460 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2462 MSIPACKAGE *package = param;
2463 static const WCHAR szHCR[] =
2464 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2465 'R','O','O','T','\\',0};
2466 static const WCHAR szHCU[] =
2467 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2468 'U','S','E','R','\\',0};
2469 static const WCHAR szHLM[] =
2470 {'H','K','E','Y','_','L','O','C','A','L','_',
2471 'M','A','C','H','I','N','E','\\',0};
2472 static const WCHAR szHU[] =
2473 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2475 LPSTR value_data = NULL;
2476 HKEY root_key, hkey;
2479 LPCWSTR szRoot, component, name, key, value;
2484 BOOL check_first = FALSE;
2487 ui_progress(package,2,0,0,0);
2494 component = MSI_RecordGetString(row, 6);
2495 comp = get_loaded_component(package,component);
2497 return ERROR_SUCCESS;
2499 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2501 TRACE("Skipping write due to disabled component %s\n",
2502 debugstr_w(component));
2504 comp->Action = comp->Installed;
2506 return ERROR_SUCCESS;
2509 comp->Action = INSTALLSTATE_LOCAL;
2511 name = MSI_RecordGetString(row, 4);
2512 if( MSI_RecordIsNull(row,5) && name )
2514 /* null values can have special meanings */
2515 if (name[0]=='-' && name[1] == 0)
2516 return ERROR_SUCCESS;
2517 else if ((name[0]=='+' && name[1] == 0) ||
2518 (name[0] == '*' && name[1] == 0))
2523 root = MSI_RecordGetInteger(row,2);
2524 key = MSI_RecordGetString(row, 3);
2526 /* get the root key */
2531 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2532 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2533 if (all_users && all_users[0] == '1')
2535 root_key = HKEY_LOCAL_MACHINE;
2540 root_key = HKEY_CURRENT_USER;
2543 msi_free(all_users);
2546 case 0: root_key = HKEY_CLASSES_ROOT;
2549 case 1: root_key = HKEY_CURRENT_USER;
2552 case 2: root_key = HKEY_LOCAL_MACHINE;
2555 case 3: root_key = HKEY_USERS;
2559 ERR("Unknown root %i\n",root);
2565 return ERROR_SUCCESS;
2567 deformat_string(package, key , &deformated);
2568 size = strlenW(deformated) + strlenW(szRoot) + 1;
2569 uikey = msi_alloc(size*sizeof(WCHAR));
2570 strcpyW(uikey,szRoot);
2571 strcatW(uikey,deformated);
2573 if (RegCreateKeyW( root_key, deformated, &hkey))
2575 ERR("Could not create key %s\n",debugstr_w(deformated));
2576 msi_free(deformated);
2578 return ERROR_SUCCESS;
2580 msi_free(deformated);
2582 value = MSI_RecordGetString(row,5);
2584 value_data = parse_value(package, value, &type, &size);
2587 static const WCHAR szEmpty[] = {0};
2588 value_data = (LPSTR)strdupW(szEmpty);
2589 size = sizeof(szEmpty);
2593 deformat_string(package, name, &deformated);
2597 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2599 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2604 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2605 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2607 TRACE("value %s of %s checked already exists\n",
2608 debugstr_w(deformated), debugstr_w(uikey));
2612 TRACE("Checked and setting value %s of %s\n",
2613 debugstr_w(deformated), debugstr_w(uikey));
2614 if (deformated || size)
2615 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2620 uirow = MSI_CreateRecord(3);
2621 MSI_RecordSetStringW(uirow,2,deformated);
2622 MSI_RecordSetStringW(uirow,1,uikey);
2625 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2627 MSI_RecordSetStringW(uirow,3,value);
2629 ui_actiondata(package,szWriteRegistryValues,uirow);
2630 msiobj_release( &uirow->hdr );
2632 msi_free(value_data);
2633 msi_free(deformated);
2636 return ERROR_SUCCESS;
2639 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2643 static const WCHAR ExecSeqQuery[] =
2644 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2645 '`','R','e','g','i','s','t','r','y','`',0 };
2647 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2648 if (rc != ERROR_SUCCESS)
2649 return ERROR_SUCCESS;
2651 /* increment progress bar each time action data is sent */
2652 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2654 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2656 msiobj_release(&view->hdr);
2660 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2662 package->script->CurrentlyScripting = TRUE;
2664 return ERROR_SUCCESS;
2668 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2673 static const WCHAR q1[]=
2674 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2675 '`','R','e','g','i','s','t','r','y','`',0};
2678 MSIFEATURE *feature;
2681 TRACE("InstallValidate\n");
2683 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2684 if (rc == ERROR_SUCCESS)
2686 MSI_IterateRecords( view, &progress, NULL, package );
2687 msiobj_release( &view->hdr );
2688 total += progress * REG_PROGRESS_VALUE;
2691 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2692 total += COMPONENT_PROGRESS_VALUE;
2694 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2695 total += file->FileSize;
2697 ui_progress(package,0,total,0,0);
2699 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2701 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2702 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2703 feature->ActionRequest);
2706 return ERROR_SUCCESS;
2709 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2711 MSIPACKAGE* package = param;
2712 LPCWSTR cond = NULL;
2713 LPCWSTR message = NULL;
2716 static const WCHAR title[]=
2717 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2719 cond = MSI_RecordGetString(row,1);
2721 r = MSI_EvaluateConditionW(package,cond);
2722 if (r == MSICONDITION_FALSE)
2724 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2727 message = MSI_RecordGetString(row,2);
2728 deformat_string(package,message,&deformated);
2729 MessageBoxW(NULL,deformated,title,MB_OK);
2730 msi_free(deformated);
2733 return ERROR_INSTALL_FAILURE;
2736 return ERROR_SUCCESS;
2739 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2742 MSIQUERY * view = NULL;
2743 static const WCHAR ExecSeqQuery[] =
2744 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2745 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2747 TRACE("Checking launch conditions\n");
2749 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2750 if (rc != ERROR_SUCCESS)
2751 return ERROR_SUCCESS;
2753 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2754 msiobj_release(&view->hdr);
2759 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2763 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2765 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2767 MSIRECORD * row = 0;
2769 LPWSTR deformated,buffer,deformated_name;
2771 static const WCHAR ExecSeqQuery[] =
2772 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2773 '`','R','e','g','i','s','t','r','y','`',' ',
2774 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2775 ' ','=',' ' ,'\'','%','s','\'',0 };
2776 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2777 static const WCHAR fmt2[]=
2778 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2780 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2784 root = MSI_RecordGetInteger(row,2);
2785 key = MSI_RecordGetString(row, 3);
2786 name = MSI_RecordGetString(row, 4);
2787 deformat_string(package, key , &deformated);
2788 deformat_string(package, name, &deformated_name);
2790 len = strlenW(deformated) + 6;
2791 if (deformated_name)
2792 len+=strlenW(deformated_name);
2794 buffer = msi_alloc( len *sizeof(WCHAR));
2796 if (deformated_name)
2797 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2799 sprintfW(buffer,fmt,root,deformated);
2801 msi_free(deformated);
2802 msi_free(deformated_name);
2803 msiobj_release(&row->hdr);
2807 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2809 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2814 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2817 return strdupW( file->TargetPath );
2822 static HKEY openSharedDLLsKey(void)
2825 static const WCHAR path[] =
2826 {'S','o','f','t','w','a','r','e','\\',
2827 'M','i','c','r','o','s','o','f','t','\\',
2828 'W','i','n','d','o','w','s','\\',
2829 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2830 'S','h','a','r','e','d','D','L','L','s',0};
2832 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2836 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2841 DWORD sz = sizeof(count);
2844 hkey = openSharedDLLsKey();
2845 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2846 if (rc != ERROR_SUCCESS)
2852 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2856 hkey = openSharedDLLsKey();
2858 msi_reg_set_val_dword( hkey, path, count );
2860 RegDeleteValueW(hkey,path);
2866 * Return TRUE if the count should be written out and FALSE if not
2868 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2870 MSIFEATURE *feature;
2874 /* only refcount DLLs */
2875 if (comp->KeyPath == NULL ||
2876 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2877 comp->Attributes & msidbComponentAttributesODBCDataSource)
2881 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2882 write = (count > 0);
2884 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2888 /* increment counts */
2889 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2893 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2896 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2898 if ( cl->component == comp )
2903 /* decrement counts */
2904 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2908 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2911 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2913 if ( cl->component == comp )
2918 /* ref count all the files in the component */
2923 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2925 if (file->Component == comp)
2926 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2930 /* add a count for permanent */
2931 if (comp->Attributes & msidbComponentAttributesPermanent)
2934 comp->RefCount = count;
2937 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2940 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2942 WCHAR squished_pc[GUID_SIZE];
2943 WCHAR squished_cc[GUID_SIZE];
2950 squash_guid(package->ProductCode,squished_pc);
2951 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2953 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2957 ui_progress(package,2,0,0,0);
2958 if (!comp->ComponentId)
2961 squash_guid(comp->ComponentId,squished_cc);
2963 msi_free(comp->FullKeypath);
2964 comp->FullKeypath = resolve_keypath( package, comp );
2966 ACTION_RefCountComponent( package, comp );
2968 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2969 debugstr_w(comp->Component),
2970 debugstr_w(squished_cc),
2971 debugstr_w(comp->FullKeypath),
2974 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2975 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2977 if (!comp->FullKeypath)
2980 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2981 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2984 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2987 if (rc != ERROR_SUCCESS)
2990 if (comp->Attributes & msidbComponentAttributesPermanent)
2992 static const WCHAR szPermKey[] =
2993 { '0','0','0','0','0','0','0','0','0','0','0','0',
2994 '0','0','0','0','0','0','0','0','0','0','0','0',
2995 '0','0','0','0','0','0','0','0',0 };
2997 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3000 if (comp->Action == INSTALLSTATE_LOCAL)
3001 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3007 WCHAR source[MAX_PATH];
3008 WCHAR base[MAX_PATH];
3011 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3012 static const WCHAR query[] = {
3013 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3014 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3015 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3016 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3017 '`','D','i','s','k','I','d','`',0};
3019 file = get_loaded_file(package, comp->KeyPath);
3023 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3024 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3025 ptr2 = strrchrW(source, '\\') + 1;
3026 msiobj_release(&row->hdr);
3028 lstrcpyW(base, package->PackagePath);
3029 ptr = strrchrW(base, '\\');
3032 sourcepath = resolve_file_source(package, file);
3033 ptr = sourcepath + lstrlenW(base);
3034 lstrcpyW(ptr2, ptr);
3035 msi_free(sourcepath);
3037 msi_reg_set_val_str(hkey, squished_pc, source);
3041 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
3043 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3044 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3046 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3050 uirow = MSI_CreateRecord(3);
3051 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3052 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3053 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3054 ui_actiondata(package,szProcessComponents,uirow);
3055 msiobj_release( &uirow->hdr );
3058 return ERROR_SUCCESS;
3069 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3070 LPWSTR lpszName, LONG_PTR lParam)
3073 typelib_struct *tl_struct = (typelib_struct*) lParam;
3074 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3078 if (!IS_INTRESOURCE(lpszName))
3080 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3084 sz = strlenW(tl_struct->source)+4;
3085 sz *= sizeof(WCHAR);
3087 if ((INT_PTR)lpszName == 1)
3088 tl_struct->path = strdupW(tl_struct->source);
3091 tl_struct->path = msi_alloc(sz);
3092 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3095 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3096 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3099 msi_free(tl_struct->path);
3100 tl_struct->path = NULL;
3105 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3106 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3108 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3112 msi_free(tl_struct->path);
3113 tl_struct->path = NULL;
3115 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3116 ITypeLib_Release(tl_struct->ptLib);
3121 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3123 MSIPACKAGE* package = param;
3127 typelib_struct tl_struct;
3132 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3134 component = MSI_RecordGetString(row,3);
3135 comp = get_loaded_component(package,component);
3137 return ERROR_SUCCESS;
3139 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3141 TRACE("Skipping typelib reg due to disabled component\n");
3143 comp->Action = comp->Installed;
3145 return ERROR_SUCCESS;
3148 comp->Action = INSTALLSTATE_LOCAL;
3150 file = get_loaded_file( package, comp->KeyPath );
3152 return ERROR_SUCCESS;
3154 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3158 guid = MSI_RecordGetString(row,1);
3159 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3160 tl_struct.source = strdupW( file->TargetPath );
3161 tl_struct.path = NULL;
3163 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3164 (LONG_PTR)&tl_struct);
3172 helpid = MSI_RecordGetString(row,6);
3175 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3176 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3180 ERR("Failed to register type library %s\n",
3181 debugstr_w(tl_struct.path));
3184 ui_actiondata(package,szRegisterTypeLibraries,row);
3186 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3189 ITypeLib_Release(tl_struct.ptLib);
3190 msi_free(tl_struct.path);
3193 ERR("Failed to load type library %s\n",
3194 debugstr_w(tl_struct.source));
3196 FreeLibrary(module);
3197 msi_free(tl_struct.source);
3201 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3204 ERR("Failed to load type library: %08x\n", hr);
3205 return ERROR_FUNCTION_FAILED;
3208 ITypeLib_Release(tlib);
3211 return ERROR_SUCCESS;
3214 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3217 * OK this is a bit confusing.. I am given a _Component key and I believe
3218 * that the file that is being registered as a type library is the "key file
3219 * of that component" which I interpret to mean "The file in the KeyPath of
3224 static const WCHAR Query[] =
3225 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3226 '`','T','y','p','e','L','i','b','`',0};
3228 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3229 if (rc != ERROR_SUCCESS)
3230 return ERROR_SUCCESS;
3232 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3233 msiobj_release(&view->hdr);
3237 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3239 MSIPACKAGE *package = param;
3240 LPWSTR target_file, target_folder, filename;
3241 LPCWSTR buffer, extension;
3243 static const WCHAR szlnk[]={'.','l','n','k',0};
3244 IShellLinkW *sl = NULL;
3245 IPersistFile *pf = NULL;
3248 buffer = MSI_RecordGetString(row,4);
3249 comp = get_loaded_component(package,buffer);
3251 return ERROR_SUCCESS;
3253 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3255 TRACE("Skipping shortcut creation due to disabled component\n");
3257 comp->Action = comp->Installed;
3259 return ERROR_SUCCESS;
3262 comp->Action = INSTALLSTATE_LOCAL;
3264 ui_actiondata(package,szCreateShortcuts,row);
3266 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3267 &IID_IShellLinkW, (LPVOID *) &sl );
3271 ERR("CLSID_ShellLink not available\n");
3275 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3278 ERR("QueryInterface(IID_IPersistFile) failed\n");
3282 buffer = MSI_RecordGetString(row,2);
3283 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3285 /* may be needed because of a bug somewhere else */
3286 create_full_pathW(target_folder);
3288 filename = msi_dup_record_field( row, 3 );
3289 reduce_to_longfilename(filename);
3291 extension = strchrW(filename,'.');
3292 if (!extension || strcmpiW(extension,szlnk))
3294 int len = strlenW(filename);
3295 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3296 memcpy(filename + len, szlnk, sizeof(szlnk));
3298 target_file = build_directory_name(2, target_folder, filename);
3299 msi_free(target_folder);
3302 buffer = MSI_RecordGetString(row,5);
3303 if (strchrW(buffer,'['))
3306 deformat_string(package,buffer,&deformated);
3307 IShellLinkW_SetPath(sl,deformated);
3308 msi_free(deformated);
3312 FIXME("poorly handled shortcut format, advertised shortcut\n");
3313 IShellLinkW_SetPath(sl,comp->FullKeypath);
3316 if (!MSI_RecordIsNull(row,6))
3319 buffer = MSI_RecordGetString(row,6);
3320 deformat_string(package,buffer,&deformated);
3321 IShellLinkW_SetArguments(sl,deformated);
3322 msi_free(deformated);
3325 if (!MSI_RecordIsNull(row,7))
3327 buffer = MSI_RecordGetString(row,7);
3328 IShellLinkW_SetDescription(sl,buffer);
3331 if (!MSI_RecordIsNull(row,8))
3332 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3334 if (!MSI_RecordIsNull(row,9))
3339 buffer = MSI_RecordGetString(row,9);
3341 Path = build_icon_path(package,buffer);
3342 index = MSI_RecordGetInteger(row,10);
3344 /* no value means 0 */
3345 if (index == MSI_NULL_INTEGER)
3348 IShellLinkW_SetIconLocation(sl,Path,index);
3352 if (!MSI_RecordIsNull(row,11))
3353 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3355 if (!MSI_RecordIsNull(row,12))
3358 buffer = MSI_RecordGetString(row,12);
3359 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3361 IShellLinkW_SetWorkingDirectory(sl,Path);
3365 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3366 IPersistFile_Save(pf,target_file,FALSE);
3368 msi_free(target_file);
3372 IPersistFile_Release( pf );
3374 IShellLinkW_Release( sl );
3376 return ERROR_SUCCESS;
3379 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3384 static const WCHAR Query[] =
3385 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3386 '`','S','h','o','r','t','c','u','t','`',0};
3388 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3389 if (rc != ERROR_SUCCESS)
3390 return ERROR_SUCCESS;
3392 res = CoInitialize( NULL );
3394 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3395 msiobj_release(&view->hdr);
3403 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3405 MSIPACKAGE* package = param;
3414 FileName = MSI_RecordGetString(row,1);
3417 ERR("Unable to get FileName\n");
3418 return ERROR_SUCCESS;
3421 FilePath = build_icon_path(package,FileName);
3423 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3425 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3426 FILE_ATTRIBUTE_NORMAL, NULL);
3428 if (the_file == INVALID_HANDLE_VALUE)
3430 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3432 return ERROR_SUCCESS;
3439 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3440 if (rc != ERROR_SUCCESS)
3442 ERR("Failed to get stream\n");
3443 CloseHandle(the_file);
3444 DeleteFileW(FilePath);
3447 WriteFile(the_file,buffer,sz,&write,NULL);
3448 } while (sz == 1024);
3452 CloseHandle(the_file);
3454 uirow = MSI_CreateRecord(1);
3455 MSI_RecordSetStringW(uirow,1,FileName);
3456 ui_actiondata(package,szPublishProduct,uirow);
3457 msiobj_release( &uirow->hdr );
3459 return ERROR_SUCCESS;
3462 static UINT msi_publish_icons(MSIPACKAGE *package)
3467 static const WCHAR query[]= {
3468 'S','E','L','E','C','T',' ','*',' ',
3469 'F','R','O','M',' ','`','I','c','o','n','`',0};
3471 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3472 if (r == ERROR_SUCCESS)
3474 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3475 msiobj_release(&view->hdr);
3478 return ERROR_SUCCESS;
3481 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3487 MSISOURCELISTINFO *info;
3489 static const WCHAR szEmpty[] = {0};
3490 static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
3492 r = RegCreateKeyW(hkey, szSourceList, &source);
3493 if (r != ERROR_SUCCESS)
3496 RegCloseKey(source);
3498 buffer = strrchrW(package->PackagePath, '\\') + 1;
3499 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3500 package->Context, MSICODE_PRODUCT,
3501 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3502 if (r != ERROR_SUCCESS)
3505 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3506 package->Context, MSICODE_PRODUCT,
3507 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3508 if (r != ERROR_SUCCESS)
3511 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3512 package->Context, MSICODE_PRODUCT,
3513 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3514 if (r != ERROR_SUCCESS)
3517 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3519 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3520 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3521 info->options, info->value);
3523 MsiSourceListSetInfoW(package->ProductCode, NULL,
3524 info->context, info->options,
3525 info->property, info->value);
3528 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3530 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3531 disk->context, disk->options,
3532 disk->disk_id, disk->volume_label, disk->disk_prompt);
3535 return ERROR_SUCCESS;
3538 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3540 MSIHANDLE hdb, suminfo;
3541 WCHAR guids[MAX_PATH];
3542 WCHAR packcode[SQUISH_GUID_SIZE];
3549 static const WCHAR szProductLanguage[] =
3550 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3551 static const WCHAR szARPProductIcon[] =
3552 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3553 static const WCHAR szProductVersion[] =
3554 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3555 static const WCHAR szAssignment[] =
3556 {'A','s','s','i','g','n','m','e','n','t',0};
3557 static const WCHAR szAdvertiseFlags[] =
3558 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3559 static const WCHAR szClients[] =
3560 {'C','l','i','e','n','t','s',0};
3561 static const WCHAR szColon[] = {':',0};
3563 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3564 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3567 langid = msi_get_property_int(package, szProductLanguage, 0);
3568 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3571 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3573 buffer = msi_dup_property(package, szARPProductIcon);
3576 LPWSTR path = build_icon_path(package,buffer);
3577 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3582 buffer = msi_dup_property(package, szProductVersion);
3585 DWORD verdword = msi_version_str_to_dword(buffer);
3586 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3590 msi_reg_set_val_dword(hkey, szAssignment, 0);
3591 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3592 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3593 msi_reg_set_val_str(hkey, szClients, szColon);
3595 hdb = alloc_msihandle(&package->db->hdr);
3597 return ERROR_NOT_ENOUGH_MEMORY;
3599 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3600 MsiCloseHandle(hdb);
3601 if (r != ERROR_SUCCESS)
3605 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3606 NULL, guids, &size);
3607 if (r != ERROR_SUCCESS)
3610 ptr = strchrW(guids, ';');
3612 squash_guid(guids, packcode);
3613 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3616 MsiCloseHandle(suminfo);
3617 return ERROR_SUCCESS;
3620 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3625 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3627 static const WCHAR szUpgradeCode[] =
3628 {'U','p','g','r','a','d','e','C','o','d','e',0};
3630 upgrade = msi_dup_property(package, szUpgradeCode);
3632 return ERROR_SUCCESS;
3634 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3636 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3637 if (r != ERROR_SUCCESS)
3642 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3643 if (r != ERROR_SUCCESS)
3647 squash_guid(package->ProductCode, squashed_pc);
3648 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3657 static BOOL msi_check_publish(MSIPACKAGE *package)
3659 MSIFEATURE *feature;
3661 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3663 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3670 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3672 MSIFEATURE *feature;
3674 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3676 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3683 static UINT msi_publish_patch(MSIPACKAGE *package, HKEY prodkey, HKEY hudkey)
3685 WCHAR patch_squashed[GUID_SIZE];
3688 UINT r = ERROR_FUNCTION_FAILED;
3690 static const WCHAR szPatches[] = {'P','a','t','c','h','e','s',0};
3692 res = RegCreateKeyExW(prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
3694 if (res != ERROR_SUCCESS)
3695 return ERROR_FUNCTION_FAILED;
3697 squash_guid(package->patch->patchcode, patch_squashed);
3699 res = RegSetValueExW(patches, szPatches, 0, REG_MULTI_SZ,
3700 (const BYTE *)patch_squashed,
3701 (lstrlenW(patch_squashed) + 1) * sizeof(WCHAR));
3702 if (res != ERROR_SUCCESS)
3705 res = RegSetValueExW(patches, patch_squashed, 0, REG_SZ,
3706 (const BYTE *)package->patch->transforms,
3707 (lstrlenW(package->patch->transforms) + 1) * sizeof(WCHAR));
3708 if (res == ERROR_SUCCESS)
3712 RegCloseKey(patches);
3717 * 99% of the work done here is only done for
3718 * advertised installs. However this is where the
3719 * Icon table is processed and written out
3720 * so that is what I am going to do here.
3722 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3728 /* FIXME: also need to publish if the product is in advertise mode */
3729 if (!msi_check_publish(package))
3730 return ERROR_SUCCESS;
3732 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3734 if (rc != ERROR_SUCCESS)
3737 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3738 NULL, &hudkey, TRUE);
3739 if (rc != ERROR_SUCCESS)
3742 rc = msi_publish_upgrade_code(package);
3743 if (rc != ERROR_SUCCESS)
3748 rc = msi_publish_patch(package, hukey, hudkey);
3749 if (rc != ERROR_SUCCESS)
3753 rc = msi_publish_product_properties(package, hukey);
3754 if (rc != ERROR_SUCCESS)
3757 rc = msi_publish_sourcelist(package, hukey);
3758 if (rc != ERROR_SUCCESS)
3761 rc = msi_publish_icons(package);
3765 RegCloseKey(hudkey);
3770 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3772 MSIPACKAGE *package = param;
3773 LPCWSTR component, section, key, value, identifier, dirproperty;
3774 LPWSTR deformated_section, deformated_key, deformated_value;
3775 LPWSTR folder, filename, fullname = NULL;
3776 LPCWSTR filenameptr;
3780 static const WCHAR szWindowsFolder[] =
3781 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3783 component = MSI_RecordGetString(row, 8);
3784 comp = get_loaded_component(package,component);
3786 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3788 TRACE("Skipping ini file due to disabled component %s\n",
3789 debugstr_w(component));
3791 comp->Action = comp->Installed;
3793 return ERROR_SUCCESS;
3796 comp->Action = INSTALLSTATE_LOCAL;
3798 identifier = MSI_RecordGetString(row,1);
3799 dirproperty = MSI_RecordGetString(row,3);
3800 section = MSI_RecordGetString(row,4);
3801 key = MSI_RecordGetString(row,5);
3802 value = MSI_RecordGetString(row,6);
3803 action = MSI_RecordGetInteger(row,7);
3805 deformat_string(package,section,&deformated_section);
3806 deformat_string(package,key,&deformated_key);
3807 deformat_string(package,value,&deformated_value);
3809 filename = msi_dup_record_field(row, 2);
3810 if (filename && (filenameptr = strchrW(filename, '|')))
3813 filenameptr = filename;
3817 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3819 folder = msi_dup_property( package, dirproperty );
3822 folder = msi_dup_property( package, szWindowsFolder );
3826 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3830 fullname = build_directory_name(2, folder, filenameptr);
3834 TRACE("Adding value %s to section %s in %s\n",
3835 debugstr_w(deformated_key), debugstr_w(deformated_section),
3836 debugstr_w(fullname));
3837 WritePrivateProfileStringW(deformated_section, deformated_key,
3838 deformated_value, fullname);
3840 else if (action == 1)
3843 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3844 returned, 10, fullname);
3845 if (returned[0] == 0)
3847 TRACE("Adding value %s to section %s in %s\n",
3848 debugstr_w(deformated_key), debugstr_w(deformated_section),
3849 debugstr_w(fullname));
3851 WritePrivateProfileStringW(deformated_section, deformated_key,
3852 deformated_value, fullname);
3855 else if (action == 3)
3856 FIXME("Append to existing section not yet implemented\n");
3858 uirow = MSI_CreateRecord(4);
3859 MSI_RecordSetStringW(uirow,1,identifier);
3860 MSI_RecordSetStringW(uirow,2,deformated_section);
3861 MSI_RecordSetStringW(uirow,3,deformated_key);
3862 MSI_RecordSetStringW(uirow,4,deformated_value);
3863 ui_actiondata(package,szWriteIniValues,uirow);
3864 msiobj_release( &uirow->hdr );
3870 msi_free(deformated_key);
3871 msi_free(deformated_value);
3872 msi_free(deformated_section);
3873 return ERROR_SUCCESS;
3876 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3880 static const WCHAR ExecSeqQuery[] =
3881 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3882 '`','I','n','i','F','i','l','e','`',0};
3884 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3885 if (rc != ERROR_SUCCESS)
3887 TRACE("no IniFile table\n");
3888 return ERROR_SUCCESS;
3891 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3892 msiobj_release(&view->hdr);
3896 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3898 MSIPACKAGE *package = param;
3903 static const WCHAR ExeStr[] =
3904 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3905 static const WCHAR close[] = {'\"',0};
3907 PROCESS_INFORMATION info;
3912 memset(&si,0,sizeof(STARTUPINFOW));
3914 filename = MSI_RecordGetString(row,1);
3915 file = get_loaded_file( package, filename );
3919 ERR("Unable to find file id %s\n",debugstr_w(filename));
3920 return ERROR_SUCCESS;
3923 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3925 FullName = msi_alloc(len*sizeof(WCHAR));
3926 strcpyW(FullName,ExeStr);
3927 strcatW( FullName, file->TargetPath );
3928 strcatW(FullName,close);
3930 TRACE("Registering %s\n",debugstr_w(FullName));
3931 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3936 CloseHandle(info.hThread);
3937 msi_dialog_check_messages(info.hProcess);
3938 CloseHandle(info.hProcess);
3944 uirow = MSI_CreateRecord( 2 );
3945 uipath = strdupW( file->TargetPath );
3946 p = strrchrW(uipath,'\\');
3949 MSI_RecordSetStringW( uirow, 1, &p[1] );
3950 MSI_RecordSetStringW( uirow, 2, uipath);
3951 ui_actiondata( package, szSelfRegModules, uirow);
3952 msiobj_release( &uirow->hdr );
3954 /* FIXME: call ui_progress? */
3956 return ERROR_SUCCESS;
3959 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3963 static const WCHAR ExecSeqQuery[] =
3964 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3965 '`','S','e','l','f','R','e','g','`',0};
3967 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3968 if (rc != ERROR_SUCCESS)
3970 TRACE("no SelfReg table\n");
3971 return ERROR_SUCCESS;
3974 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3975 msiobj_release(&view->hdr);
3977 return ERROR_SUCCESS;
3980 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3982 MSIFEATURE *feature;
3985 HKEY userdata = NULL;
3987 if (!msi_check_publish(package))
3988 return ERROR_SUCCESS;
3990 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3992 if (rc != ERROR_SUCCESS)
3995 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3997 if (rc != ERROR_SUCCESS)
4000 /* here the guids are base 85 encoded */
4001 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4007 BOOL absent = FALSE;
4010 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
4011 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
4012 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
4016 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4020 if (feature->Feature_Parent)
4021 size += strlenW( feature->Feature_Parent )+2;
4023 data = msi_alloc(size * sizeof(WCHAR));
4026 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4028 MSICOMPONENT* component = cl->component;
4032 if (component->ComponentId)
4034 TRACE("From %s\n",debugstr_w(component->ComponentId));
4035 CLSIDFromString(component->ComponentId, &clsid);
4036 encode_base85_guid(&clsid,buf);
4037 TRACE("to %s\n",debugstr_w(buf));
4042 if (feature->Feature_Parent)
4044 static const WCHAR sep[] = {'\2',0};
4046 strcatW(data,feature->Feature_Parent);
4049 msi_reg_set_val_str( userdata, feature->Feature, data );
4053 if (feature->Feature_Parent)
4054 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4057 static const WCHAR emptyW[] = {0};
4058 size += sizeof(WCHAR);
4059 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4060 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
4064 size += 2*sizeof(WCHAR);
4065 data = msi_alloc(size);
4068 if (feature->Feature_Parent)
4069 strcpyW( &data[1], feature->Feature_Parent );
4070 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4076 uirow = MSI_CreateRecord( 1 );
4077 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4078 ui_actiondata( package, szPublishFeatures, uirow);
4079 msiobj_release( &uirow->hdr );
4080 /* FIXME: call ui_progress? */
4085 RegCloseKey(userdata);
4089 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4094 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4096 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4098 if (r == ERROR_SUCCESS)
4100 RegDeleteValueW(hkey, feature->Feature);
4104 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4106 if (r == ERROR_SUCCESS)
4108 RegDeleteValueW(hkey, feature->Feature);
4112 return ERROR_SUCCESS;
4115 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4117 MSIFEATURE *feature;
4119 if (!msi_check_unpublish(package))
4120 return ERROR_SUCCESS;
4122 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4124 msi_unpublish_feature(package, feature);
4127 return ERROR_SUCCESS;
4130 static UINT msi_get_local_package_name( LPWSTR path )
4132 static const WCHAR szInstaller[] = {
4133 '\\','I','n','s','t','a','l','l','e','r','\\',0};
4134 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
4138 time = GetTickCount();
4139 GetWindowsDirectoryW( path, MAX_PATH );
4140 lstrcatW( path, szInstaller );
4141 CreateDirectoryW( path, NULL );
4143 len = lstrlenW(path);
4144 for (i=0; i<0x10000; i++)
4146 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
4147 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
4148 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
4149 if (handle != INVALID_HANDLE_VALUE)
4151 CloseHandle(handle);
4154 if (GetLastError() != ERROR_FILE_EXISTS &&
4155 GetLastError() != ERROR_SHARING_VIOLATION)
4156 return ERROR_FUNCTION_FAILED;
4159 return ERROR_SUCCESS;
4162 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
4164 WCHAR packagefile[MAX_PATH];
4167 r = msi_get_local_package_name( packagefile );
4168 if (r != ERROR_SUCCESS)
4171 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
4173 r = CopyFileW( package->db->path, packagefile, FALSE);
4177 ERR("Unable to copy package (%s -> %s) (error %d)\n",
4178 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4179 return ERROR_FUNCTION_FAILED;
4182 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4184 return ERROR_SUCCESS;
4187 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4189 LPWSTR prop, val, key;
4195 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4196 static const WCHAR szWindowsInstaller[] =
4197 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4198 static const WCHAR modpath_fmt[] =
4199 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4200 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4201 static const WCHAR szModifyPath[] =
4202 {'M','o','d','i','f','y','P','a','t','h',0};
4203 static const WCHAR szUninstallString[] =
4204 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4205 static const WCHAR szEstimatedSize[] =
4206 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4207 static const WCHAR szProductLanguage[] =
4208 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4209 static const WCHAR szProductVersion[] =
4210 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4211 static const WCHAR szProductName[] =
4212 {'P','r','o','d','u','c','t','N','a','m','e',0};
4213 static const WCHAR szDisplayName[] =
4214 {'D','i','s','p','l','a','y','N','a','m','e',0};
4215 static const WCHAR szDisplayVersion[] =
4216 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4217 static const WCHAR szManufacturer[] =
4218 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4220 static const LPCSTR propval[] = {
4221 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4222 "ARPCONTACT", "Contact",
4223 "ARPCOMMENTS", "Comments",
4224 "ProductName", "DisplayName",
4225 "ProductVersion", "DisplayVersion",
4226 "ARPHELPLINK", "HelpLink",
4227 "ARPHELPTELEPHONE", "HelpTelephone",
4228 "ARPINSTALLLOCATION", "InstallLocation",
4229 "SourceDir", "InstallSource",
4230 "Manufacturer", "Publisher",
4231 "ARPREADME", "Readme",
4233 "ARPURLINFOABOUT", "URLInfoAbout",
4234 "ARPURLUPDATEINFO", "URLUpdateInfo",
4237 const LPCSTR *p = propval;
4241 prop = strdupAtoW(*p++);
4242 key = strdupAtoW(*p++);
4243 val = msi_dup_property(package, prop);
4244 msi_reg_set_val_str(hkey, key, val);
4250 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4252 size = deformat_string(package, modpath_fmt, &buffer);
4253 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4254 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4257 /* FIXME: Write real Estimated Size when we have it */
4258 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4260 buffer = msi_dup_property(package, szProductName);
4261 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4264 buffer = msi_dup_property(package, cszSourceDir);
4265 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4268 buffer = msi_dup_property(package, szManufacturer);
4269 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4272 GetLocalTime(&systime);
4273 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4274 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4276 langid = msi_get_property_int(package, szProductLanguage, 0);
4277 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4279 buffer = msi_dup_property(package, szProductVersion);
4280 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4283 DWORD verdword = msi_version_str_to_dword(buffer);
4285 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4286 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4287 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4291 return ERROR_SUCCESS;
4294 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4296 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4297 LPWSTR upgrade_code;
4302 static const WCHAR szUpgradeCode[] = {
4303 'U','p','g','r','a','d','e','C','o','d','e',0};
4305 /* FIXME: also need to publish if the product is in advertise mode */
4306 if (!msi_check_publish(package))
4307 return ERROR_SUCCESS;
4309 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4310 if (rc != ERROR_SUCCESS)
4313 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4314 NULL, &props, TRUE);
4315 if (rc != ERROR_SUCCESS)
4318 msi_make_package_local(package, props);
4320 rc = msi_publish_install_properties(package, hkey);
4321 if (rc != ERROR_SUCCESS)
4324 rc = msi_publish_install_properties(package, props);
4325 if (rc != ERROR_SUCCESS)
4328 upgrade_code = msi_dup_property(package, szUpgradeCode);
4331 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4332 squash_guid(package->ProductCode, squashed_pc);
4333 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4334 RegCloseKey(upgrade);
4335 msi_free(upgrade_code);
4341 return ERROR_SUCCESS;
4344 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4346 return execute_script(package,INSTALL_SCRIPT);
4349 static UINT msi_unpublish_product(MSIPACKAGE *package)
4352 LPWSTR remove = NULL;
4353 LPWSTR *features = NULL;
4354 BOOL full_uninstall = TRUE;
4355 MSIFEATURE *feature;
4357 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4358 static const WCHAR szAll[] = {'A','L','L',0};
4359 static const WCHAR szUpgradeCode[] =
4360 {'U','p','g','r','a','d','e','C','o','d','e',0};
4362 remove = msi_dup_property(package, szRemove);
4364 return ERROR_SUCCESS;
4366 features = msi_split_string(remove, ',');
4370 ERR("REMOVE feature list is empty!\n");
4371 return ERROR_FUNCTION_FAILED;
4374 if (!lstrcmpW(features[0], szAll))
4375 full_uninstall = TRUE;
4378 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4380 if (feature->Action != INSTALLSTATE_ABSENT)
4381 full_uninstall = FALSE;
4385 if (!full_uninstall)
4388 MSIREG_DeleteProductKey(package->ProductCode);
4389 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4390 MSIREG_DeleteUninstallKey(package->ProductCode);
4392 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4394 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4395 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4399 MSIREG_DeleteUserProductKey(package->ProductCode);
4400 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4403 upgrade = msi_dup_property(package, szUpgradeCode);
4406 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4413 return ERROR_SUCCESS;
4416 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4420 rc = msi_unpublish_product(package);
4421 if (rc != ERROR_SUCCESS)
4424 /* turn off scheduling */
4425 package->script->CurrentlyScripting= FALSE;
4427 /* first do the same as an InstallExecute */
4428 rc = ACTION_InstallExecute(package);
4429 if (rc != ERROR_SUCCESS)
4432 /* then handle Commit Actions */
4433 rc = execute_script(package,COMMIT_SCRIPT);
4438 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4440 static const WCHAR RunOnce[] = {
4441 'S','o','f','t','w','a','r','e','\\',
4442 'M','i','c','r','o','s','o','f','t','\\',
4443 'W','i','n','d','o','w','s','\\',
4444 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4445 'R','u','n','O','n','c','e',0};
4446 static const WCHAR InstallRunOnce[] = {
4447 'S','o','f','t','w','a','r','e','\\',
4448 'M','i','c','r','o','s','o','f','t','\\',
4449 'W','i','n','d','o','w','s','\\',
4450 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4451 'I','n','s','t','a','l','l','e','r','\\',
4452 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4454 static const WCHAR msiexec_fmt[] = {
4456 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4457 '\"','%','s','\"',0};
4458 static const WCHAR install_fmt[] = {
4459 '/','I',' ','\"','%','s','\"',' ',
4460 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4461 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4462 WCHAR buffer[256], sysdir[MAX_PATH];
4464 WCHAR squished_pc[100];
4466 squash_guid(package->ProductCode,squished_pc);
4468 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4469 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4470 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4473 msi_reg_set_val_str( hkey, squished_pc, buffer );
4476 TRACE("Reboot command %s\n",debugstr_w(buffer));
4478 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4479 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4481 msi_reg_set_val_str( hkey, squished_pc, buffer );
4484 return ERROR_INSTALL_SUSPEND;
4487 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4493 * We are currently doing what should be done here in the top level Install
4494 * however for Administrative and uninstalls this step will be needed
4496 if (!package->PackagePath)
4497 return ERROR_SUCCESS;
4499 msi_set_sourcedir_props(package, TRUE);
4501 attrib = GetFileAttributesW(package->db->path);
4502 if (attrib == INVALID_FILE_ATTRIBUTES)
4508 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4509 package->Context, MSICODE_PRODUCT,
4510 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4511 if (rc == ERROR_MORE_DATA)
4513 prompt = msi_alloc(size * sizeof(WCHAR));
4514 MsiSourceListGetInfoW(package->ProductCode, NULL,
4515 package->Context, MSICODE_PRODUCT,
4516 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4519 prompt = strdupW(package->db->path);
4521 msg = generate_error_string(package,1302,1,prompt);
4522 while(attrib == INVALID_FILE_ATTRIBUTES)
4524 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4527 rc = ERROR_INSTALL_USEREXIT;
4530 attrib = GetFileAttributesW(package->db->path);
4536 return ERROR_SUCCESS;
4541 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4548 static const WCHAR szPropKeys[][80] =
4550 {'P','r','o','d','u','c','t','I','D',0},
4551 {'U','S','E','R','N','A','M','E',0},
4552 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4556 static const WCHAR szRegKeys[][80] =
4558 {'P','r','o','d','u','c','t','I','D',0},
4559 {'R','e','g','O','w','n','e','r',0},
4560 {'R','e','g','C','o','m','p','a','n','y',0},
4564 if (msi_check_unpublish(package))
4566 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4567 return ERROR_SUCCESS;
4570 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4572 return ERROR_SUCCESS;
4574 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4576 if (rc != ERROR_SUCCESS)
4579 for( i = 0; szPropKeys[i][0]; i++ )
4581 buffer = msi_dup_property( package, szPropKeys[i] );
4582 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4587 msi_free(productid);
4590 /* FIXME: call ui_actiondata */
4596 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4600 package->script->InWhatSequence |= SEQUENCE_EXEC;
4601 rc = ACTION_ProcessExecSequence(package,FALSE);
4606 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4608 MSIPACKAGE *package = param;
4609 LPCWSTR compgroupid=NULL;
4610 LPCWSTR feature=NULL;
4611 LPCWSTR text = NULL;
4612 LPCWSTR qualifier = NULL;
4613 LPCWSTR component = NULL;
4614 LPWSTR advertise = NULL;
4615 LPWSTR output = NULL;
4617 UINT rc = ERROR_SUCCESS;
4622 component = MSI_RecordGetString(rec,3);
4623 comp = get_loaded_component(package,component);
4625 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4626 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4627 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4629 TRACE("Skipping: Component %s not scheduled for install\n",
4630 debugstr_w(component));
4632 return ERROR_SUCCESS;
4635 compgroupid = MSI_RecordGetString(rec,1);
4636 qualifier = MSI_RecordGetString(rec,2);
4638 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4639 if (rc != ERROR_SUCCESS)
4642 text = MSI_RecordGetString(rec,4);
4643 feature = MSI_RecordGetString(rec,5);
4645 advertise = create_component_advertise_string(package, comp, feature);
4647 sz = strlenW(advertise);
4650 sz += lstrlenW(text);
4653 sz *= sizeof(WCHAR);
4655 output = msi_alloc_zero(sz);
4656 strcpyW(output,advertise);
4657 msi_free(advertise);
4660 strcatW(output,text);
4662 msi_reg_set_val_multi_str( hkey, qualifier, output );
4669 uirow = MSI_CreateRecord( 2 );
4670 MSI_RecordSetStringW( uirow, 1, compgroupid );
4671 MSI_RecordSetStringW( uirow, 2, qualifier);
4672 ui_actiondata( package, szPublishComponents, uirow);
4673 msiobj_release( &uirow->hdr );
4674 /* FIXME: call ui_progress? */
4680 * At present I am ignorning the advertised components part of this and only
4681 * focusing on the qualified component sets
4683 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4687 static const WCHAR ExecSeqQuery[] =
4688 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4689 '`','P','u','b','l','i','s','h',
4690 'C','o','m','p','o','n','e','n','t','`',0};
4692 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4693 if (rc != ERROR_SUCCESS)
4694 return ERROR_SUCCESS;
4696 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4697 msiobj_release(&view->hdr);
4702 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4704 MSIPACKAGE *package = param;
4707 SC_HANDLE hscm, service = NULL;
4708 LPCWSTR comp, depends, pass;
4709 LPWSTR name = NULL, disp = NULL;
4710 LPCWSTR load_order, serv_name, key;
4711 DWORD serv_type, start_type;
4714 static const WCHAR query[] =
4715 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4716 '`','C','o','m','p','o','n','e','n','t','`',' ',
4717 'W','H','E','R','E',' ',
4718 '`','C','o','m','p','o','n','e','n','t','`',' ',
4719 '=','\'','%','s','\'',0};
4721 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4724 ERR("Failed to open the SC Manager!\n");
4728 start_type = MSI_RecordGetInteger(rec, 5);
4729 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4732 depends = MSI_RecordGetString(rec, 8);
4733 if (depends && *depends)
4734 FIXME("Dependency list unhandled!\n");
4736 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4737 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4738 serv_type = MSI_RecordGetInteger(rec, 4);
4739 err_control = MSI_RecordGetInteger(rec, 6);
4740 load_order = MSI_RecordGetString(rec, 7);
4741 serv_name = MSI_RecordGetString(rec, 9);
4742 pass = MSI_RecordGetString(rec, 10);
4743 comp = MSI_RecordGetString(rec, 12);
4745 /* fetch the service path */
4746 row = MSI_QueryGetRecord(package->db, query, comp);
4749 ERR("Control query failed!\n");
4753 key = MSI_RecordGetString(row, 6);
4755 file = get_loaded_file(package, key);
4756 msiobj_release(&row->hdr);
4759 ERR("Failed to load the service file\n");
4763 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4764 start_type, err_control, file->TargetPath,
4765 load_order, NULL, NULL, serv_name, pass);
4768 if (GetLastError() != ERROR_SERVICE_EXISTS)
4769 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4773 CloseServiceHandle(service);
4774 CloseServiceHandle(hscm);
4778 return ERROR_SUCCESS;
4781 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4785 static const WCHAR ExecSeqQuery[] =
4786 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4787 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4789 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4790 if (rc != ERROR_SUCCESS)
4791 return ERROR_SUCCESS;
4793 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4794 msiobj_release(&view->hdr);
4799 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4800 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4802 LPCWSTR *vector, *temp_vector;
4806 static const WCHAR separator[] = {'[','~',']',0};
4809 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4814 vector = msi_alloc(sizeof(LPWSTR));
4822 vector[*numargs - 1] = p;
4824 if ((q = strstrW(p, separator)))
4828 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4834 vector = temp_vector;
4843 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4845 MSIPACKAGE *package = param;
4847 SC_HANDLE scm, service = NULL;
4848 LPCWSTR name, *vector = NULL;
4850 DWORD event, numargs;
4851 UINT r = ERROR_FUNCTION_FAILED;
4853 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4854 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4855 return ERROR_SUCCESS;
4857 name = MSI_RecordGetString(rec, 2);
4858 event = MSI_RecordGetInteger(rec, 3);
4859 args = strdupW(MSI_RecordGetString(rec, 4));
4861 if (!(event & msidbServiceControlEventStart))
4862 return ERROR_SUCCESS;
4864 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4867 ERR("Failed to open the service control manager\n");
4871 service = OpenServiceW(scm, name, SERVICE_START);
4874 ERR("Failed to open service %s\n", debugstr_w(name));
4878 vector = msi_service_args_to_vector(args, &numargs);
4880 if (!StartServiceW(service, numargs, vector))
4882 ERR("Failed to start service %s\n", debugstr_w(name));
4889 CloseServiceHandle(service);
4890 CloseServiceHandle(scm);
4897 static UINT ACTION_StartServices( MSIPACKAGE *package )
4902 static const WCHAR query[] = {
4903 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4904 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4906 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4907 if (rc != ERROR_SUCCESS)
4908 return ERROR_SUCCESS;
4910 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4911 msiobj_release(&view->hdr);
4916 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4918 DWORD i, needed, count;
4919 ENUM_SERVICE_STATUSW *dependencies;
4923 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4924 0, &needed, &count))
4927 if (GetLastError() != ERROR_MORE_DATA)
4930 dependencies = msi_alloc(needed);
4934 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4935 needed, &needed, &count))
4938 for (i = 0; i < count; i++)
4940 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4941 SERVICE_STOP | SERVICE_QUERY_STATUS);
4945 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4952 msi_free(dependencies);
4956 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4958 MSIPACKAGE *package = param;
4960 SERVICE_STATUS status;
4961 SERVICE_STATUS_PROCESS ssp;
4962 SC_HANDLE scm = NULL, service = NULL;
4964 DWORD event, needed;
4966 event = MSI_RecordGetInteger(rec, 3);
4967 if (!(event & msidbServiceControlEventStop))
4968 return ERROR_SUCCESS;
4970 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4971 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4972 return ERROR_SUCCESS;
4974 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4975 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4976 args = strdupW(MSI_RecordGetString(rec, 4));
4978 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4981 WARN("Failed to open the SCM: %d\n", GetLastError());
4985 service = OpenServiceW(scm, name,
4987 SERVICE_QUERY_STATUS |
4988 SERVICE_ENUMERATE_DEPENDENTS);
4991 WARN("Failed to open service (%s): %d\n",
4992 debugstr_w(name), GetLastError());
4996 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4997 sizeof(SERVICE_STATUS_PROCESS), &needed))
4999 WARN("Failed to query service status (%s): %d\n",
5000 debugstr_w(name), GetLastError());
5004 if (ssp.dwCurrentState == SERVICE_STOPPED)
5007 stop_service_dependents(scm, service);
5009 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5010 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5013 CloseServiceHandle(service);
5014 CloseServiceHandle(scm);
5018 return ERROR_SUCCESS;
5021 static UINT ACTION_StopServices( MSIPACKAGE *package )
5026 static const WCHAR query[] = {
5027 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5028 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5030 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5031 if (rc != ERROR_SUCCESS)
5032 return ERROR_SUCCESS;
5034 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5035 msiobj_release(&view->hdr);
5040 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
5044 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
5046 if (!lstrcmpW(file->File, filename))
5053 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5055 MSIPACKAGE *package = param;
5056 LPWSTR driver, driver_path, ptr;
5057 WCHAR outpath[MAX_PATH];
5058 MSIFILE *driver_file, *setup_file;
5061 UINT r = ERROR_SUCCESS;
5063 static const WCHAR driver_fmt[] = {
5064 'D','r','i','v','e','r','=','%','s',0};
5065 static const WCHAR setup_fmt[] = {
5066 'S','e','t','u','p','=','%','s',0};
5067 static const WCHAR usage_fmt[] = {
5068 'F','i','l','e','U','s','a','g','e','=','1',0};
5070 desc = MSI_RecordGetString(rec, 3);
5072 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5073 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5075 if (!driver_file || !setup_file)
5077 ERR("ODBC Driver entry not found!\n");
5078 return ERROR_FUNCTION_FAILED;
5081 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
5082 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
5083 lstrlenW(usage_fmt) + 1;
5084 driver = msi_alloc(len * sizeof(WCHAR));
5086 return ERROR_OUTOFMEMORY;
5089 lstrcpyW(ptr, desc);
5090 ptr += lstrlenW(ptr) + 1;
5092 sprintfW(ptr, driver_fmt, driver_file->FileName);
5093 ptr += lstrlenW(ptr) + 1;
5095 sprintfW(ptr, setup_fmt, setup_file->FileName);
5096 ptr += lstrlenW(ptr) + 1;
5098 lstrcpyW(ptr, usage_fmt);
5099 ptr += lstrlenW(ptr) + 1;
5102 driver_path = strdupW(driver_file->TargetPath);
5103 ptr = strrchrW(driver_path, '\\');
5104 if (ptr) *ptr = '\0';
5106 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5107 NULL, ODBC_INSTALL_COMPLETE, &usage))
5109 ERR("Failed to install SQL driver!\n");
5110 r = ERROR_FUNCTION_FAILED;
5114 msi_free(driver_path);
5119 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5121 MSIPACKAGE *package = param;
5122 LPWSTR translator, translator_path, ptr;
5123 WCHAR outpath[MAX_PATH];
5124 MSIFILE *translator_file, *setup_file;
5127 UINT r = ERROR_SUCCESS;
5129 static const WCHAR translator_fmt[] = {
5130 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5131 static const WCHAR setup_fmt[] = {
5132 'S','e','t','u','p','=','%','s',0};
5134 desc = MSI_RecordGetString(rec, 3);
5136 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5137 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5139 if (!translator_file || !setup_file)
5141 ERR("ODBC Translator entry not found!\n");
5142 return ERROR_FUNCTION_FAILED;
5145 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
5146 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
5147 translator = msi_alloc(len * sizeof(WCHAR));
5149 return ERROR_OUTOFMEMORY;
5152 lstrcpyW(ptr, desc);
5153 ptr += lstrlenW(ptr) + 1;
5155 sprintfW(ptr, translator_fmt, translator_file->FileName);
5156 ptr += lstrlenW(ptr) + 1;
5158 sprintfW(ptr, setup_fmt, setup_file->FileName);
5159 ptr += lstrlenW(ptr) + 1;
5162 translator_path = strdupW(translator_file->TargetPath);
5163 ptr = strrchrW(translator_path, '\\');
5164 if (ptr) *ptr = '\0';
5166 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5167 NULL, ODBC_INSTALL_COMPLETE, &usage))
5169 ERR("Failed to install SQL translator!\n");
5170 r = ERROR_FUNCTION_FAILED;
5173 msi_free(translator);
5174 msi_free(translator_path);
5179 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5182 LPCWSTR desc, driver;
5183 WORD request = ODBC_ADD_SYS_DSN;
5186 UINT r = ERROR_SUCCESS;
5188 static const WCHAR attrs_fmt[] = {
5189 'D','S','N','=','%','s',0 };
5191 desc = MSI_RecordGetString(rec, 3);
5192 driver = MSI_RecordGetString(rec, 4);
5193 registration = MSI_RecordGetInteger(rec, 5);
5195 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5196 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5198 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5199 attrs = msi_alloc(len * sizeof(WCHAR));
5201 return ERROR_OUTOFMEMORY;
5203 sprintfW(attrs, attrs_fmt, desc);
5204 attrs[len - 1] = '\0';
5206 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5208 ERR("Failed to install SQL data source!\n");
5209 r = ERROR_FUNCTION_FAILED;
5217 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5222 static const WCHAR driver_query[] = {
5223 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5224 'O','D','B','C','D','r','i','v','e','r',0 };
5226 static const WCHAR translator_query[] = {
5227 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5228 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5230 static const WCHAR source_query[] = {
5231 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5232 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5234 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5235 if (rc != ERROR_SUCCESS)
5236 return ERROR_SUCCESS;
5238 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5239 msiobj_release(&view->hdr);
5241 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5242 if (rc != ERROR_SUCCESS)
5243 return ERROR_SUCCESS;
5245 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5246 msiobj_release(&view->hdr);
5248 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5249 if (rc != ERROR_SUCCESS)
5250 return ERROR_SUCCESS;
5252 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5253 msiobj_release(&view->hdr);
5258 #define ENV_ACT_SETALWAYS 0x1
5259 #define ENV_ACT_SETABSENT 0x2
5260 #define ENV_ACT_REMOVE 0x4
5261 #define ENV_ACT_REMOVEMATCH 0x8
5263 #define ENV_MOD_MACHINE 0x20000000
5264 #define ENV_MOD_APPEND 0x40000000
5265 #define ENV_MOD_PREFIX 0x80000000
5266 #define ENV_MOD_MASK 0xC0000000
5268 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5270 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5272 LPCWSTR cptr = *name;
5273 LPCWSTR ptr = *value;
5275 static const WCHAR prefix[] = {'[','~',']',0};
5276 static const int prefix_len = 3;
5282 *flags |= ENV_ACT_SETALWAYS;
5283 else if (*cptr == '+')
5284 *flags |= ENV_ACT_SETABSENT;
5285 else if (*cptr == '-')
5286 *flags |= ENV_ACT_REMOVE;
5287 else if (*cptr == '!')
5288 *flags |= ENV_ACT_REMOVEMATCH;
5289 else if (*cptr == '*')
5290 *flags |= ENV_MOD_MACHINE;
5300 ERR("Missing environment variable\n");
5301 return ERROR_FUNCTION_FAILED;
5304 if (!strncmpW(ptr, prefix, prefix_len))
5306 *flags |= ENV_MOD_APPEND;
5307 *value += lstrlenW(prefix);
5309 else if (lstrlenW(*value) >= prefix_len)
5311 ptr += lstrlenW(ptr) - prefix_len;
5312 if (!lstrcmpW(ptr, prefix))
5314 *flags |= ENV_MOD_PREFIX;
5315 /* the "[~]" will be removed by deformat_string */;
5319 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5320 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5321 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5322 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5324 ERR("Invalid flags: %08x\n", *flags);
5325 return ERROR_FUNCTION_FAILED;
5329 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
5331 return ERROR_SUCCESS;
5334 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5336 MSIPACKAGE *package = param;
5337 LPCWSTR name, value;
5338 LPWSTR data = NULL, newval = NULL;
5339 LPWSTR deformatted = NULL, ptr;
5340 DWORD flags, type, size;
5342 HKEY env = NULL, root;
5343 LPCWSTR environment;
5345 static const WCHAR user_env[] =
5346 {'E','n','v','i','r','o','n','m','e','n','t',0};
5347 static const WCHAR machine_env[] =
5348 {'S','y','s','t','e','m','\\',
5349 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5350 'C','o','n','t','r','o','l','\\',
5351 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5352 'E','n','v','i','r','o','n','m','e','n','t',0};
5353 static const WCHAR semicolon[] = {';',0};
5355 name = MSI_RecordGetString(rec, 2);
5356 value = MSI_RecordGetString(rec, 3);
5358 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
5360 res = env_set_flags(&name, &value, &flags);
5361 if (res != ERROR_SUCCESS)
5364 deformat_string(package, value, &deformatted);
5367 res = ERROR_OUTOFMEMORY;
5371 value = deformatted;
5373 if (flags & ENV_MOD_MACHINE)
5375 environment = machine_env;
5376 root = HKEY_LOCAL_MACHINE;
5380 environment = user_env;
5381 root = HKEY_CURRENT_USER;
5384 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5385 KEY_ALL_ACCESS, NULL, &env, NULL);
5386 if (res != ERROR_SUCCESS)
5389 if (flags & ENV_ACT_REMOVE)
5390 FIXME("Not removing environment variable on uninstall!\n");
5393 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5394 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5395 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5398 if (res != ERROR_FILE_NOT_FOUND)
5400 if (flags & ENV_ACT_SETABSENT)
5402 res = ERROR_SUCCESS;
5406 data = msi_alloc(size);
5410 return ERROR_OUTOFMEMORY;
5413 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5414 if (res != ERROR_SUCCESS)
5417 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5419 res = RegDeleteKeyW(env, name);
5423 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5424 newval = msi_alloc(size);
5428 res = ERROR_OUTOFMEMORY;
5432 if (!(flags & ENV_MOD_MASK))
5433 lstrcpyW(newval, value);
5436 if (flags & ENV_MOD_PREFIX)
5438 lstrcpyW(newval, value);
5439 lstrcatW(newval, semicolon);
5440 ptr = newval + lstrlenW(value) + 1;
5443 lstrcpyW(ptr, data);
5445 if (flags & ENV_MOD_APPEND)
5447 lstrcatW(newval, semicolon);
5448 lstrcatW(newval, value);
5454 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5455 newval = msi_alloc(size);
5458 res = ERROR_OUTOFMEMORY;
5462 lstrcpyW(newval, value);
5465 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5466 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5469 if (env) RegCloseKey(env);
5470 msi_free(deformatted);
5476 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5480 static const WCHAR ExecSeqQuery[] =
5481 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5482 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5483 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5484 if (rc != ERROR_SUCCESS)
5485 return ERROR_SUCCESS;
5487 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5488 msiobj_release(&view->hdr);
5493 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5504 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5508 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5509 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5511 WARN("Source or dest is directory, not moving\n");
5515 if (options == msidbMoveFileOptionsMove)
5517 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5518 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5521 WARN("MoveFile failed: %d\n", GetLastError());
5527 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5528 ret = CopyFileW(source, dest, FALSE);
5531 WARN("CopyFile failed: %d\n", GetLastError());
5539 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5542 DWORD dirlen, pathlen;
5544 ptr = strrchrW(wildcard, '\\');
5545 dirlen = ptr - wildcard + 1;
5547 pathlen = dirlen + lstrlenW(filename) + 1;
5548 path = msi_alloc(pathlen * sizeof(WCHAR));
5550 lstrcpynW(path, wildcard, dirlen + 1);
5551 lstrcatW(path, filename);
5556 static void free_file_entry(FILE_LIST *file)
5558 msi_free(file->source);
5559 msi_free(file->dest);
5563 static void free_list(FILE_LIST *list)
5565 while (!list_empty(&list->entry))
5567 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5569 list_remove(&file->entry);
5570 free_file_entry(file);
5574 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5576 FILE_LIST *new, *file;
5577 LPWSTR ptr, filename;
5580 new = msi_alloc_zero(sizeof(FILE_LIST));
5584 new->source = strdupW(source);
5585 ptr = strrchrW(dest, '\\') + 1;
5586 filename = strrchrW(new->source, '\\') + 1;
5588 new->sourcename = filename;
5591 new->destname = ptr;
5593 new->destname = new->sourcename;
5595 size = (ptr - dest) + lstrlenW(filename) + 1;
5596 new->dest = msi_alloc(size * sizeof(WCHAR));
5599 free_file_entry(new);
5603 lstrcpynW(new->dest, dest, ptr - dest + 1);
5604 lstrcatW(new->dest, filename);
5606 if (list_empty(&files->entry))
5608 list_add_head(&files->entry, &new->entry);
5612 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5614 if (lstrcmpW(source, file->source) < 0)
5616 list_add_before(&file->entry, &new->entry);
5621 list_add_after(&file->entry, &new->entry);
5625 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5627 WIN32_FIND_DATAW wfd;
5631 FILE_LIST files, *file;
5634 hfile = FindFirstFileW(source, &wfd);
5635 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5637 list_init(&files.entry);
5639 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5641 if (is_dot_dir(wfd.cFileName)) continue;
5643 path = wildcard_to_file(source, wfd.cFileName);
5650 add_wildcard(&files, path, dest);
5654 /* no files match the wildcard */
5655 if (list_empty(&files.entry))
5658 /* only the first wildcard match gets renamed to dest */
5659 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5660 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5661 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5668 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5670 while (!list_empty(&files.entry))
5672 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5674 msi_move_file(file->source, file->dest, options);
5676 list_remove(&file->entry);
5677 free_file_entry(file);
5688 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5690 MSIPACKAGE *package = param;
5693 LPWSTR destname = NULL;
5694 LPWSTR sourcedir = NULL, destdir = NULL;
5695 LPWSTR source = NULL, dest = NULL;
5698 BOOL ret, wildcards;
5700 static const WCHAR backslash[] = {'\\',0};
5702 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5703 if (!comp || !comp->Enabled ||
5704 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5706 TRACE("Component not set for install, not moving file\n");
5707 return ERROR_SUCCESS;
5710 sourcename = MSI_RecordGetString(rec, 3);
5711 options = MSI_RecordGetInteger(rec, 7);
5713 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5717 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5723 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5726 source = strdupW(sourcedir);
5732 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5733 source = msi_alloc(size * sizeof(WCHAR));
5737 lstrcpyW(source, sourcedir);
5738 if (source[lstrlenW(source) - 1] != '\\')
5739 lstrcatW(source, backslash);
5740 lstrcatW(source, sourcename);
5743 wildcards = strchrW(source, '*') || strchrW(source, '?');
5745 if (MSI_RecordIsNull(rec, 4))
5749 destname = strdupW(sourcename);
5756 destname = strdupW(MSI_RecordGetString(rec, 4));
5758 reduce_to_longfilename(destname);
5763 size = lstrlenW(destname);
5765 size += lstrlenW(destdir) + 2;
5766 dest = msi_alloc(size * sizeof(WCHAR));
5770 lstrcpyW(dest, destdir);
5771 if (dest[lstrlenW(dest) - 1] != '\\')
5772 lstrcatW(dest, backslash);
5775 lstrcatW(dest, destname);
5777 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5779 ret = CreateDirectoryW(destdir, NULL);
5782 WARN("CreateDirectory failed: %d\n", GetLastError());
5783 return ERROR_SUCCESS;
5788 msi_move_file(source, dest, options);
5790 move_files_wildcard(source, dest, options);
5793 msi_free(sourcedir);
5799 return ERROR_SUCCESS;
5802 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5807 static const WCHAR ExecSeqQuery[] =
5808 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5809 '`','M','o','v','e','F','i','l','e','`',0};
5811 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5812 if (rc != ERROR_SUCCESS)
5813 return ERROR_SUCCESS;
5815 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5816 msiobj_release(&view->hdr);
5821 typedef struct tagMSIASSEMBLY
5824 MSICOMPONENT *component;
5825 MSIFEATURE *feature;
5833 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5835 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5836 LPVOID pvReserved, HMODULE *phModDll);
5838 static BOOL init_functionpointers(void)
5844 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5846 hmscoree = LoadLibraryA("mscoree.dll");
5849 WARN("mscoree.dll not available\n");
5853 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5854 if (!pLoadLibraryShim)
5856 WARN("LoadLibraryShim not available\n");
5857 FreeLibrary(hmscoree);
5861 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5864 WARN("fusion.dll not available\n");
5865 FreeLibrary(hmscoree);
5869 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5871 FreeLibrary(hmscoree);
5875 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5878 IAssemblyCache *cache;
5880 UINT r = ERROR_FUNCTION_FAILED;
5882 TRACE("installing assembly: %s\n", debugstr_w(path));
5884 if (assembly->feature)
5885 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5887 if (assembly->manifest)
5888 FIXME("Manifest unhandled\n");
5890 if (assembly->application)
5892 FIXME("Assembly should be privately installed\n");
5893 return ERROR_SUCCESS;
5896 if (assembly->attributes == msidbAssemblyAttributesWin32)
5898 FIXME("Win32 assemblies not handled\n");
5899 return ERROR_SUCCESS;
5902 hr = pCreateAssemblyCache(&cache, 0);
5906 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5908 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5913 IAssemblyCache_Release(cache);
5917 typedef struct tagASSEMBLY_LIST
5919 MSIPACKAGE *package;
5920 IAssemblyCache *cache;
5921 struct list *assemblies;
5924 typedef struct tagASSEMBLY_NAME
5932 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
5934 ASSEMBLY_NAME *asmname = param;
5935 LPCWSTR name = MSI_RecordGetString(rec, 2);
5936 LPWSTR val = msi_dup_record_field(rec, 3);
5938 static const WCHAR Name[] = {'N','a','m','e',0};
5939 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
5940 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
5941 static const WCHAR PublicKeyToken[] = {
5942 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
5944 if (!strcmpiW(name, Name))
5945 asmname->name = val;
5946 else if (!strcmpiW(name, Version))
5947 asmname->version = val;
5948 else if (!strcmpiW(name, Culture))
5949 asmname->culture = val;
5950 else if (!strcmpiW(name, PublicKeyToken))
5951 asmname->pubkeytoken = val;
5955 return ERROR_SUCCESS;
5958 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
5962 *size = lstrlenW(append) + 1;
5963 *str = msi_alloc((*size) * sizeof(WCHAR));
5964 lstrcpyW(*str, append);
5968 (*size) += lstrlenW(append);
5969 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
5970 lstrcatW(*str, append);
5973 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
5976 ASSEMBLY_INFO asminfo;
5984 static const WCHAR separator[] = {',',' ',0};
5985 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
5986 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
5987 static const WCHAR PublicKeyToken[] = {
5988 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
5989 static const WCHAR query[] = {
5990 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5991 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
5992 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
5993 '=','\'','%','s','\'',0};
5997 ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
5998 ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
6000 r = MSI_OpenQuery(db, &view, query, comp->Component);
6001 if (r != ERROR_SUCCESS)
6002 return ERROR_SUCCESS;
6004 MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
6005 msiobj_release(&view->hdr);
6009 ERR("No assembly name specified!\n");
6013 append_str(&disp, &size, name.name);
6017 append_str(&disp, &size, separator);
6018 append_str(&disp, &size, Version);
6019 append_str(&disp, &size, name.version);
6024 append_str(&disp, &size, separator);
6025 append_str(&disp, &size, Culture);
6026 append_str(&disp, &size, name.culture);
6029 if (name.pubkeytoken)
6031 append_str(&disp, &size, separator);
6032 append_str(&disp, &size, PublicKeyToken);
6033 append_str(&disp, &size, name.pubkeytoken);
6036 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6037 IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
6039 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6043 msi_free(name.name);
6044 msi_free(name.version);
6045 msi_free(name.culture);
6046 msi_free(name.pubkeytoken);
6051 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6053 ASSEMBLY_LIST *list = param;
6054 MSIASSEMBLY *assembly;
6056 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6058 return ERROR_OUTOFMEMORY;
6060 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
6062 if (!assembly->component || !assembly->component->Enabled ||
6063 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
6065 TRACE("Component not set for install, not publishing assembly\n");
6067 return ERROR_SUCCESS;
6070 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6071 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6073 if (!assembly->file)
6075 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6076 return ERROR_FUNCTION_FAILED;
6079 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6080 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6081 assembly->attributes = MSI_RecordGetInteger(rec, 5);
6083 if (assembly->application)
6086 DWORD size = sizeof(version)/sizeof(WCHAR);
6088 /* FIXME: we should probably check the manifest file here */
6090 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6091 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6093 assembly->installed = TRUE;
6097 assembly->installed = check_assembly_installed(list->package->db,
6099 assembly->component);
6101 list_add_head(list->assemblies, &assembly->entry);
6102 return ERROR_SUCCESS;
6105 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6107 IAssemblyCache *cache = NULL;
6113 static const WCHAR query[] =
6114 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6115 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6117 r = MSI_DatabaseOpenViewW(package->db, query, &view);
6118 if (r != ERROR_SUCCESS)
6119 return ERROR_SUCCESS;
6121 hr = pCreateAssemblyCache(&cache, 0);
6123 return ERROR_FUNCTION_FAILED;
6125 list.package = package;
6127 list.assemblies = assemblies;
6129 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6130 msiobj_release(&view->hdr);
6132 IAssemblyCache_Release(cache);
6137 static void free_assemblies(struct list *assemblies)
6139 struct list *item, *cursor;
6141 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6143 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6145 list_remove(&assembly->entry);
6146 msi_free(assembly->application);
6147 msi_free(assembly->manifest);
6152 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6154 MSIASSEMBLY *assembly;
6156 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6158 if (!lstrcmpW(assembly->file->File, file))
6168 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6169 LPWSTR *path, DWORD *attrs, PVOID user)
6171 MSIASSEMBLY *assembly;
6172 WCHAR temppath[MAX_PATH];
6173 struct list *assemblies = user;
6176 if (!find_assembly(assemblies, file, &assembly))
6179 GetTempPathW(MAX_PATH, temppath);
6180 PathAddBackslashW(temppath);
6181 lstrcatW(temppath, assembly->file->FileName);
6183 if (action == MSICABEXTRACT_BEGINEXTRACT)
6185 if (assembly->installed)
6188 *path = strdupW(temppath);
6189 *attrs = assembly->file->Attributes;
6191 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6193 assembly->installed = TRUE;
6195 r = install_assembly(package, assembly, temppath);
6196 if (r != ERROR_SUCCESS)
6197 ERR("Failed to install assembly\n");
6203 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6206 struct list assemblies = LIST_INIT(assemblies);
6207 MSIASSEMBLY *assembly;
6210 if (!init_functionpointers() || !pCreateAssemblyCache)
6211 return ERROR_FUNCTION_FAILED;
6213 r = load_assemblies(package, &assemblies);
6214 if (r != ERROR_SUCCESS)
6217 if (list_empty(&assemblies))
6220 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6223 r = ERROR_OUTOFMEMORY;
6227 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6229 if (assembly->installed && !mi->is_continuous)
6232 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6233 (assembly->file->IsCompressed && !mi->is_extracted))
6237 r = ready_media(package, assembly->file, mi);
6238 if (r != ERROR_SUCCESS)
6240 ERR("Failed to ready media\n");
6245 data.package = package;
6246 data.cb = installassembly_cb;
6247 data.user = &assemblies;
6249 if (assembly->file->IsCompressed &&
6250 !msi_cabextract(package, mi, &data))
6252 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6253 r = ERROR_FUNCTION_FAILED;
6258 if (!assembly->file->IsCompressed)
6260 LPWSTR source = resolve_file_source(package, assembly->file);
6262 r = install_assembly(package, assembly, source);
6263 if (r != ERROR_SUCCESS)
6264 ERR("Failed to install assembly\n");
6269 /* FIXME: write Installer assembly reg values */
6273 free_assemblies(&assemblies);
6277 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6278 LPCSTR action, LPCWSTR table )
6280 static const WCHAR query[] = {
6281 'S','E','L','E','C','T',' ','*',' ',
6282 'F','R','O','M',' ','`','%','s','`',0 };
6283 MSIQUERY *view = NULL;
6287 r = MSI_OpenQuery( package->db, &view, query, table );
6288 if (r == ERROR_SUCCESS)
6290 r = MSI_IterateRecords(view, &count, NULL, package);
6291 msiobj_release(&view->hdr);
6295 FIXME("%s -> %u ignored %s table values\n",
6296 action, count, debugstr_w(table));
6298 return ERROR_SUCCESS;
6301 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6303 TRACE("%p\n", package);
6304 return ERROR_SUCCESS;
6307 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6309 static const WCHAR table[] =
6310 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6311 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6314 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6316 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6317 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6320 static UINT ACTION_BindImage( MSIPACKAGE *package )
6322 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6323 return msi_unimplemented_action_stub( package, "BindImage", table );
6326 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6328 static const WCHAR table[] = {
6329 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6330 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6333 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6335 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6336 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6339 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
6341 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
6342 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
6345 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6347 static const WCHAR table[] = {
6348 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6349 return msi_unimplemented_action_stub( package, "DeleteServices", table );
6351 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6353 static const WCHAR table[] = {
6354 'P','r','o','d','u','c','t','I','D',0 };
6355 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6358 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6360 static const WCHAR table[] = {
6361 'E','n','v','i','r','o','n','m','e','n','t',0 };
6362 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6365 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6367 static const WCHAR table[] = {
6368 'M','s','i','A','s','s','e','m','b','l','y',0 };
6369 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6372 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6374 static const WCHAR table[] = { 'F','o','n','t',0 };
6375 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6378 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6380 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6381 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6384 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6386 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6387 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6390 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6392 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6393 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6396 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6398 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6399 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6402 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6404 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6405 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6408 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6410 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6411 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6414 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
6416 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
6417 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
6420 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6422 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6423 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6426 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6428 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6429 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6432 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6434 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6435 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6438 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6440 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6441 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6444 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6446 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6447 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6450 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6452 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6453 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6456 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6458 static const WCHAR table[] = { 'M','I','M','E',0 };
6459 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6462 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6464 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6465 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6468 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6470 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6471 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6474 static const struct _actions StandardActions[] = {
6475 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6476 { szAppSearch, ACTION_AppSearch },
6477 { szBindImage, ACTION_BindImage },
6478 { szCCPSearch, ACTION_CCPSearch },
6479 { szCostFinalize, ACTION_CostFinalize },
6480 { szCostInitialize, ACTION_CostInitialize },
6481 { szCreateFolders, ACTION_CreateFolders },
6482 { szCreateShortcuts, ACTION_CreateShortcuts },
6483 { szDeleteServices, ACTION_DeleteServices },
6484 { szDisableRollback, NULL },
6485 { szDuplicateFiles, ACTION_DuplicateFiles },
6486 { szExecuteAction, ACTION_ExecuteAction },
6487 { szFileCost, ACTION_FileCost },
6488 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6489 { szForceReboot, ACTION_ForceReboot },
6490 { szInstallAdminPackage, NULL },
6491 { szInstallExecute, ACTION_InstallExecute },
6492 { szInstallExecuteAgain, ACTION_InstallExecute },
6493 { szInstallFiles, ACTION_InstallFiles},
6494 { szInstallFinalize, ACTION_InstallFinalize },
6495 { szInstallInitialize, ACTION_InstallInitialize },
6496 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6497 { szInstallValidate, ACTION_InstallValidate },
6498 { szIsolateComponents, ACTION_IsolateComponents },
6499 { szLaunchConditions, ACTION_LaunchConditions },
6500 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6501 { szMoveFiles, ACTION_MoveFiles },
6502 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6503 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6504 { szInstallODBC, ACTION_InstallODBC },
6505 { szInstallServices, ACTION_InstallServices },
6506 { szPatchFiles, ACTION_PatchFiles },
6507 { szProcessComponents, ACTION_ProcessComponents },
6508 { szPublishComponents, ACTION_PublishComponents },
6509 { szPublishFeatures, ACTION_PublishFeatures },
6510 { szPublishProduct, ACTION_PublishProduct },
6511 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6512 { szRegisterComPlus, ACTION_RegisterComPlus},
6513 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6514 { szRegisterFonts, ACTION_RegisterFonts },
6515 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6516 { szRegisterProduct, ACTION_RegisterProduct },
6517 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6518 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6519 { szRegisterUser, ACTION_RegisterUser },
6520 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6521 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6522 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6523 { szRemoveFiles, ACTION_RemoveFiles },
6524 { szRemoveFolders, ACTION_RemoveFolders },
6525 { szRemoveIniValues, ACTION_RemoveIniValues },
6526 { szRemoveODBC, ACTION_RemoveODBC },
6527 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6528 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6529 { szResolveSource, ACTION_ResolveSource },
6530 { szRMCCPSearch, ACTION_RMCCPSearch },
6531 { szScheduleReboot, NULL },
6532 { szSelfRegModules, ACTION_SelfRegModules },
6533 { szSelfUnregModules, ACTION_SelfUnregModules },
6534 { szSetODBCFolders, NULL },
6535 { szStartServices, ACTION_StartServices },
6536 { szStopServices, ACTION_StopServices },
6537 { szUnpublishComponents, ACTION_UnpublishComponents },
6538 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6539 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6540 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6541 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6542 { szUnregisterFonts, ACTION_UnregisterFonts },
6543 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6544 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6545 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6546 { szValidateProductID, ACTION_ValidateProductID },
6547 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6548 { szWriteIniValues, ACTION_WriteIniValues },
6549 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6553 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6554 UINT* rc, BOOL force )
6560 if (!run && !package->script->CurrentlyScripting)
6565 if (strcmpW(action,szInstallFinalize) == 0 ||
6566 strcmpW(action,szInstallExecute) == 0 ||
6567 strcmpW(action,szInstallExecuteAgain) == 0)
6572 while (StandardActions[i].action != NULL)
6574 if (strcmpW(StandardActions[i].action, action)==0)
6578 ui_actioninfo(package, action, TRUE, 0);
6579 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6580 ui_actioninfo(package, action, FALSE, *rc);
6584 ui_actionstart(package, action);
6585 if (StandardActions[i].handler)
6587 *rc = StandardActions[i].handler(package);
6591 FIXME("unhandled standard action %s\n",debugstr_w(action));
6592 *rc = ERROR_SUCCESS;