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);
886 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
888 UINT rc = ERROR_SUCCESS;
890 static const WCHAR ExecSeqQuery[] =
891 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
892 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
893 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
894 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
896 static const WCHAR UISeqQuery[] =
897 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
898 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
899 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
900 ' ', '=',' ','%','i',0};
903 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
905 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
909 LPCWSTR action, cond;
911 TRACE("Running the actions\n");
913 /* check conditions */
914 cond = MSI_RecordGetString(row,2);
916 /* this is a hack to skip errors in the condition code */
917 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
920 action = MSI_RecordGetString(row,1);
923 ERR("failed to fetch action\n");
924 rc = ERROR_FUNCTION_FAILED;
929 rc = ACTION_PerformUIAction(package,action,-1);
931 rc = ACTION_PerformAction(package,action,-1,FALSE);
933 msiobj_release(&row->hdr);
944 } iterate_action_param;
946 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
948 iterate_action_param *iap = param;
950 LPCWSTR cond, action;
952 action = MSI_RecordGetString(row,1);
955 ERR("Error is retrieving action name\n");
956 return ERROR_FUNCTION_FAILED;
959 /* check conditions */
960 cond = MSI_RecordGetString(row,2);
962 /* this is a hack to skip errors in the condition code */
963 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
965 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
966 return ERROR_SUCCESS;
970 rc = ACTION_PerformUIAction(iap->package,action,-1);
972 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
974 msi_dialog_check_messages( NULL );
976 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
977 rc = iap->package->CurrentInstallState;
979 if (rc == ERROR_FUNCTION_NOT_CALLED)
982 if (rc != ERROR_SUCCESS)
983 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
988 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
992 static const WCHAR query[] =
993 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
995 ' ','W','H','E','R','E',' ',
996 '`','S','e','q','u','e','n','c','e','`',' ',
997 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
998 '`','S','e','q','u','e','n','c','e','`',0};
999 iterate_action_param iap;
1002 * FIXME: probably should be checking UILevel in the
1003 * ACTION_PerformUIAction/ACTION_PerformAction
1004 * rather than saving the UI level here. Those
1005 * two functions can be merged too.
1007 iap.package = package;
1010 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
1012 r = MSI_OpenQuery( package->db, &view, query, szTable );
1013 if (r == ERROR_SUCCESS)
1015 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
1016 msiobj_release(&view->hdr);
1022 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
1026 static const WCHAR ExecSeqQuery[] =
1027 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1028 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
1029 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
1030 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
1031 'O','R','D','E','R',' ', 'B','Y',' ',
1032 '`','S','e','q','u','e','n','c','e','`',0 };
1033 MSIRECORD * row = 0;
1034 static const WCHAR IVQuery[] =
1035 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
1036 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
1037 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
1038 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
1039 ' ','\'', 'I','n','s','t','a','l','l',
1040 'V','a','l','i','d','a','t','e','\'', 0};
1042 iterate_action_param iap;
1044 iap.package = package;
1047 if (package->script->ExecuteSequenceRun)
1049 TRACE("Execute Sequence already Run\n");
1050 return ERROR_SUCCESS;
1053 package->script->ExecuteSequenceRun = TRUE;
1055 /* get the sequence number */
1058 row = MSI_QueryGetRecord(package->db, IVQuery);
1060 return ERROR_FUNCTION_FAILED;
1061 seq = MSI_RecordGetInteger(row,1);
1062 msiobj_release(&row->hdr);
1065 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
1066 if (rc == ERROR_SUCCESS)
1068 TRACE("Running the actions\n");
1070 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
1071 msiobj_release(&view->hdr);
1077 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
1081 static const WCHAR ExecSeqQuery [] =
1082 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1083 '`','I','n','s','t','a','l','l',
1084 'U','I','S','e','q','u','e','n','c','e','`',
1085 ' ','W','H','E','R','E',' ',
1086 '`','S','e','q','u','e','n','c','e','`',' ',
1087 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1088 '`','S','e','q','u','e','n','c','e','`',0};
1089 iterate_action_param iap;
1091 iap.package = package;
1094 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1096 if (rc == ERROR_SUCCESS)
1098 TRACE("Running the actions\n");
1100 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
1101 msiobj_release(&view->hdr);
1107 /********************************************************
1108 * ACTION helper functions and functions that perform the actions
1109 *******************************************************/
1110 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1111 UINT* rc, UINT script, BOOL force )
1116 arc = ACTION_CustomAction(package, action, script, force);
1118 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1127 * A lot of actions are really important even if they don't do anything
1128 * explicit... Lots of properties are set at the beginning of the installation
1129 * CostFinalize does a bunch of work to translate the directories and such
1131 * But until I get write access to the database that is hard, so I am going to
1132 * hack it to see if I can get something to run.
1134 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1136 UINT rc = ERROR_SUCCESS;
1139 TRACE("Performing action (%s)\n",debugstr_w(action));
1141 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1144 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1148 WARN("unhandled msi action %s\n",debugstr_w(action));
1149 rc = ERROR_FUNCTION_NOT_CALLED;
1155 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1157 UINT rc = ERROR_SUCCESS;
1158 BOOL handled = FALSE;
1160 TRACE("Performing action (%s)\n",debugstr_w(action));
1162 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1165 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1167 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1172 WARN("unhandled msi action %s\n",debugstr_w(action));
1173 rc = ERROR_FUNCTION_NOT_CALLED;
1181 * Actual Action Handlers
1184 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1186 MSIPACKAGE *package = param;
1192 dir = MSI_RecordGetString(row,1);
1195 ERR("Unable to get folder id\n");
1196 return ERROR_SUCCESS;
1199 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1202 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1203 return ERROR_SUCCESS;
1206 TRACE("Folder is %s\n",debugstr_w(full_path));
1209 uirow = MSI_CreateRecord(1);
1210 MSI_RecordSetStringW(uirow,1,full_path);
1211 ui_actiondata(package,szCreateFolders,uirow);
1212 msiobj_release( &uirow->hdr );
1214 if (folder->State == 0)
1215 create_full_pathW(full_path);
1219 msi_free(full_path);
1220 return ERROR_SUCCESS;
1223 /* FIXME: probably should merge this with the above function */
1224 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1226 UINT rc = ERROR_SUCCESS;
1228 LPWSTR install_path;
1230 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1232 return ERROR_FUNCTION_FAILED;
1234 /* create the path */
1235 if (folder->State == 0)
1237 create_full_pathW(install_path);
1240 msi_free(install_path);
1245 UINT msi_create_component_directories( MSIPACKAGE *package )
1249 /* create all the folders required by the components are going to install */
1250 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1252 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1254 msi_create_directory( package, comp->Directory );
1257 return ERROR_SUCCESS;
1261 * Also we cannot enable/disable components either, so for now I am just going
1262 * to do all the directories for all the components.
1264 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1266 static const WCHAR ExecSeqQuery[] =
1267 {'S','E','L','E','C','T',' ',
1268 '`','D','i','r','e','c','t','o','r','y','_','`',
1269 ' ','F','R','O','M',' ',
1270 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1274 /* create all the empty folders specified in the CreateFolder table */
1275 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1276 if (rc != ERROR_SUCCESS)
1277 return ERROR_SUCCESS;
1279 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1280 msiobj_release(&view->hdr);
1282 msi_create_component_directories( package );
1287 static UINT load_component( MSIRECORD *row, LPVOID param )
1289 MSIPACKAGE *package = param;
1292 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1294 return ERROR_FUNCTION_FAILED;
1296 list_add_tail( &package->components, &comp->entry );
1298 /* fill in the data */
1299 comp->Component = msi_dup_record_field( row, 1 );
1301 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1303 comp->ComponentId = msi_dup_record_field( row, 2 );
1304 comp->Directory = msi_dup_record_field( row, 3 );
1305 comp->Attributes = MSI_RecordGetInteger(row,4);
1306 comp->Condition = msi_dup_record_field( row, 5 );
1307 comp->KeyPath = msi_dup_record_field( row, 6 );
1309 comp->Installed = INSTALLSTATE_UNKNOWN;
1310 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1312 return ERROR_SUCCESS;
1315 static UINT load_all_components( MSIPACKAGE *package )
1317 static const WCHAR query[] = {
1318 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1319 '`','C','o','m','p','o','n','e','n','t','`',0 };
1323 if (!list_empty(&package->components))
1324 return ERROR_SUCCESS;
1326 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1327 if (r != ERROR_SUCCESS)
1330 r = MSI_IterateRecords(view, NULL, load_component, package);
1331 msiobj_release(&view->hdr);
1336 MSIPACKAGE *package;
1337 MSIFEATURE *feature;
1340 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1344 cl = msi_alloc( sizeof (*cl) );
1346 return ERROR_NOT_ENOUGH_MEMORY;
1347 cl->component = comp;
1348 list_add_tail( &feature->Components, &cl->entry );
1350 return ERROR_SUCCESS;
1353 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1357 fl = msi_alloc( sizeof(*fl) );
1359 return ERROR_NOT_ENOUGH_MEMORY;
1360 fl->feature = child;
1361 list_add_tail( &parent->Children, &fl->entry );
1363 return ERROR_SUCCESS;
1366 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1368 _ilfs* ilfs = param;
1372 component = MSI_RecordGetString(row,1);
1374 /* check to see if the component is already loaded */
1375 comp = get_loaded_component( ilfs->package, component );
1378 ERR("unknown component %s\n", debugstr_w(component));
1379 return ERROR_FUNCTION_FAILED;
1382 add_feature_component( ilfs->feature, comp );
1383 comp->Enabled = TRUE;
1385 return ERROR_SUCCESS;
1388 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1390 MSIFEATURE *feature;
1395 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1397 if ( !lstrcmpW( feature->Feature, name ) )
1404 static UINT load_feature(MSIRECORD * row, LPVOID param)
1406 MSIPACKAGE* package = param;
1407 MSIFEATURE* feature;
1408 static const WCHAR Query1[] =
1409 {'S','E','L','E','C','T',' ',
1410 '`','C','o','m','p','o','n','e','n','t','_','`',
1411 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1412 'C','o','m','p','o','n','e','n','t','s','`',' ',
1413 'W','H','E','R','E',' ',
1414 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1419 /* fill in the data */
1421 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1423 return ERROR_NOT_ENOUGH_MEMORY;
1425 list_init( &feature->Children );
1426 list_init( &feature->Components );
1428 feature->Feature = msi_dup_record_field( row, 1 );
1430 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1432 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1433 feature->Title = msi_dup_record_field( row, 3 );
1434 feature->Description = msi_dup_record_field( row, 4 );
1436 if (!MSI_RecordIsNull(row,5))
1437 feature->Display = MSI_RecordGetInteger(row,5);
1439 feature->Level= MSI_RecordGetInteger(row,6);
1440 feature->Directory = msi_dup_record_field( row, 7 );
1441 feature->Attributes = MSI_RecordGetInteger(row,8);
1443 feature->Installed = INSTALLSTATE_UNKNOWN;
1444 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1446 list_add_tail( &package->features, &feature->entry );
1448 /* load feature components */
1450 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1451 if (rc != ERROR_SUCCESS)
1452 return ERROR_SUCCESS;
1454 ilfs.package = package;
1455 ilfs.feature = feature;
1457 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1458 msiobj_release(&view->hdr);
1460 return ERROR_SUCCESS;
1463 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1465 MSIPACKAGE* package = param;
1466 MSIFEATURE *parent, *child;
1468 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1470 return ERROR_FUNCTION_FAILED;
1472 if (!child->Feature_Parent)
1473 return ERROR_SUCCESS;
1475 parent = find_feature_by_name( package, child->Feature_Parent );
1477 return ERROR_FUNCTION_FAILED;
1479 add_feature_child( parent, child );
1480 return ERROR_SUCCESS;
1483 static UINT load_all_features( MSIPACKAGE *package )
1485 static const WCHAR query[] = {
1486 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1487 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1488 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1492 if (!list_empty(&package->features))
1493 return ERROR_SUCCESS;
1495 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1496 if (r != ERROR_SUCCESS)
1499 r = MSI_IterateRecords( view, NULL, load_feature, package );
1500 if (r != ERROR_SUCCESS)
1503 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1504 msiobj_release( &view->hdr );
1509 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1520 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1522 static const WCHAR query[] = {
1523 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1524 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1525 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1526 MSIQUERY *view = NULL;
1527 MSIRECORD *row = NULL;
1530 TRACE("%s\n", debugstr_w(file->File));
1532 r = MSI_OpenQuery(package->db, &view, query, file->File);
1533 if (r != ERROR_SUCCESS)
1536 r = MSI_ViewExecute(view, NULL);
1537 if (r != ERROR_SUCCESS)
1540 r = MSI_ViewFetch(view, &row);
1541 if (r != ERROR_SUCCESS)
1544 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1545 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1546 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1547 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1548 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1551 if (view) msiobj_release(&view->hdr);
1552 if (row) msiobj_release(&row->hdr);
1556 static UINT load_file(MSIRECORD *row, LPVOID param)
1558 MSIPACKAGE* package = param;
1562 /* fill in the data */
1564 file = msi_alloc_zero( sizeof (MSIFILE) );
1566 return ERROR_NOT_ENOUGH_MEMORY;
1568 file->File = msi_dup_record_field( row, 1 );
1570 component = MSI_RecordGetString( row, 2 );
1571 file->Component = get_loaded_component( package, component );
1573 if (!file->Component)
1575 WARN("Component not found: %s\n", debugstr_w(component));
1576 msi_free(file->File);
1578 return ERROR_SUCCESS;
1581 file->FileName = msi_dup_record_field( row, 3 );
1582 reduce_to_longfilename( file->FileName );
1584 file->ShortName = msi_dup_record_field( row, 3 );
1585 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1587 file->FileSize = MSI_RecordGetInteger( row, 4 );
1588 file->Version = msi_dup_record_field( row, 5 );
1589 file->Language = msi_dup_record_field( row, 6 );
1590 file->Attributes = MSI_RecordGetInteger( row, 7 );
1591 file->Sequence = MSI_RecordGetInteger( row, 8 );
1593 file->state = msifs_invalid;
1595 /* if the compressed bits are not set in the file attributes,
1596 * then read the information from the package word count property
1598 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1600 file->IsCompressed = FALSE;
1602 else if (file->Attributes &
1603 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1605 file->IsCompressed = TRUE;
1607 else if (file->Attributes & msidbFileAttributesNoncompressed)
1609 file->IsCompressed = FALSE;
1613 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1616 load_file_hash(package, file);
1618 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1620 list_add_tail( &package->files, &file->entry );
1622 return ERROR_SUCCESS;
1625 static UINT load_all_files(MSIPACKAGE *package)
1629 static const WCHAR Query[] =
1630 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1631 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1632 '`','S','e','q','u','e','n','c','e','`', 0};
1634 if (!list_empty(&package->files))
1635 return ERROR_SUCCESS;
1637 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1638 if (rc != ERROR_SUCCESS)
1639 return ERROR_SUCCESS;
1641 rc = MSI_IterateRecords(view, NULL, load_file, package);
1642 msiobj_release(&view->hdr);
1644 return ERROR_SUCCESS;
1647 static UINT load_folder( MSIRECORD *row, LPVOID param )
1649 MSIPACKAGE *package = param;
1650 static const WCHAR szDot[] = { '.',0 };
1651 static WCHAR szEmpty[] = { 0 };
1652 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1655 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1657 return ERROR_NOT_ENOUGH_MEMORY;
1659 folder->Directory = msi_dup_record_field( row, 1 );
1661 TRACE("%s\n", debugstr_w(folder->Directory));
1663 p = msi_dup_record_field(row, 3);
1665 /* split src and target dir */
1667 src_short = folder_split_path( p, ':' );
1669 /* split the long and short paths */
1670 tgt_long = folder_split_path( tgt_short, '|' );
1671 src_long = folder_split_path( src_short, '|' );
1673 /* check for no-op dirs */
1674 if (!lstrcmpW(szDot, tgt_short))
1675 tgt_short = szEmpty;
1676 if (!lstrcmpW(szDot, src_short))
1677 src_short = szEmpty;
1680 tgt_long = tgt_short;
1683 src_short = tgt_short;
1684 src_long = tgt_long;
1688 src_long = src_short;
1690 /* FIXME: use the target short path too */
1691 folder->TargetDefault = strdupW(tgt_long);
1692 folder->SourceShortPath = strdupW(src_short);
1693 folder->SourceLongPath = strdupW(src_long);
1696 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1697 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1698 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1700 folder->Parent = msi_dup_record_field( row, 2 );
1702 folder->Property = msi_dup_property( package, folder->Directory );
1704 list_add_tail( &package->folders, &folder->entry );
1706 TRACE("returning %p\n", folder);
1708 return ERROR_SUCCESS;
1711 static UINT load_all_folders( MSIPACKAGE *package )
1713 static const WCHAR query[] = {
1714 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1715 '`','D','i','r','e','c','t','o','r','y','`',0 };
1719 if (!list_empty(&package->folders))
1720 return ERROR_SUCCESS;
1722 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1723 if (r != ERROR_SUCCESS)
1726 r = MSI_IterateRecords(view, NULL, load_folder, package);
1727 msiobj_release(&view->hdr);
1732 * I am not doing any of the costing functionality yet.
1733 * Mostly looking at doing the Component and Feature loading
1735 * The native MSI does A LOT of modification to tables here. Mostly adding
1736 * a lot of temporary columns to the Feature and Component tables.
1738 * note: Native msi also tracks the short filename. But I am only going to
1739 * track the long ones. Also looking at this directory table
1740 * it appears that the directory table does not get the parents
1741 * resolved base on property only based on their entries in the
1744 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1746 static const WCHAR szCosting[] =
1747 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1748 static const WCHAR szZero[] = { '0', 0 };
1750 MSI_SetPropertyW(package, szCosting, szZero);
1751 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1753 load_all_folders( package );
1754 load_all_components( package );
1755 load_all_features( package );
1756 load_all_files( package );
1758 return ERROR_SUCCESS;
1761 static UINT execute_script(MSIPACKAGE *package, UINT script )
1764 UINT rc = ERROR_SUCCESS;
1766 TRACE("Executing Script %i\n",script);
1768 if (!package->script)
1770 ERR("no script!\n");
1771 return ERROR_FUNCTION_FAILED;
1774 for (i = 0; i < package->script->ActionCount[script]; i++)
1777 action = package->script->Actions[script][i];
1778 ui_actionstart(package, action);
1779 TRACE("Executing Action (%s)\n",debugstr_w(action));
1780 rc = ACTION_PerformAction(package, action, script, TRUE);
1781 if (rc != ERROR_SUCCESS)
1784 msi_free_action_script(package, script);
1788 static UINT ACTION_FileCost(MSIPACKAGE *package)
1790 return ERROR_SUCCESS;
1793 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1799 state = MsiQueryProductStateW(package->ProductCode);
1801 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1803 if (!comp->ComponentId)
1806 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1807 comp->Installed = INSTALLSTATE_ABSENT;
1810 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1811 package->Context, comp->ComponentId,
1813 if (r != ERROR_SUCCESS)
1814 comp->Installed = INSTALLSTATE_ABSENT;
1819 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1821 MSIFEATURE *feature;
1824 state = MsiQueryProductStateW(package->ProductCode);
1826 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1828 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1829 feature->Installed = INSTALLSTATE_ABSENT;
1832 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1838 static BOOL process_state_property(MSIPACKAGE* package, int level,
1839 LPCWSTR property, INSTALLSTATE state)
1841 static const WCHAR all[]={'A','L','L',0};
1842 static const WCHAR remove[] = {'R','E','M','O','V','E',0};
1843 static const WCHAR reinstall[] = {'R','E','I','N','S','T','A','L','L',0};
1845 MSIFEATURE *feature;
1847 override = msi_dup_property( package, property );
1851 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1853 if (lstrcmpW(property, remove) &&
1854 (feature->Level <= 0 || feature->Level > level))
1857 if (!strcmpW(property, reinstall)) state = feature->Installed;
1859 if (strcmpiW(override,all)==0)
1860 msi_feature_set_state(package, feature, state);
1863 LPWSTR ptr = override;
1864 LPWSTR ptr2 = strchrW(override,',');
1868 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1869 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1871 msi_feature_set_state(package, feature, state);
1877 ptr2 = strchrW(ptr,',');
1889 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1892 static const WCHAR szlevel[] =
1893 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1894 static const WCHAR szAddLocal[] =
1895 {'A','D','D','L','O','C','A','L',0};
1896 static const WCHAR szAddSource[] =
1897 {'A','D','D','S','O','U','R','C','E',0};
1898 static const WCHAR szRemove[] =
1899 {'R','E','M','O','V','E',0};
1900 static const WCHAR szReinstall[] =
1901 {'R','E','I','N','S','T','A','L','L',0};
1902 static const WCHAR szAdvertise[] =
1903 {'A','D','V','E','R','T','I','S','E',0};
1904 BOOL override = FALSE;
1905 MSICOMPONENT* component;
1906 MSIFEATURE *feature;
1909 /* I do not know if this is where it should happen.. but */
1911 TRACE("Checking Install Level\n");
1913 level = msi_get_property_int(package, szlevel, 1);
1915 /* ok here is the _real_ rub
1916 * all these activation/deactivation things happen in order and things
1917 * later on the list override things earlier on the list.
1918 * 0) INSTALLLEVEL processing
1929 * 11) FILEADDDEFAULT
1931 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1932 * REMOVE are the big ones, since we don't handle administrative installs
1935 override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
1936 override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
1937 override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
1938 override |= process_state_property(package, level, szReinstall, INSTALLSTATE_UNKNOWN);
1939 override |= process_state_property(package, level, szAdvertise, INSTALLSTATE_ADVERTISED);
1943 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1945 BOOL feature_state = ((feature->Level > 0) &&
1946 (feature->Level <= level));
1948 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1950 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1951 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1952 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1953 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1955 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1959 /* disable child features of unselected parent features */
1960 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1964 if (feature->Level > 0 && feature->Level <= level)
1967 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1968 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1973 /* set the Preselected Property */
1974 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1975 static const WCHAR szOne[] = { '1', 0 };
1977 MSI_SetPropertyW(package,szPreselected,szOne);
1981 * now we want to enable or disable components base on feature
1984 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1988 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1989 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1991 if (!feature->Level)
1994 /* features with components that have compressed files are made local */
1995 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1997 if (cl->component->Enabled &&
1998 cl->component->ForceLocalState &&
1999 feature->Action == INSTALLSTATE_SOURCE)
2001 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
2006 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2008 component = cl->component;
2010 if (!component->Enabled)
2013 switch (feature->Action)
2015 case INSTALLSTATE_ABSENT:
2016 component->anyAbsent = 1;
2018 case INSTALLSTATE_ADVERTISED:
2019 component->hasAdvertiseFeature = 1;
2021 case INSTALLSTATE_SOURCE:
2022 component->hasSourceFeature = 1;
2024 case INSTALLSTATE_LOCAL:
2025 component->hasLocalFeature = 1;
2027 case INSTALLSTATE_DEFAULT:
2028 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
2029 component->hasAdvertiseFeature = 1;
2030 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
2031 component->hasSourceFeature = 1;
2033 component->hasLocalFeature = 1;
2041 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2043 /* if the component isn't enabled, leave it alone */
2044 if (!component->Enabled)
2047 /* check if it's local or source */
2048 if (!(component->Attributes & msidbComponentAttributesOptional) &&
2049 (component->hasLocalFeature || component->hasSourceFeature))
2051 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
2052 !component->ForceLocalState)
2053 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
2055 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
2059 /* if any feature is local, the component must be local too */
2060 if (component->hasLocalFeature)
2062 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
2066 if (component->hasSourceFeature)
2068 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
2072 if (component->hasAdvertiseFeature)
2074 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
2078 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
2079 if (component->anyAbsent)
2080 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
2083 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2085 if (component->Action == INSTALLSTATE_DEFAULT)
2087 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
2088 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
2091 TRACE("Result: Component %s (Installed %i, Action %i)\n",
2092 debugstr_w(component->Component), component->Installed, component->Action);
2096 return ERROR_SUCCESS;
2099 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
2101 MSIPACKAGE *package = param;
2106 name = MSI_RecordGetString(row,1);
2108 f = get_loaded_folder(package, name);
2109 if (!f) return ERROR_SUCCESS;
2111 /* reset the ResolvedTarget */
2112 msi_free(f->ResolvedTarget);
2113 f->ResolvedTarget = NULL;
2115 /* This helper function now does ALL the work */
2116 TRACE("Dir %s ...\n",debugstr_w(name));
2117 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2118 TRACE("resolves to %s\n",debugstr_w(path));
2121 return ERROR_SUCCESS;
2124 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2126 MSIPACKAGE *package = param;
2128 MSIFEATURE *feature;
2130 name = MSI_RecordGetString( row, 1 );
2132 feature = get_loaded_feature( package, name );
2134 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2138 Condition = MSI_RecordGetString(row,3);
2140 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2142 int level = MSI_RecordGetInteger(row,2);
2143 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2144 feature->Level = level;
2147 return ERROR_SUCCESS;
2150 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2152 static const WCHAR name_fmt[] =
2153 {'%','u','.','%','u','.','%','u','.','%','u',0};
2154 static const WCHAR name[] = {'\\',0};
2155 VS_FIXEDFILEINFO *lpVer;
2156 WCHAR filever[0x100];
2162 TRACE("%s\n", debugstr_w(filename));
2164 versize = GetFileVersionInfoSizeW( filename, &handle );
2168 version = msi_alloc( versize );
2169 GetFileVersionInfoW( filename, 0, versize, version );
2171 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2173 msi_free( version );
2177 sprintfW( filever, name_fmt,
2178 HIWORD(lpVer->dwFileVersionMS),
2179 LOWORD(lpVer->dwFileVersionMS),
2180 HIWORD(lpVer->dwFileVersionLS),
2181 LOWORD(lpVer->dwFileVersionLS));
2183 msi_free( version );
2185 return strdupW( filever );
2188 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2190 LPWSTR file_version;
2193 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2195 MSICOMPONENT* comp = file->Component;
2201 if (file->IsCompressed)
2202 comp->ForceLocalState = TRUE;
2204 /* calculate target */
2205 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2207 msi_free(file->TargetPath);
2209 TRACE("file %s is named %s\n",
2210 debugstr_w(file->File), debugstr_w(file->FileName));
2212 file->TargetPath = build_directory_name(2, p, file->FileName);
2216 TRACE("file %s resolves to %s\n",
2217 debugstr_w(file->File), debugstr_w(file->TargetPath));
2219 /* don't check files of components that aren't installed */
2220 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2221 comp->Installed == INSTALLSTATE_ABSENT)
2223 file->state = msifs_missing; /* assume files are missing */
2227 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2229 file->state = msifs_missing;
2230 comp->Cost += file->FileSize;
2234 if (file->Version &&
2235 (file_version = msi_get_disk_file_version( file->TargetPath )))
2237 TRACE("new %s old %s\n", debugstr_w(file->Version),
2238 debugstr_w(file_version));
2239 /* FIXME: seems like a bad way to compare version numbers */
2240 if (lstrcmpiW(file_version, file->Version)<0)
2242 file->state = msifs_overwrite;
2243 comp->Cost += file->FileSize;
2246 file->state = msifs_present;
2247 msi_free( file_version );
2250 file->state = msifs_present;
2253 return ERROR_SUCCESS;
2257 * A lot is done in this function aside from just the costing.
2258 * The costing needs to be implemented at some point but for now I am going
2259 * to focus on the directory building
2262 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2264 static const WCHAR ExecSeqQuery[] =
2265 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2266 '`','D','i','r','e','c','t','o','r','y','`',0};
2267 static const WCHAR ConditionQuery[] =
2268 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2269 '`','C','o','n','d','i','t','i','o','n','`',0};
2270 static const WCHAR szCosting[] =
2271 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2272 static const WCHAR szlevel[] =
2273 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2274 static const WCHAR szOutOfDiskSpace[] =
2275 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2276 static const WCHAR szOne[] = { '1', 0 };
2277 static const WCHAR szZero[] = { '0', 0 };
2283 TRACE("Building Directory properties\n");
2285 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2286 if (rc == ERROR_SUCCESS)
2288 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2290 msiobj_release(&view->hdr);
2293 /* read components states from the registry */
2294 ACTION_GetComponentInstallStates(package);
2295 ACTION_GetFeatureInstallStates(package);
2297 TRACE("File calculations\n");
2298 msi_check_file_install_states( package );
2300 TRACE("Evaluating Condition Table\n");
2302 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2303 if (rc == ERROR_SUCCESS)
2305 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2307 msiobj_release(&view->hdr);
2310 TRACE("Enabling or Disabling Components\n");
2311 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2313 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2315 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2316 comp->Enabled = FALSE;
2319 comp->Enabled = TRUE;
2322 MSI_SetPropertyW(package,szCosting,szOne);
2323 /* set default run level if not set */
2324 level = msi_dup_property( package, szlevel );
2326 MSI_SetPropertyW(package,szlevel, szOne);
2329 /* FIXME: check volume disk space */
2330 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2332 return MSI_SetFeatureStates(package);
2335 /* OK this value is "interpreted" and then formatted based on the
2336 first few characters */
2337 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2342 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2348 LPWSTR deformated = NULL;
2351 deformat_string(package, &value[2], &deformated);
2353 /* binary value type */
2357 *size = (strlenW(ptr)/2)+1;
2359 *size = strlenW(ptr)/2;
2361 data = msi_alloc(*size);
2367 /* if uneven pad with a zero in front */
2373 data[count] = (BYTE)strtol(byte,NULL,0);
2375 TRACE("Uneven byte count\n");
2383 data[count] = (BYTE)strtol(byte,NULL,0);
2386 msi_free(deformated);
2388 TRACE("Data %i bytes(%i)\n",*size,count);
2395 deformat_string(package, &value[1], &deformated);
2398 *size = sizeof(DWORD);
2399 data = msi_alloc(*size);
2405 if ( (*p < '0') || (*p > '9') )
2411 if (deformated[0] == '-')
2414 TRACE("DWORD %i\n",*(LPDWORD)data);
2416 msi_free(deformated);
2421 static const WCHAR szMulti[] = {'[','~',']',0};
2430 *type=REG_EXPAND_SZ;
2438 if (strstrW(value,szMulti))
2439 *type = REG_MULTI_SZ;
2441 /* remove initial delimiter */
2442 if (!strncmpW(value, szMulti, 3))
2445 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2447 /* add double NULL terminator */
2448 if (*type == REG_MULTI_SZ)
2450 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2451 data = msi_realloc_zero(data, *size);
2457 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2459 MSIPACKAGE *package = param;
2460 static const WCHAR szHCR[] =
2461 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2462 'R','O','O','T','\\',0};
2463 static const WCHAR szHCU[] =
2464 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2465 'U','S','E','R','\\',0};
2466 static const WCHAR szHLM[] =
2467 {'H','K','E','Y','_','L','O','C','A','L','_',
2468 'M','A','C','H','I','N','E','\\',0};
2469 static const WCHAR szHU[] =
2470 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2472 LPSTR value_data = NULL;
2473 HKEY root_key, hkey;
2476 LPCWSTR szRoot, component, name, key, value;
2481 BOOL check_first = FALSE;
2484 ui_progress(package,2,0,0,0);
2491 component = MSI_RecordGetString(row, 6);
2492 comp = get_loaded_component(package,component);
2494 return ERROR_SUCCESS;
2496 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2498 TRACE("Skipping write due to disabled component %s\n",
2499 debugstr_w(component));
2501 comp->Action = comp->Installed;
2503 return ERROR_SUCCESS;
2506 comp->Action = INSTALLSTATE_LOCAL;
2508 name = MSI_RecordGetString(row, 4);
2509 if( MSI_RecordIsNull(row,5) && name )
2511 /* null values can have special meanings */
2512 if (name[0]=='-' && name[1] == 0)
2513 return ERROR_SUCCESS;
2514 else if ((name[0]=='+' && name[1] == 0) ||
2515 (name[0] == '*' && name[1] == 0))
2520 root = MSI_RecordGetInteger(row,2);
2521 key = MSI_RecordGetString(row, 3);
2523 /* get the root key */
2528 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2529 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2530 if (all_users && all_users[0] == '1')
2532 root_key = HKEY_LOCAL_MACHINE;
2537 root_key = HKEY_CURRENT_USER;
2540 msi_free(all_users);
2543 case 0: root_key = HKEY_CLASSES_ROOT;
2546 case 1: root_key = HKEY_CURRENT_USER;
2549 case 2: root_key = HKEY_LOCAL_MACHINE;
2552 case 3: root_key = HKEY_USERS;
2556 ERR("Unknown root %i\n",root);
2562 return ERROR_SUCCESS;
2564 deformat_string(package, key , &deformated);
2565 size = strlenW(deformated) + strlenW(szRoot) + 1;
2566 uikey = msi_alloc(size*sizeof(WCHAR));
2567 strcpyW(uikey,szRoot);
2568 strcatW(uikey,deformated);
2570 if (RegCreateKeyW( root_key, deformated, &hkey))
2572 ERR("Could not create key %s\n",debugstr_w(deformated));
2573 msi_free(deformated);
2575 return ERROR_SUCCESS;
2577 msi_free(deformated);
2579 value = MSI_RecordGetString(row,5);
2581 value_data = parse_value(package, value, &type, &size);
2584 static const WCHAR szEmpty[] = {0};
2585 value_data = (LPSTR)strdupW(szEmpty);
2586 size = sizeof(szEmpty);
2590 deformat_string(package, name, &deformated);
2594 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2596 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2601 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2602 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2604 TRACE("value %s of %s checked already exists\n",
2605 debugstr_w(deformated), debugstr_w(uikey));
2609 TRACE("Checked and setting value %s of %s\n",
2610 debugstr_w(deformated), debugstr_w(uikey));
2611 if (deformated || size)
2612 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2617 uirow = MSI_CreateRecord(3);
2618 MSI_RecordSetStringW(uirow,2,deformated);
2619 MSI_RecordSetStringW(uirow,1,uikey);
2622 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2624 MSI_RecordSetStringW(uirow,3,value);
2626 ui_actiondata(package,szWriteRegistryValues,uirow);
2627 msiobj_release( &uirow->hdr );
2629 msi_free(value_data);
2630 msi_free(deformated);
2633 return ERROR_SUCCESS;
2636 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2640 static const WCHAR ExecSeqQuery[] =
2641 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2642 '`','R','e','g','i','s','t','r','y','`',0 };
2644 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2645 if (rc != ERROR_SUCCESS)
2646 return ERROR_SUCCESS;
2648 /* increment progress bar each time action data is sent */
2649 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2651 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2653 msiobj_release(&view->hdr);
2657 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2659 package->script->CurrentlyScripting = TRUE;
2661 return ERROR_SUCCESS;
2665 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2670 static const WCHAR q1[]=
2671 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2672 '`','R','e','g','i','s','t','r','y','`',0};
2675 MSIFEATURE *feature;
2678 TRACE("InstallValidate\n");
2680 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2681 if (rc == ERROR_SUCCESS)
2683 MSI_IterateRecords( view, &progress, NULL, package );
2684 msiobj_release( &view->hdr );
2685 total += progress * REG_PROGRESS_VALUE;
2688 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2689 total += COMPONENT_PROGRESS_VALUE;
2691 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2692 total += file->FileSize;
2694 ui_progress(package,0,total,0,0);
2696 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2698 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2699 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2700 feature->ActionRequest);
2703 return ERROR_SUCCESS;
2706 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2708 MSIPACKAGE* package = param;
2709 LPCWSTR cond = NULL;
2710 LPCWSTR message = NULL;
2713 static const WCHAR title[]=
2714 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2716 cond = MSI_RecordGetString(row,1);
2718 r = MSI_EvaluateConditionW(package,cond);
2719 if (r == MSICONDITION_FALSE)
2721 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2724 message = MSI_RecordGetString(row,2);
2725 deformat_string(package,message,&deformated);
2726 MessageBoxW(NULL,deformated,title,MB_OK);
2727 msi_free(deformated);
2730 return ERROR_INSTALL_FAILURE;
2733 return ERROR_SUCCESS;
2736 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2739 MSIQUERY * view = NULL;
2740 static const WCHAR ExecSeqQuery[] =
2741 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2742 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2744 TRACE("Checking launch conditions\n");
2746 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2747 if (rc != ERROR_SUCCESS)
2748 return ERROR_SUCCESS;
2750 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2751 msiobj_release(&view->hdr);
2756 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2760 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2762 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2764 MSIRECORD * row = 0;
2766 LPWSTR deformated,buffer,deformated_name;
2768 static const WCHAR ExecSeqQuery[] =
2769 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2770 '`','R','e','g','i','s','t','r','y','`',' ',
2771 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2772 ' ','=',' ' ,'\'','%','s','\'',0 };
2773 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2774 static const WCHAR fmt2[]=
2775 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2777 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2781 root = MSI_RecordGetInteger(row,2);
2782 key = MSI_RecordGetString(row, 3);
2783 name = MSI_RecordGetString(row, 4);
2784 deformat_string(package, key , &deformated);
2785 deformat_string(package, name, &deformated_name);
2787 len = strlenW(deformated) + 6;
2788 if (deformated_name)
2789 len+=strlenW(deformated_name);
2791 buffer = msi_alloc( len *sizeof(WCHAR));
2793 if (deformated_name)
2794 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2796 sprintfW(buffer,fmt,root,deformated);
2798 msi_free(deformated);
2799 msi_free(deformated_name);
2800 msiobj_release(&row->hdr);
2804 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2806 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2811 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2814 return strdupW( file->TargetPath );
2819 static HKEY openSharedDLLsKey(void)
2822 static const WCHAR path[] =
2823 {'S','o','f','t','w','a','r','e','\\',
2824 'M','i','c','r','o','s','o','f','t','\\',
2825 'W','i','n','d','o','w','s','\\',
2826 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2827 'S','h','a','r','e','d','D','L','L','s',0};
2829 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2833 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2838 DWORD sz = sizeof(count);
2841 hkey = openSharedDLLsKey();
2842 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2843 if (rc != ERROR_SUCCESS)
2849 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2853 hkey = openSharedDLLsKey();
2855 msi_reg_set_val_dword( hkey, path, count );
2857 RegDeleteValueW(hkey,path);
2863 * Return TRUE if the count should be written out and FALSE if not
2865 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2867 MSIFEATURE *feature;
2871 /* only refcount DLLs */
2872 if (comp->KeyPath == NULL ||
2873 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2874 comp->Attributes & msidbComponentAttributesODBCDataSource)
2878 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2879 write = (count > 0);
2881 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2885 /* increment counts */
2886 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2890 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2893 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2895 if ( cl->component == comp )
2900 /* decrement counts */
2901 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2905 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2908 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2910 if ( cl->component == comp )
2915 /* ref count all the files in the component */
2920 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2922 if (file->Component == comp)
2923 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2927 /* add a count for permanent */
2928 if (comp->Attributes & msidbComponentAttributesPermanent)
2931 comp->RefCount = count;
2934 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2937 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2939 WCHAR squished_pc[GUID_SIZE];
2940 WCHAR squished_cc[GUID_SIZE];
2947 squash_guid(package->ProductCode,squished_pc);
2948 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2950 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2954 ui_progress(package,2,0,0,0);
2955 if (!comp->ComponentId)
2958 squash_guid(comp->ComponentId,squished_cc);
2960 msi_free(comp->FullKeypath);
2961 comp->FullKeypath = resolve_keypath( package, comp );
2963 ACTION_RefCountComponent( package, comp );
2965 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2966 debugstr_w(comp->Component),
2967 debugstr_w(squished_cc),
2968 debugstr_w(comp->FullKeypath),
2971 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2972 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2974 if (!comp->FullKeypath)
2977 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2978 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2981 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2984 if (rc != ERROR_SUCCESS)
2987 if (comp->Attributes & msidbComponentAttributesPermanent)
2989 static const WCHAR szPermKey[] =
2990 { '0','0','0','0','0','0','0','0','0','0','0','0',
2991 '0','0','0','0','0','0','0','0','0','0','0','0',
2992 '0','0','0','0','0','0','0','0',0 };
2994 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2997 if (comp->Action == INSTALLSTATE_LOCAL)
2998 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3004 WCHAR source[MAX_PATH];
3005 WCHAR base[MAX_PATH];
3008 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3009 static const WCHAR query[] = {
3010 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3011 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3012 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3013 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3014 '`','D','i','s','k','I','d','`',0};
3016 file = get_loaded_file(package, comp->KeyPath);
3020 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3021 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3022 ptr2 = strrchrW(source, '\\') + 1;
3023 msiobj_release(&row->hdr);
3025 lstrcpyW(base, package->PackagePath);
3026 ptr = strrchrW(base, '\\');
3029 sourcepath = resolve_file_source(package, file);
3030 ptr = sourcepath + lstrlenW(base);
3031 lstrcpyW(ptr2, ptr);
3032 msi_free(sourcepath);
3034 msi_reg_set_val_str(hkey, squished_pc, source);
3038 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
3040 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3041 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3043 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3047 uirow = MSI_CreateRecord(3);
3048 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3049 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3050 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3051 ui_actiondata(package,szProcessComponents,uirow);
3052 msiobj_release( &uirow->hdr );
3055 return ERROR_SUCCESS;
3066 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3067 LPWSTR lpszName, LONG_PTR lParam)
3070 typelib_struct *tl_struct = (typelib_struct*) lParam;
3071 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3075 if (!IS_INTRESOURCE(lpszName))
3077 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3081 sz = strlenW(tl_struct->source)+4;
3082 sz *= sizeof(WCHAR);
3084 if ((INT_PTR)lpszName == 1)
3085 tl_struct->path = strdupW(tl_struct->source);
3088 tl_struct->path = msi_alloc(sz);
3089 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3092 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3093 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3096 msi_free(tl_struct->path);
3097 tl_struct->path = NULL;
3102 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3103 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3105 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3109 msi_free(tl_struct->path);
3110 tl_struct->path = NULL;
3112 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3113 ITypeLib_Release(tl_struct->ptLib);
3118 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3120 MSIPACKAGE* package = param;
3124 typelib_struct tl_struct;
3129 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3131 component = MSI_RecordGetString(row,3);
3132 comp = get_loaded_component(package,component);
3134 return ERROR_SUCCESS;
3136 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3138 TRACE("Skipping typelib reg due to disabled component\n");
3140 comp->Action = comp->Installed;
3142 return ERROR_SUCCESS;
3145 comp->Action = INSTALLSTATE_LOCAL;
3147 file = get_loaded_file( package, comp->KeyPath );
3149 return ERROR_SUCCESS;
3151 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3155 guid = MSI_RecordGetString(row,1);
3156 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3157 tl_struct.source = strdupW( file->TargetPath );
3158 tl_struct.path = NULL;
3160 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3161 (LONG_PTR)&tl_struct);
3169 helpid = MSI_RecordGetString(row,6);
3172 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3173 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3177 ERR("Failed to register type library %s\n",
3178 debugstr_w(tl_struct.path));
3181 ui_actiondata(package,szRegisterTypeLibraries,row);
3183 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3186 ITypeLib_Release(tl_struct.ptLib);
3187 msi_free(tl_struct.path);
3190 ERR("Failed to load type library %s\n",
3191 debugstr_w(tl_struct.source));
3193 FreeLibrary(module);
3194 msi_free(tl_struct.source);
3198 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3201 ERR("Failed to load type library: %08x\n", hr);
3202 return ERROR_FUNCTION_FAILED;
3205 ITypeLib_Release(tlib);
3208 return ERROR_SUCCESS;
3211 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3214 * OK this is a bit confusing.. I am given a _Component key and I believe
3215 * that the file that is being registered as a type library is the "key file
3216 * of that component" which I interpret to mean "The file in the KeyPath of
3221 static const WCHAR Query[] =
3222 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3223 '`','T','y','p','e','L','i','b','`',0};
3225 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3226 if (rc != ERROR_SUCCESS)
3227 return ERROR_SUCCESS;
3229 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3230 msiobj_release(&view->hdr);
3234 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3236 MSIPACKAGE *package = param;
3237 LPWSTR target_file, target_folder, filename;
3238 LPCWSTR buffer, extension;
3240 static const WCHAR szlnk[]={'.','l','n','k',0};
3241 IShellLinkW *sl = NULL;
3242 IPersistFile *pf = NULL;
3245 buffer = MSI_RecordGetString(row,4);
3246 comp = get_loaded_component(package,buffer);
3248 return ERROR_SUCCESS;
3250 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3252 TRACE("Skipping shortcut creation due to disabled component\n");
3254 comp->Action = comp->Installed;
3256 return ERROR_SUCCESS;
3259 comp->Action = INSTALLSTATE_LOCAL;
3261 ui_actiondata(package,szCreateShortcuts,row);
3263 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3264 &IID_IShellLinkW, (LPVOID *) &sl );
3268 ERR("CLSID_ShellLink not available\n");
3272 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3275 ERR("QueryInterface(IID_IPersistFile) failed\n");
3279 buffer = MSI_RecordGetString(row,2);
3280 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3282 /* may be needed because of a bug somewhere else */
3283 create_full_pathW(target_folder);
3285 filename = msi_dup_record_field( row, 3 );
3286 reduce_to_longfilename(filename);
3288 extension = strchrW(filename,'.');
3289 if (!extension || strcmpiW(extension,szlnk))
3291 int len = strlenW(filename);
3292 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3293 memcpy(filename + len, szlnk, sizeof(szlnk));
3295 target_file = build_directory_name(2, target_folder, filename);
3296 msi_free(target_folder);
3299 buffer = MSI_RecordGetString(row,5);
3300 if (strchrW(buffer,'['))
3303 deformat_string(package,buffer,&deformated);
3304 IShellLinkW_SetPath(sl,deformated);
3305 msi_free(deformated);
3309 FIXME("poorly handled shortcut format, advertised shortcut\n");
3310 IShellLinkW_SetPath(sl,comp->FullKeypath);
3313 if (!MSI_RecordIsNull(row,6))
3316 buffer = MSI_RecordGetString(row,6);
3317 deformat_string(package,buffer,&deformated);
3318 IShellLinkW_SetArguments(sl,deformated);
3319 msi_free(deformated);
3322 if (!MSI_RecordIsNull(row,7))
3324 buffer = MSI_RecordGetString(row,7);
3325 IShellLinkW_SetDescription(sl,buffer);
3328 if (!MSI_RecordIsNull(row,8))
3329 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3331 if (!MSI_RecordIsNull(row,9))
3336 buffer = MSI_RecordGetString(row,9);
3338 Path = build_icon_path(package,buffer);
3339 index = MSI_RecordGetInteger(row,10);
3341 /* no value means 0 */
3342 if (index == MSI_NULL_INTEGER)
3345 IShellLinkW_SetIconLocation(sl,Path,index);
3349 if (!MSI_RecordIsNull(row,11))
3350 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3352 if (!MSI_RecordIsNull(row,12))
3355 buffer = MSI_RecordGetString(row,12);
3356 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3358 IShellLinkW_SetWorkingDirectory(sl,Path);
3362 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3363 IPersistFile_Save(pf,target_file,FALSE);
3365 msi_free(target_file);
3369 IPersistFile_Release( pf );
3371 IShellLinkW_Release( sl );
3373 return ERROR_SUCCESS;
3376 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3381 static const WCHAR Query[] =
3382 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3383 '`','S','h','o','r','t','c','u','t','`',0};
3385 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3386 if (rc != ERROR_SUCCESS)
3387 return ERROR_SUCCESS;
3389 res = CoInitialize( NULL );
3391 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3392 msiobj_release(&view->hdr);
3400 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3402 MSIPACKAGE* package = param;
3411 FileName = MSI_RecordGetString(row,1);
3414 ERR("Unable to get FileName\n");
3415 return ERROR_SUCCESS;
3418 FilePath = build_icon_path(package,FileName);
3420 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3422 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3423 FILE_ATTRIBUTE_NORMAL, NULL);
3425 if (the_file == INVALID_HANDLE_VALUE)
3427 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3429 return ERROR_SUCCESS;
3436 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3437 if (rc != ERROR_SUCCESS)
3439 ERR("Failed to get stream\n");
3440 CloseHandle(the_file);
3441 DeleteFileW(FilePath);
3444 WriteFile(the_file,buffer,sz,&write,NULL);
3445 } while (sz == 1024);
3449 CloseHandle(the_file);
3451 uirow = MSI_CreateRecord(1);
3452 MSI_RecordSetStringW(uirow,1,FileName);
3453 ui_actiondata(package,szPublishProduct,uirow);
3454 msiobj_release( &uirow->hdr );
3456 return ERROR_SUCCESS;
3459 static UINT msi_publish_icons(MSIPACKAGE *package)
3464 static const WCHAR query[]= {
3465 'S','E','L','E','C','T',' ','*',' ',
3466 'F','R','O','M',' ','`','I','c','o','n','`',0};
3468 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3469 if (r == ERROR_SUCCESS)
3471 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3472 msiobj_release(&view->hdr);
3475 return ERROR_SUCCESS;
3478 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3484 MSISOURCELISTINFO *info;
3486 static const WCHAR szEmpty[] = {0};
3487 static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
3489 r = RegCreateKeyW(hkey, szSourceList, &source);
3490 if (r != ERROR_SUCCESS)
3493 RegCloseKey(source);
3495 buffer = strrchrW(package->PackagePath, '\\') + 1;
3496 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3497 package->Context, MSICODE_PRODUCT,
3498 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3499 if (r != ERROR_SUCCESS)
3502 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3503 package->Context, MSICODE_PRODUCT,
3504 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3505 if (r != ERROR_SUCCESS)
3508 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3509 package->Context, MSICODE_PRODUCT,
3510 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3511 if (r != ERROR_SUCCESS)
3514 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3516 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3517 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3518 info->options, info->value);
3520 MsiSourceListSetInfoW(package->ProductCode, NULL,
3521 info->context, info->options,
3522 info->property, info->value);
3525 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3527 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3528 disk->context, disk->options,
3529 disk->disk_id, disk->volume_label, disk->disk_prompt);
3532 return ERROR_SUCCESS;
3535 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3537 MSIHANDLE hdb, suminfo;
3538 WCHAR guids[MAX_PATH];
3539 WCHAR packcode[SQUISH_GUID_SIZE];
3546 static const WCHAR szProductLanguage[] =
3547 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3548 static const WCHAR szARPProductIcon[] =
3549 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3550 static const WCHAR szProductVersion[] =
3551 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3552 static const WCHAR szAssignment[] =
3553 {'A','s','s','i','g','n','m','e','n','t',0};
3554 static const WCHAR szAdvertiseFlags[] =
3555 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3556 static const WCHAR szClients[] =
3557 {'C','l','i','e','n','t','s',0};
3558 static const WCHAR szColon[] = {':',0};
3560 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3561 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3564 langid = msi_get_property_int(package, szProductLanguage, 0);
3565 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3568 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3570 buffer = msi_dup_property(package, szARPProductIcon);
3573 LPWSTR path = build_icon_path(package,buffer);
3574 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3579 buffer = msi_dup_property(package, szProductVersion);
3582 DWORD verdword = msi_version_str_to_dword(buffer);
3583 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3587 msi_reg_set_val_dword(hkey, szAssignment, 0);
3588 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3589 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3590 msi_reg_set_val_str(hkey, szClients, szColon);
3592 hdb = alloc_msihandle(&package->db->hdr);
3594 return ERROR_NOT_ENOUGH_MEMORY;
3596 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3597 MsiCloseHandle(hdb);
3598 if (r != ERROR_SUCCESS)
3602 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3603 NULL, guids, &size);
3604 if (r != ERROR_SUCCESS)
3607 ptr = strchrW(guids, ';');
3609 squash_guid(guids, packcode);
3610 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3613 MsiCloseHandle(suminfo);
3614 return ERROR_SUCCESS;
3617 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3622 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3624 static const WCHAR szUpgradeCode[] =
3625 {'U','p','g','r','a','d','e','C','o','d','e',0};
3627 upgrade = msi_dup_property(package, szUpgradeCode);
3629 return ERROR_SUCCESS;
3631 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3633 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3634 if (r != ERROR_SUCCESS)
3639 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3640 if (r != ERROR_SUCCESS)
3644 squash_guid(package->ProductCode, squashed_pc);
3645 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3654 static BOOL msi_check_publish(MSIPACKAGE *package)
3656 MSIFEATURE *feature;
3658 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3660 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3667 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3669 MSIFEATURE *feature;
3671 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3673 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3680 static UINT msi_publish_patch(MSIPACKAGE *package, HKEY prodkey, HKEY hudkey)
3682 WCHAR patch_squashed[GUID_SIZE];
3685 UINT r = ERROR_FUNCTION_FAILED;
3687 static const WCHAR szPatches[] = {'P','a','t','c','h','e','s',0};
3689 res = RegCreateKeyExW(prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
3691 if (res != ERROR_SUCCESS)
3692 return ERROR_FUNCTION_FAILED;
3694 squash_guid(package->patch->patchcode, patch_squashed);
3696 res = RegSetValueExW(patches, szPatches, 0, REG_MULTI_SZ,
3697 (const BYTE *)patch_squashed,
3698 (lstrlenW(patch_squashed) + 1) * sizeof(WCHAR));
3699 if (res != ERROR_SUCCESS)
3702 res = RegSetValueExW(patches, patch_squashed, 0, REG_SZ,
3703 (const BYTE *)package->patch->transforms,
3704 (lstrlenW(package->patch->transforms) + 1) * sizeof(WCHAR));
3705 if (res == ERROR_SUCCESS)
3709 RegCloseKey(patches);
3714 * 99% of the work done here is only done for
3715 * advertised installs. However this is where the
3716 * Icon table is processed and written out
3717 * so that is what I am going to do here.
3719 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3725 /* FIXME: also need to publish if the product is in advertise mode */
3726 if (!msi_check_publish(package))
3727 return ERROR_SUCCESS;
3729 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3731 if (rc != ERROR_SUCCESS)
3734 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3735 NULL, &hudkey, TRUE);
3736 if (rc != ERROR_SUCCESS)
3739 rc = msi_publish_upgrade_code(package);
3740 if (rc != ERROR_SUCCESS)
3745 rc = msi_publish_patch(package, hukey, hudkey);
3746 if (rc != ERROR_SUCCESS)
3750 rc = msi_publish_product_properties(package, hukey);
3751 if (rc != ERROR_SUCCESS)
3754 rc = msi_publish_sourcelist(package, hukey);
3755 if (rc != ERROR_SUCCESS)
3758 rc = msi_publish_icons(package);
3762 RegCloseKey(hudkey);
3767 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3769 MSIPACKAGE *package = param;
3770 LPCWSTR component, section, key, value, identifier, dirproperty;
3771 LPWSTR deformated_section, deformated_key, deformated_value;
3772 LPWSTR folder, filename, fullname = NULL;
3773 LPCWSTR filenameptr;
3777 static const WCHAR szWindowsFolder[] =
3778 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3780 component = MSI_RecordGetString(row, 8);
3781 comp = get_loaded_component(package,component);
3783 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3785 TRACE("Skipping ini file due to disabled component %s\n",
3786 debugstr_w(component));
3788 comp->Action = comp->Installed;
3790 return ERROR_SUCCESS;
3793 comp->Action = INSTALLSTATE_LOCAL;
3795 identifier = MSI_RecordGetString(row,1);
3796 dirproperty = MSI_RecordGetString(row,3);
3797 section = MSI_RecordGetString(row,4);
3798 key = MSI_RecordGetString(row,5);
3799 value = MSI_RecordGetString(row,6);
3800 action = MSI_RecordGetInteger(row,7);
3802 deformat_string(package,section,&deformated_section);
3803 deformat_string(package,key,&deformated_key);
3804 deformat_string(package,value,&deformated_value);
3806 filename = msi_dup_record_field(row, 2);
3807 if (filename && (filenameptr = strchrW(filename, '|')))
3810 filenameptr = filename;
3814 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3816 folder = msi_dup_property( package, dirproperty );
3819 folder = msi_dup_property( package, szWindowsFolder );
3823 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3827 fullname = build_directory_name(2, folder, filenameptr);
3831 TRACE("Adding value %s to section %s in %s\n",
3832 debugstr_w(deformated_key), debugstr_w(deformated_section),
3833 debugstr_w(fullname));
3834 WritePrivateProfileStringW(deformated_section, deformated_key,
3835 deformated_value, fullname);
3837 else if (action == 1)
3840 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3841 returned, 10, fullname);
3842 if (returned[0] == 0)
3844 TRACE("Adding value %s to section %s in %s\n",
3845 debugstr_w(deformated_key), debugstr_w(deformated_section),
3846 debugstr_w(fullname));
3848 WritePrivateProfileStringW(deformated_section, deformated_key,
3849 deformated_value, fullname);
3852 else if (action == 3)
3853 FIXME("Append to existing section not yet implemented\n");
3855 uirow = MSI_CreateRecord(4);
3856 MSI_RecordSetStringW(uirow,1,identifier);
3857 MSI_RecordSetStringW(uirow,2,deformated_section);
3858 MSI_RecordSetStringW(uirow,3,deformated_key);
3859 MSI_RecordSetStringW(uirow,4,deformated_value);
3860 ui_actiondata(package,szWriteIniValues,uirow);
3861 msiobj_release( &uirow->hdr );
3867 msi_free(deformated_key);
3868 msi_free(deformated_value);
3869 msi_free(deformated_section);
3870 return ERROR_SUCCESS;
3873 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3877 static const WCHAR ExecSeqQuery[] =
3878 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3879 '`','I','n','i','F','i','l','e','`',0};
3881 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3882 if (rc != ERROR_SUCCESS)
3884 TRACE("no IniFile table\n");
3885 return ERROR_SUCCESS;
3888 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3889 msiobj_release(&view->hdr);
3893 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3895 MSIPACKAGE *package = param;
3900 static const WCHAR ExeStr[] =
3901 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3902 static const WCHAR close[] = {'\"',0};
3904 PROCESS_INFORMATION info;
3909 memset(&si,0,sizeof(STARTUPINFOW));
3911 filename = MSI_RecordGetString(row,1);
3912 file = get_loaded_file( package, filename );
3916 ERR("Unable to find file id %s\n",debugstr_w(filename));
3917 return ERROR_SUCCESS;
3920 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3922 FullName = msi_alloc(len*sizeof(WCHAR));
3923 strcpyW(FullName,ExeStr);
3924 strcatW( FullName, file->TargetPath );
3925 strcatW(FullName,close);
3927 TRACE("Registering %s\n",debugstr_w(FullName));
3928 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3933 CloseHandle(info.hThread);
3934 msi_dialog_check_messages(info.hProcess);
3935 CloseHandle(info.hProcess);
3941 uirow = MSI_CreateRecord( 2 );
3942 uipath = strdupW( file->TargetPath );
3943 p = strrchrW(uipath,'\\');
3946 MSI_RecordSetStringW( uirow, 1, &p[1] );
3947 MSI_RecordSetStringW( uirow, 2, uipath);
3948 ui_actiondata( package, szSelfRegModules, uirow);
3949 msiobj_release( &uirow->hdr );
3951 /* FIXME: call ui_progress? */
3953 return ERROR_SUCCESS;
3956 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3960 static const WCHAR ExecSeqQuery[] =
3961 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3962 '`','S','e','l','f','R','e','g','`',0};
3964 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3965 if (rc != ERROR_SUCCESS)
3967 TRACE("no SelfReg table\n");
3968 return ERROR_SUCCESS;
3971 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3972 msiobj_release(&view->hdr);
3974 return ERROR_SUCCESS;
3977 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3979 MSIFEATURE *feature;
3982 HKEY userdata = NULL;
3984 if (!msi_check_publish(package))
3985 return ERROR_SUCCESS;
3987 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3989 if (rc != ERROR_SUCCESS)
3992 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3994 if (rc != ERROR_SUCCESS)
3997 /* here the guids are base 85 encoded */
3998 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4004 BOOL absent = FALSE;
4007 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
4008 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
4009 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
4013 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4017 if (feature->Feature_Parent)
4018 size += strlenW( feature->Feature_Parent )+2;
4020 data = msi_alloc(size * sizeof(WCHAR));
4023 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4025 MSICOMPONENT* component = cl->component;
4029 if (component->ComponentId)
4031 TRACE("From %s\n",debugstr_w(component->ComponentId));
4032 CLSIDFromString(component->ComponentId, &clsid);
4033 encode_base85_guid(&clsid,buf);
4034 TRACE("to %s\n",debugstr_w(buf));
4039 if (feature->Feature_Parent)
4041 static const WCHAR sep[] = {'\2',0};
4043 strcatW(data,feature->Feature_Parent);
4046 msi_reg_set_val_str( userdata, feature->Feature, data );
4050 if (feature->Feature_Parent)
4051 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4054 static const WCHAR emptyW[] = {0};
4055 size += sizeof(WCHAR);
4056 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4057 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
4061 size += 2*sizeof(WCHAR);
4062 data = msi_alloc(size);
4065 if (feature->Feature_Parent)
4066 strcpyW( &data[1], feature->Feature_Parent );
4067 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4073 uirow = MSI_CreateRecord( 1 );
4074 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4075 ui_actiondata( package, szPublishFeatures, uirow);
4076 msiobj_release( &uirow->hdr );
4077 /* FIXME: call ui_progress? */
4082 RegCloseKey(userdata);
4086 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4091 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4093 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4095 if (r == ERROR_SUCCESS)
4097 RegDeleteValueW(hkey, feature->Feature);
4101 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4103 if (r == ERROR_SUCCESS)
4105 RegDeleteValueW(hkey, feature->Feature);
4109 return ERROR_SUCCESS;
4112 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4114 MSIFEATURE *feature;
4116 if (!msi_check_unpublish(package))
4117 return ERROR_SUCCESS;
4119 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4121 msi_unpublish_feature(package, feature);
4124 return ERROR_SUCCESS;
4127 static UINT msi_get_local_package_name( LPWSTR path )
4129 static const WCHAR szInstaller[] = {
4130 '\\','I','n','s','t','a','l','l','e','r','\\',0};
4131 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
4135 time = GetTickCount();
4136 GetWindowsDirectoryW( path, MAX_PATH );
4137 lstrcatW( path, szInstaller );
4138 CreateDirectoryW( path, NULL );
4140 len = lstrlenW(path);
4141 for (i=0; i<0x10000; i++)
4143 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
4144 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
4145 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
4146 if (handle != INVALID_HANDLE_VALUE)
4148 CloseHandle(handle);
4151 if (GetLastError() != ERROR_FILE_EXISTS &&
4152 GetLastError() != ERROR_SHARING_VIOLATION)
4153 return ERROR_FUNCTION_FAILED;
4156 return ERROR_SUCCESS;
4159 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
4161 WCHAR packagefile[MAX_PATH];
4164 r = msi_get_local_package_name( packagefile );
4165 if (r != ERROR_SUCCESS)
4168 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
4170 r = CopyFileW( package->db->path, packagefile, FALSE);
4174 ERR("Unable to copy package (%s -> %s) (error %d)\n",
4175 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4176 return ERROR_FUNCTION_FAILED;
4179 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4181 return ERROR_SUCCESS;
4184 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4186 LPWSTR prop, val, key;
4192 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4193 static const WCHAR szWindowsInstaller[] =
4194 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4195 static const WCHAR modpath_fmt[] =
4196 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4197 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4198 static const WCHAR szModifyPath[] =
4199 {'M','o','d','i','f','y','P','a','t','h',0};
4200 static const WCHAR szUninstallString[] =
4201 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4202 static const WCHAR szEstimatedSize[] =
4203 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4204 static const WCHAR szProductLanguage[] =
4205 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4206 static const WCHAR szProductVersion[] =
4207 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4208 static const WCHAR szProductName[] =
4209 {'P','r','o','d','u','c','t','N','a','m','e',0};
4210 static const WCHAR szDisplayName[] =
4211 {'D','i','s','p','l','a','y','N','a','m','e',0};
4212 static const WCHAR szDisplayVersion[] =
4213 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4214 static const WCHAR szManufacturer[] =
4215 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4217 static const LPCSTR propval[] = {
4218 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4219 "ARPCONTACT", "Contact",
4220 "ARPCOMMENTS", "Comments",
4221 "ProductName", "DisplayName",
4222 "ProductVersion", "DisplayVersion",
4223 "ARPHELPLINK", "HelpLink",
4224 "ARPHELPTELEPHONE", "HelpTelephone",
4225 "ARPINSTALLLOCATION", "InstallLocation",
4226 "SourceDir", "InstallSource",
4227 "Manufacturer", "Publisher",
4228 "ARPREADME", "Readme",
4230 "ARPURLINFOABOUT", "URLInfoAbout",
4231 "ARPURLUPDATEINFO", "URLUpdateInfo",
4234 const LPCSTR *p = propval;
4238 prop = strdupAtoW(*p++);
4239 key = strdupAtoW(*p++);
4240 val = msi_dup_property(package, prop);
4241 msi_reg_set_val_str(hkey, key, val);
4247 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4249 size = deformat_string(package, modpath_fmt, &buffer);
4250 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4251 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4254 /* FIXME: Write real Estimated Size when we have it */
4255 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4257 buffer = msi_dup_property(package, szProductName);
4258 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4261 buffer = msi_dup_property(package, cszSourceDir);
4262 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4265 buffer = msi_dup_property(package, szManufacturer);
4266 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4269 GetLocalTime(&systime);
4270 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4271 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4273 langid = msi_get_property_int(package, szProductLanguage, 0);
4274 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4276 buffer = msi_dup_property(package, szProductVersion);
4277 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4280 DWORD verdword = msi_version_str_to_dword(buffer);
4282 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4283 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4284 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4288 return ERROR_SUCCESS;
4291 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4293 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4294 LPWSTR upgrade_code;
4299 static const WCHAR szUpgradeCode[] = {
4300 'U','p','g','r','a','d','e','C','o','d','e',0};
4302 /* FIXME: also need to publish if the product is in advertise mode */
4303 if (!msi_check_publish(package))
4304 return ERROR_SUCCESS;
4306 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4307 if (rc != ERROR_SUCCESS)
4310 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4311 NULL, &props, TRUE);
4312 if (rc != ERROR_SUCCESS)
4315 msi_make_package_local(package, props);
4317 rc = msi_publish_install_properties(package, hkey);
4318 if (rc != ERROR_SUCCESS)
4321 rc = msi_publish_install_properties(package, props);
4322 if (rc != ERROR_SUCCESS)
4325 upgrade_code = msi_dup_property(package, szUpgradeCode);
4328 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4329 squash_guid(package->ProductCode, squashed_pc);
4330 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4331 RegCloseKey(upgrade);
4332 msi_free(upgrade_code);
4338 return ERROR_SUCCESS;
4341 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4343 return execute_script(package,INSTALL_SCRIPT);
4346 static UINT msi_unpublish_product(MSIPACKAGE *package)
4349 LPWSTR remove = NULL;
4350 LPWSTR *features = NULL;
4351 BOOL full_uninstall = TRUE;
4352 MSIFEATURE *feature;
4354 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4355 static const WCHAR szAll[] = {'A','L','L',0};
4356 static const WCHAR szUpgradeCode[] =
4357 {'U','p','g','r','a','d','e','C','o','d','e',0};
4359 remove = msi_dup_property(package, szRemove);
4361 return ERROR_SUCCESS;
4363 features = msi_split_string(remove, ',');
4367 ERR("REMOVE feature list is empty!\n");
4368 return ERROR_FUNCTION_FAILED;
4371 if (!lstrcmpW(features[0], szAll))
4372 full_uninstall = TRUE;
4375 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4377 if (feature->Action != INSTALLSTATE_ABSENT)
4378 full_uninstall = FALSE;
4382 if (!full_uninstall)
4385 MSIREG_DeleteProductKey(package->ProductCode);
4386 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4387 MSIREG_DeleteUninstallKey(package->ProductCode);
4389 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4391 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4392 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4396 MSIREG_DeleteUserProductKey(package->ProductCode);
4397 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4400 upgrade = msi_dup_property(package, szUpgradeCode);
4403 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4410 return ERROR_SUCCESS;
4413 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4417 rc = msi_unpublish_product(package);
4418 if (rc != ERROR_SUCCESS)
4421 /* turn off scheduling */
4422 package->script->CurrentlyScripting= FALSE;
4424 /* first do the same as an InstallExecute */
4425 rc = ACTION_InstallExecute(package);
4426 if (rc != ERROR_SUCCESS)
4429 /* then handle Commit Actions */
4430 rc = execute_script(package,COMMIT_SCRIPT);
4435 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4437 static const WCHAR RunOnce[] = {
4438 'S','o','f','t','w','a','r','e','\\',
4439 'M','i','c','r','o','s','o','f','t','\\',
4440 'W','i','n','d','o','w','s','\\',
4441 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4442 'R','u','n','O','n','c','e',0};
4443 static const WCHAR InstallRunOnce[] = {
4444 'S','o','f','t','w','a','r','e','\\',
4445 'M','i','c','r','o','s','o','f','t','\\',
4446 'W','i','n','d','o','w','s','\\',
4447 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4448 'I','n','s','t','a','l','l','e','r','\\',
4449 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4451 static const WCHAR msiexec_fmt[] = {
4453 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4454 '\"','%','s','\"',0};
4455 static const WCHAR install_fmt[] = {
4456 '/','I',' ','\"','%','s','\"',' ',
4457 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4458 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4459 WCHAR buffer[256], sysdir[MAX_PATH];
4461 WCHAR squished_pc[100];
4463 squash_guid(package->ProductCode,squished_pc);
4465 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4466 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4467 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4470 msi_reg_set_val_str( hkey, squished_pc, buffer );
4473 TRACE("Reboot command %s\n",debugstr_w(buffer));
4475 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4476 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4478 msi_reg_set_val_str( hkey, squished_pc, buffer );
4481 return ERROR_INSTALL_SUSPEND;
4484 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4490 * We are currently doing what should be done here in the top level Install
4491 * however for Administrative and uninstalls this step will be needed
4493 if (!package->PackagePath)
4494 return ERROR_SUCCESS;
4496 msi_set_sourcedir_props(package, TRUE);
4498 attrib = GetFileAttributesW(package->db->path);
4499 if (attrib == INVALID_FILE_ATTRIBUTES)
4505 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4506 package->Context, MSICODE_PRODUCT,
4507 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4508 if (rc == ERROR_MORE_DATA)
4510 prompt = msi_alloc(size * sizeof(WCHAR));
4511 MsiSourceListGetInfoW(package->ProductCode, NULL,
4512 package->Context, MSICODE_PRODUCT,
4513 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4516 prompt = strdupW(package->db->path);
4518 msg = generate_error_string(package,1302,1,prompt);
4519 while(attrib == INVALID_FILE_ATTRIBUTES)
4521 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4524 rc = ERROR_INSTALL_USEREXIT;
4527 attrib = GetFileAttributesW(package->db->path);
4533 return ERROR_SUCCESS;
4538 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4545 static const WCHAR szPropKeys[][80] =
4547 {'P','r','o','d','u','c','t','I','D',0},
4548 {'U','S','E','R','N','A','M','E',0},
4549 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4553 static const WCHAR szRegKeys[][80] =
4555 {'P','r','o','d','u','c','t','I','D',0},
4556 {'R','e','g','O','w','n','e','r',0},
4557 {'R','e','g','C','o','m','p','a','n','y',0},
4561 if (msi_check_unpublish(package))
4563 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4564 return ERROR_SUCCESS;
4567 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4569 return ERROR_SUCCESS;
4571 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4573 if (rc != ERROR_SUCCESS)
4576 for( i = 0; szPropKeys[i][0]; i++ )
4578 buffer = msi_dup_property( package, szPropKeys[i] );
4579 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4584 msi_free(productid);
4587 /* FIXME: call ui_actiondata */
4593 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4597 package->script->InWhatSequence |= SEQUENCE_EXEC;
4598 rc = ACTION_ProcessExecSequence(package,FALSE);
4603 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4605 MSIPACKAGE *package = param;
4606 LPCWSTR compgroupid=NULL;
4607 LPCWSTR feature=NULL;
4608 LPCWSTR text = NULL;
4609 LPCWSTR qualifier = NULL;
4610 LPCWSTR component = NULL;
4611 LPWSTR advertise = NULL;
4612 LPWSTR output = NULL;
4614 UINT rc = ERROR_SUCCESS;
4619 component = MSI_RecordGetString(rec,3);
4620 comp = get_loaded_component(package,component);
4622 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4623 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4624 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4626 TRACE("Skipping: Component %s not scheduled for install\n",
4627 debugstr_w(component));
4629 return ERROR_SUCCESS;
4632 compgroupid = MSI_RecordGetString(rec,1);
4633 qualifier = MSI_RecordGetString(rec,2);
4635 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4636 if (rc != ERROR_SUCCESS)
4639 text = MSI_RecordGetString(rec,4);
4640 feature = MSI_RecordGetString(rec,5);
4642 advertise = create_component_advertise_string(package, comp, feature);
4644 sz = strlenW(advertise);
4647 sz += lstrlenW(text);
4650 sz *= sizeof(WCHAR);
4652 output = msi_alloc_zero(sz);
4653 strcpyW(output,advertise);
4654 msi_free(advertise);
4657 strcatW(output,text);
4659 msi_reg_set_val_multi_str( hkey, qualifier, output );
4666 uirow = MSI_CreateRecord( 2 );
4667 MSI_RecordSetStringW( uirow, 1, compgroupid );
4668 MSI_RecordSetStringW( uirow, 2, qualifier);
4669 ui_actiondata( package, szPublishComponents, uirow);
4670 msiobj_release( &uirow->hdr );
4671 /* FIXME: call ui_progress? */
4677 * At present I am ignorning the advertised components part of this and only
4678 * focusing on the qualified component sets
4680 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4684 static const WCHAR ExecSeqQuery[] =
4685 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4686 '`','P','u','b','l','i','s','h',
4687 'C','o','m','p','o','n','e','n','t','`',0};
4689 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4690 if (rc != ERROR_SUCCESS)
4691 return ERROR_SUCCESS;
4693 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4694 msiobj_release(&view->hdr);
4699 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4701 MSIPACKAGE *package = param;
4704 SC_HANDLE hscm, service = NULL;
4705 LPCWSTR comp, depends, pass;
4706 LPWSTR name = NULL, disp = NULL;
4707 LPCWSTR load_order, serv_name, key;
4708 DWORD serv_type, start_type;
4711 static const WCHAR query[] =
4712 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4713 '`','C','o','m','p','o','n','e','n','t','`',' ',
4714 'W','H','E','R','E',' ',
4715 '`','C','o','m','p','o','n','e','n','t','`',' ',
4716 '=','\'','%','s','\'',0};
4718 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4721 ERR("Failed to open the SC Manager!\n");
4725 start_type = MSI_RecordGetInteger(rec, 5);
4726 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4729 depends = MSI_RecordGetString(rec, 8);
4730 if (depends && *depends)
4731 FIXME("Dependency list unhandled!\n");
4733 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4734 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4735 serv_type = MSI_RecordGetInteger(rec, 4);
4736 err_control = MSI_RecordGetInteger(rec, 6);
4737 load_order = MSI_RecordGetString(rec, 7);
4738 serv_name = MSI_RecordGetString(rec, 9);
4739 pass = MSI_RecordGetString(rec, 10);
4740 comp = MSI_RecordGetString(rec, 12);
4742 /* fetch the service path */
4743 row = MSI_QueryGetRecord(package->db, query, comp);
4746 ERR("Control query failed!\n");
4750 key = MSI_RecordGetString(row, 6);
4752 file = get_loaded_file(package, key);
4753 msiobj_release(&row->hdr);
4756 ERR("Failed to load the service file\n");
4760 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4761 start_type, err_control, file->TargetPath,
4762 load_order, NULL, NULL, serv_name, pass);
4765 if (GetLastError() != ERROR_SERVICE_EXISTS)
4766 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4770 CloseServiceHandle(service);
4771 CloseServiceHandle(hscm);
4775 return ERROR_SUCCESS;
4778 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4782 static const WCHAR ExecSeqQuery[] =
4783 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4784 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4786 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4787 if (rc != ERROR_SUCCESS)
4788 return ERROR_SUCCESS;
4790 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4791 msiobj_release(&view->hdr);
4796 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4797 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4799 LPCWSTR *vector, *temp_vector;
4803 static const WCHAR separator[] = {'[','~',']',0};
4806 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4811 vector = msi_alloc(sizeof(LPWSTR));
4819 vector[*numargs - 1] = p;
4821 if ((q = strstrW(p, separator)))
4825 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4831 vector = temp_vector;
4840 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4842 MSIPACKAGE *package = param;
4844 SC_HANDLE scm, service = NULL;
4845 LPCWSTR name, *vector = NULL;
4847 DWORD event, numargs;
4848 UINT r = ERROR_FUNCTION_FAILED;
4850 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4851 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4852 return ERROR_SUCCESS;
4854 name = MSI_RecordGetString(rec, 2);
4855 event = MSI_RecordGetInteger(rec, 3);
4856 args = strdupW(MSI_RecordGetString(rec, 4));
4858 if (!(event & msidbServiceControlEventStart))
4859 return ERROR_SUCCESS;
4861 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4864 ERR("Failed to open the service control manager\n");
4868 service = OpenServiceW(scm, name, SERVICE_START);
4871 ERR("Failed to open service %s\n", debugstr_w(name));
4875 vector = msi_service_args_to_vector(args, &numargs);
4877 if (!StartServiceW(service, numargs, vector))
4879 ERR("Failed to start service %s\n", debugstr_w(name));
4886 CloseServiceHandle(service);
4887 CloseServiceHandle(scm);
4894 static UINT ACTION_StartServices( MSIPACKAGE *package )
4899 static const WCHAR query[] = {
4900 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4901 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4903 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4904 if (rc != ERROR_SUCCESS)
4905 return ERROR_SUCCESS;
4907 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4908 msiobj_release(&view->hdr);
4913 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4915 DWORD i, needed, count;
4916 ENUM_SERVICE_STATUSW *dependencies;
4920 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4921 0, &needed, &count))
4924 if (GetLastError() != ERROR_MORE_DATA)
4927 dependencies = msi_alloc(needed);
4931 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4932 needed, &needed, &count))
4935 for (i = 0; i < count; i++)
4937 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4938 SERVICE_STOP | SERVICE_QUERY_STATUS);
4942 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4949 msi_free(dependencies);
4953 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4955 MSIPACKAGE *package = param;
4957 SERVICE_STATUS status;
4958 SERVICE_STATUS_PROCESS ssp;
4959 SC_HANDLE scm = NULL, service = NULL;
4961 DWORD event, needed;
4963 event = MSI_RecordGetInteger(rec, 3);
4964 if (!(event & msidbServiceControlEventStop))
4965 return ERROR_SUCCESS;
4967 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4968 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4969 return ERROR_SUCCESS;
4971 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4972 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4973 args = strdupW(MSI_RecordGetString(rec, 4));
4975 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4978 WARN("Failed to open the SCM: %d\n", GetLastError());
4982 service = OpenServiceW(scm, name,
4984 SERVICE_QUERY_STATUS |
4985 SERVICE_ENUMERATE_DEPENDENTS);
4988 WARN("Failed to open service (%s): %d\n",
4989 debugstr_w(name), GetLastError());
4993 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4994 sizeof(SERVICE_STATUS_PROCESS), &needed))
4996 WARN("Failed to query service status (%s): %d\n",
4997 debugstr_w(name), GetLastError());
5001 if (ssp.dwCurrentState == SERVICE_STOPPED)
5004 stop_service_dependents(scm, service);
5006 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5007 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5010 CloseServiceHandle(service);
5011 CloseServiceHandle(scm);
5015 return ERROR_SUCCESS;
5018 static UINT ACTION_StopServices( MSIPACKAGE *package )
5023 static const WCHAR query[] = {
5024 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5025 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5027 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5028 if (rc != ERROR_SUCCESS)
5029 return ERROR_SUCCESS;
5031 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5032 msiobj_release(&view->hdr);
5037 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
5041 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
5043 if (!lstrcmpW(file->File, filename))
5050 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5052 MSIPACKAGE *package = param;
5053 LPWSTR driver, driver_path, ptr;
5054 WCHAR outpath[MAX_PATH];
5055 MSIFILE *driver_file, *setup_file;
5058 UINT r = ERROR_SUCCESS;
5060 static const WCHAR driver_fmt[] = {
5061 'D','r','i','v','e','r','=','%','s',0};
5062 static const WCHAR setup_fmt[] = {
5063 'S','e','t','u','p','=','%','s',0};
5064 static const WCHAR usage_fmt[] = {
5065 'F','i','l','e','U','s','a','g','e','=','1',0};
5067 desc = MSI_RecordGetString(rec, 3);
5069 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5070 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5072 if (!driver_file || !setup_file)
5074 ERR("ODBC Driver entry not found!\n");
5075 return ERROR_FUNCTION_FAILED;
5078 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
5079 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
5080 lstrlenW(usage_fmt) + 1;
5081 driver = msi_alloc(len * sizeof(WCHAR));
5083 return ERROR_OUTOFMEMORY;
5086 lstrcpyW(ptr, desc);
5087 ptr += lstrlenW(ptr) + 1;
5089 sprintfW(ptr, driver_fmt, driver_file->FileName);
5090 ptr += lstrlenW(ptr) + 1;
5092 sprintfW(ptr, setup_fmt, setup_file->FileName);
5093 ptr += lstrlenW(ptr) + 1;
5095 lstrcpyW(ptr, usage_fmt);
5096 ptr += lstrlenW(ptr) + 1;
5099 driver_path = strdupW(driver_file->TargetPath);
5100 ptr = strrchrW(driver_path, '\\');
5101 if (ptr) *ptr = '\0';
5103 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5104 NULL, ODBC_INSTALL_COMPLETE, &usage))
5106 ERR("Failed to install SQL driver!\n");
5107 r = ERROR_FUNCTION_FAILED;
5111 msi_free(driver_path);
5116 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5118 MSIPACKAGE *package = param;
5119 LPWSTR translator, translator_path, ptr;
5120 WCHAR outpath[MAX_PATH];
5121 MSIFILE *translator_file, *setup_file;
5124 UINT r = ERROR_SUCCESS;
5126 static const WCHAR translator_fmt[] = {
5127 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5128 static const WCHAR setup_fmt[] = {
5129 'S','e','t','u','p','=','%','s',0};
5131 desc = MSI_RecordGetString(rec, 3);
5133 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5134 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5136 if (!translator_file || !setup_file)
5138 ERR("ODBC Translator entry not found!\n");
5139 return ERROR_FUNCTION_FAILED;
5142 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
5143 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
5144 translator = msi_alloc(len * sizeof(WCHAR));
5146 return ERROR_OUTOFMEMORY;
5149 lstrcpyW(ptr, desc);
5150 ptr += lstrlenW(ptr) + 1;
5152 sprintfW(ptr, translator_fmt, translator_file->FileName);
5153 ptr += lstrlenW(ptr) + 1;
5155 sprintfW(ptr, setup_fmt, setup_file->FileName);
5156 ptr += lstrlenW(ptr) + 1;
5159 translator_path = strdupW(translator_file->TargetPath);
5160 ptr = strrchrW(translator_path, '\\');
5161 if (ptr) *ptr = '\0';
5163 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5164 NULL, ODBC_INSTALL_COMPLETE, &usage))
5166 ERR("Failed to install SQL translator!\n");
5167 r = ERROR_FUNCTION_FAILED;
5170 msi_free(translator);
5171 msi_free(translator_path);
5176 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5179 LPCWSTR desc, driver;
5180 WORD request = ODBC_ADD_SYS_DSN;
5183 UINT r = ERROR_SUCCESS;
5185 static const WCHAR attrs_fmt[] = {
5186 'D','S','N','=','%','s',0 };
5188 desc = MSI_RecordGetString(rec, 3);
5189 driver = MSI_RecordGetString(rec, 4);
5190 registration = MSI_RecordGetInteger(rec, 5);
5192 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5193 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5195 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5196 attrs = msi_alloc(len * sizeof(WCHAR));
5198 return ERROR_OUTOFMEMORY;
5200 sprintfW(attrs, attrs_fmt, desc);
5201 attrs[len - 1] = '\0';
5203 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5205 ERR("Failed to install SQL data source!\n");
5206 r = ERROR_FUNCTION_FAILED;
5214 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5219 static const WCHAR driver_query[] = {
5220 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5221 'O','D','B','C','D','r','i','v','e','r',0 };
5223 static const WCHAR translator_query[] = {
5224 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5225 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5227 static const WCHAR source_query[] = {
5228 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5229 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5231 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5232 if (rc != ERROR_SUCCESS)
5233 return ERROR_SUCCESS;
5235 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5236 msiobj_release(&view->hdr);
5238 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5239 if (rc != ERROR_SUCCESS)
5240 return ERROR_SUCCESS;
5242 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5243 msiobj_release(&view->hdr);
5245 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5246 if (rc != ERROR_SUCCESS)
5247 return ERROR_SUCCESS;
5249 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5250 msiobj_release(&view->hdr);
5255 #define ENV_ACT_SETALWAYS 0x1
5256 #define ENV_ACT_SETABSENT 0x2
5257 #define ENV_ACT_REMOVE 0x4
5258 #define ENV_ACT_REMOVEMATCH 0x8
5260 #define ENV_MOD_MACHINE 0x20000000
5261 #define ENV_MOD_APPEND 0x40000000
5262 #define ENV_MOD_PREFIX 0x80000000
5263 #define ENV_MOD_MASK 0xC0000000
5265 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5267 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5269 LPCWSTR cptr = *name;
5270 LPCWSTR ptr = *value;
5272 static const WCHAR prefix[] = {'[','~',']',0};
5273 static const int prefix_len = 3;
5279 *flags |= ENV_ACT_SETALWAYS;
5280 else if (*cptr == '+')
5281 *flags |= ENV_ACT_SETABSENT;
5282 else if (*cptr == '-')
5283 *flags |= ENV_ACT_REMOVE;
5284 else if (*cptr == '!')
5285 *flags |= ENV_ACT_REMOVEMATCH;
5286 else if (*cptr == '*')
5287 *flags |= ENV_MOD_MACHINE;
5297 ERR("Missing environment variable\n");
5298 return ERROR_FUNCTION_FAILED;
5301 if (!strncmpW(ptr, prefix, prefix_len))
5303 *flags |= ENV_MOD_APPEND;
5304 *value += lstrlenW(prefix);
5306 else if (lstrlenW(*value) >= prefix_len)
5308 ptr += lstrlenW(ptr) - prefix_len;
5309 if (!lstrcmpW(ptr, prefix))
5311 *flags |= ENV_MOD_PREFIX;
5312 /* the "[~]" will be removed by deformat_string */;
5316 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5317 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5318 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5319 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5321 ERR("Invalid flags: %08x\n", *flags);
5322 return ERROR_FUNCTION_FAILED;
5326 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
5328 return ERROR_SUCCESS;
5331 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5333 MSIPACKAGE *package = param;
5334 LPCWSTR name, value;
5335 LPWSTR data = NULL, newval = NULL;
5336 LPWSTR deformatted = NULL, ptr;
5337 DWORD flags, type, size;
5339 HKEY env = NULL, root;
5340 LPCWSTR environment;
5342 static const WCHAR user_env[] =
5343 {'E','n','v','i','r','o','n','m','e','n','t',0};
5344 static const WCHAR machine_env[] =
5345 {'S','y','s','t','e','m','\\',
5346 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5347 'C','o','n','t','r','o','l','\\',
5348 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5349 'E','n','v','i','r','o','n','m','e','n','t',0};
5350 static const WCHAR semicolon[] = {';',0};
5352 name = MSI_RecordGetString(rec, 2);
5353 value = MSI_RecordGetString(rec, 3);
5355 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
5357 res = env_set_flags(&name, &value, &flags);
5358 if (res != ERROR_SUCCESS)
5361 deformat_string(package, value, &deformatted);
5364 res = ERROR_OUTOFMEMORY;
5368 value = deformatted;
5370 if (flags & ENV_MOD_MACHINE)
5372 environment = machine_env;
5373 root = HKEY_LOCAL_MACHINE;
5377 environment = user_env;
5378 root = HKEY_CURRENT_USER;
5381 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5382 KEY_ALL_ACCESS, NULL, &env, NULL);
5383 if (res != ERROR_SUCCESS)
5386 if (flags & ENV_ACT_REMOVE)
5387 FIXME("Not removing environment variable on uninstall!\n");
5390 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5391 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5392 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5395 if (res != ERROR_FILE_NOT_FOUND)
5397 if (flags & ENV_ACT_SETABSENT)
5399 res = ERROR_SUCCESS;
5403 data = msi_alloc(size);
5407 return ERROR_OUTOFMEMORY;
5410 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5411 if (res != ERROR_SUCCESS)
5414 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5416 res = RegDeleteKeyW(env, name);
5420 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5421 newval = msi_alloc(size);
5425 res = ERROR_OUTOFMEMORY;
5429 if (!(flags & ENV_MOD_MASK))
5430 lstrcpyW(newval, value);
5433 if (flags & ENV_MOD_PREFIX)
5435 lstrcpyW(newval, value);
5436 lstrcatW(newval, semicolon);
5437 ptr = newval + lstrlenW(value) + 1;
5440 lstrcpyW(ptr, data);
5442 if (flags & ENV_MOD_APPEND)
5444 lstrcatW(newval, semicolon);
5445 lstrcatW(newval, value);
5451 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5452 newval = msi_alloc(size);
5455 res = ERROR_OUTOFMEMORY;
5459 lstrcpyW(newval, value);
5462 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5463 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5466 if (env) RegCloseKey(env);
5467 msi_free(deformatted);
5473 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5477 static const WCHAR ExecSeqQuery[] =
5478 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5479 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5480 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5481 if (rc != ERROR_SUCCESS)
5482 return ERROR_SUCCESS;
5484 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5485 msiobj_release(&view->hdr);
5490 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5501 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5505 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5506 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5508 WARN("Source or dest is directory, not moving\n");
5512 if (options == msidbMoveFileOptionsMove)
5514 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5515 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5518 WARN("MoveFile failed: %d\n", GetLastError());
5524 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5525 ret = CopyFileW(source, dest, FALSE);
5528 WARN("CopyFile failed: %d\n", GetLastError());
5536 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5539 DWORD dirlen, pathlen;
5541 ptr = strrchrW(wildcard, '\\');
5542 dirlen = ptr - wildcard + 1;
5544 pathlen = dirlen + lstrlenW(filename) + 1;
5545 path = msi_alloc(pathlen * sizeof(WCHAR));
5547 lstrcpynW(path, wildcard, dirlen + 1);
5548 lstrcatW(path, filename);
5553 static void free_file_entry(FILE_LIST *file)
5555 msi_free(file->source);
5556 msi_free(file->dest);
5560 static void free_list(FILE_LIST *list)
5562 while (!list_empty(&list->entry))
5564 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5566 list_remove(&file->entry);
5567 free_file_entry(file);
5571 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5573 FILE_LIST *new, *file;
5574 LPWSTR ptr, filename;
5577 new = msi_alloc_zero(sizeof(FILE_LIST));
5581 new->source = strdupW(source);
5582 ptr = strrchrW(dest, '\\') + 1;
5583 filename = strrchrW(new->source, '\\') + 1;
5585 new->sourcename = filename;
5588 new->destname = ptr;
5590 new->destname = new->sourcename;
5592 size = (ptr - dest) + lstrlenW(filename) + 1;
5593 new->dest = msi_alloc(size * sizeof(WCHAR));
5596 free_file_entry(new);
5600 lstrcpynW(new->dest, dest, ptr - dest + 1);
5601 lstrcatW(new->dest, filename);
5603 if (list_empty(&files->entry))
5605 list_add_head(&files->entry, &new->entry);
5609 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5611 if (lstrcmpW(source, file->source) < 0)
5613 list_add_before(&file->entry, &new->entry);
5618 list_add_after(&file->entry, &new->entry);
5622 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5624 WIN32_FIND_DATAW wfd;
5628 FILE_LIST files, *file;
5631 hfile = FindFirstFileW(source, &wfd);
5632 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5634 list_init(&files.entry);
5636 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5638 if (is_dot_dir(wfd.cFileName)) continue;
5640 path = wildcard_to_file(source, wfd.cFileName);
5647 add_wildcard(&files, path, dest);
5651 /* no files match the wildcard */
5652 if (list_empty(&files.entry))
5655 /* only the first wildcard match gets renamed to dest */
5656 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5657 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5658 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5665 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5667 while (!list_empty(&files.entry))
5669 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5671 msi_move_file(file->source, file->dest, options);
5673 list_remove(&file->entry);
5674 free_file_entry(file);
5685 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5687 MSIPACKAGE *package = param;
5690 LPWSTR destname = NULL;
5691 LPWSTR sourcedir = NULL, destdir = NULL;
5692 LPWSTR source = NULL, dest = NULL;
5695 BOOL ret, wildcards;
5697 static const WCHAR backslash[] = {'\\',0};
5699 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5700 if (!comp || !comp->Enabled ||
5701 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5703 TRACE("Component not set for install, not moving file\n");
5704 return ERROR_SUCCESS;
5707 sourcename = MSI_RecordGetString(rec, 3);
5708 options = MSI_RecordGetInteger(rec, 7);
5710 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5714 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5720 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5723 source = strdupW(sourcedir);
5729 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5730 source = msi_alloc(size * sizeof(WCHAR));
5734 lstrcpyW(source, sourcedir);
5735 if (source[lstrlenW(source) - 1] != '\\')
5736 lstrcatW(source, backslash);
5737 lstrcatW(source, sourcename);
5740 wildcards = strchrW(source, '*') || strchrW(source, '?');
5742 if (MSI_RecordIsNull(rec, 4))
5746 destname = strdupW(sourcename);
5753 destname = strdupW(MSI_RecordGetString(rec, 4));
5755 reduce_to_longfilename(destname);
5760 size = lstrlenW(destname);
5762 size += lstrlenW(destdir) + 2;
5763 dest = msi_alloc(size * sizeof(WCHAR));
5767 lstrcpyW(dest, destdir);
5768 if (dest[lstrlenW(dest) - 1] != '\\')
5769 lstrcatW(dest, backslash);
5772 lstrcatW(dest, destname);
5774 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5776 ret = CreateDirectoryW(destdir, NULL);
5779 WARN("CreateDirectory failed: %d\n", GetLastError());
5780 return ERROR_SUCCESS;
5785 msi_move_file(source, dest, options);
5787 move_files_wildcard(source, dest, options);
5790 msi_free(sourcedir);
5796 return ERROR_SUCCESS;
5799 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5804 static const WCHAR ExecSeqQuery[] =
5805 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5806 '`','M','o','v','e','F','i','l','e','`',0};
5808 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5809 if (rc != ERROR_SUCCESS)
5810 return ERROR_SUCCESS;
5812 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5813 msiobj_release(&view->hdr);
5818 typedef struct tagMSIASSEMBLY
5821 MSICOMPONENT *component;
5822 MSIFEATURE *feature;
5830 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5832 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5833 LPVOID pvReserved, HMODULE *phModDll);
5835 static BOOL init_functionpointers(void)
5841 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5843 hmscoree = LoadLibraryA("mscoree.dll");
5846 WARN("mscoree.dll not available\n");
5850 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5851 if (!pLoadLibraryShim)
5853 WARN("LoadLibraryShim not available\n");
5854 FreeLibrary(hmscoree);
5858 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5861 WARN("fusion.dll not available\n");
5862 FreeLibrary(hmscoree);
5866 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5868 FreeLibrary(hmscoree);
5872 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5875 IAssemblyCache *cache;
5877 UINT r = ERROR_FUNCTION_FAILED;
5879 TRACE("installing assembly: %s\n", debugstr_w(path));
5881 if (assembly->feature)
5882 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5884 if (assembly->manifest)
5885 FIXME("Manifest unhandled\n");
5887 if (assembly->application)
5889 FIXME("Assembly should be privately installed\n");
5890 return ERROR_SUCCESS;
5893 if (assembly->attributes == msidbAssemblyAttributesWin32)
5895 FIXME("Win32 assemblies not handled\n");
5896 return ERROR_SUCCESS;
5899 hr = pCreateAssemblyCache(&cache, 0);
5903 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5905 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5910 IAssemblyCache_Release(cache);
5914 typedef struct tagASSEMBLY_LIST
5916 MSIPACKAGE *package;
5917 IAssemblyCache *cache;
5918 struct list *assemblies;
5921 typedef struct tagASSEMBLY_NAME
5929 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
5931 ASSEMBLY_NAME *asmname = param;
5932 LPCWSTR name = MSI_RecordGetString(rec, 2);
5933 LPWSTR val = msi_dup_record_field(rec, 3);
5935 static const WCHAR Name[] = {'N','a','m','e',0};
5936 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
5937 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
5938 static const WCHAR PublicKeyToken[] = {
5939 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
5941 if (!strcmpiW(name, Name))
5942 asmname->name = val;
5943 else if (!strcmpiW(name, Version))
5944 asmname->version = val;
5945 else if (!strcmpiW(name, Culture))
5946 asmname->culture = val;
5947 else if (!strcmpiW(name, PublicKeyToken))
5948 asmname->pubkeytoken = val;
5952 return ERROR_SUCCESS;
5955 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
5959 *size = lstrlenW(append) + 1;
5960 *str = msi_alloc((*size) * sizeof(WCHAR));
5961 lstrcpyW(*str, append);
5965 (*size) += lstrlenW(append);
5966 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
5967 lstrcatW(*str, append);
5970 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
5973 ASSEMBLY_INFO asminfo;
5981 static const WCHAR separator[] = {',',' ',0};
5982 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
5983 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
5984 static const WCHAR PublicKeyToken[] = {
5985 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
5986 static const WCHAR query[] = {
5987 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5988 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
5989 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
5990 '=','\'','%','s','\'',0};
5994 ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
5995 ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
5997 r = MSI_OpenQuery(db, &view, query, comp->Component);
5998 if (r != ERROR_SUCCESS)
5999 return ERROR_SUCCESS;
6001 MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
6002 msiobj_release(&view->hdr);
6006 ERR("No assembly name specified!\n");
6010 append_str(&disp, &size, name.name);
6014 append_str(&disp, &size, separator);
6015 append_str(&disp, &size, Version);
6016 append_str(&disp, &size, name.version);
6021 append_str(&disp, &size, separator);
6022 append_str(&disp, &size, Culture);
6023 append_str(&disp, &size, name.culture);
6026 if (name.pubkeytoken)
6028 append_str(&disp, &size, separator);
6029 append_str(&disp, &size, PublicKeyToken);
6030 append_str(&disp, &size, name.pubkeytoken);
6033 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6034 IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
6036 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6040 msi_free(name.name);
6041 msi_free(name.version);
6042 msi_free(name.culture);
6043 msi_free(name.pubkeytoken);
6048 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6050 ASSEMBLY_LIST *list = param;
6051 MSIASSEMBLY *assembly;
6053 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6055 return ERROR_OUTOFMEMORY;
6057 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
6059 if (!assembly->component || !assembly->component->Enabled ||
6060 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
6062 TRACE("Component not set for install, not publishing assembly\n");
6064 return ERROR_SUCCESS;
6067 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6068 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6070 if (!assembly->file)
6072 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6073 return ERROR_FUNCTION_FAILED;
6076 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6077 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6078 assembly->attributes = MSI_RecordGetInteger(rec, 5);
6080 if (assembly->application)
6083 DWORD size = sizeof(version)/sizeof(WCHAR);
6085 /* FIXME: we should probably check the manifest file here */
6087 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6088 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6090 assembly->installed = TRUE;
6094 assembly->installed = check_assembly_installed(list->package->db,
6096 assembly->component);
6098 list_add_head(list->assemblies, &assembly->entry);
6099 return ERROR_SUCCESS;
6102 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6104 IAssemblyCache *cache = NULL;
6110 static const WCHAR query[] =
6111 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6112 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6114 r = MSI_DatabaseOpenViewW(package->db, query, &view);
6115 if (r != ERROR_SUCCESS)
6116 return ERROR_SUCCESS;
6118 hr = pCreateAssemblyCache(&cache, 0);
6120 return ERROR_FUNCTION_FAILED;
6122 list.package = package;
6124 list.assemblies = assemblies;
6126 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6127 msiobj_release(&view->hdr);
6129 IAssemblyCache_Release(cache);
6134 static void free_assemblies(struct list *assemblies)
6136 struct list *item, *cursor;
6138 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6140 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6142 list_remove(&assembly->entry);
6143 msi_free(assembly->application);
6144 msi_free(assembly->manifest);
6149 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6151 MSIASSEMBLY *assembly;
6153 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6155 if (!lstrcmpW(assembly->file->File, file))
6165 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6166 LPWSTR *path, DWORD *attrs, PVOID user)
6168 MSIASSEMBLY *assembly;
6169 WCHAR temppath[MAX_PATH];
6170 struct list *assemblies = user;
6173 if (!find_assembly(assemblies, file, &assembly))
6176 GetTempPathW(MAX_PATH, temppath);
6177 PathAddBackslashW(temppath);
6178 lstrcatW(temppath, assembly->file->FileName);
6180 if (action == MSICABEXTRACT_BEGINEXTRACT)
6182 if (assembly->installed)
6185 *path = strdupW(temppath);
6186 *attrs = assembly->file->Attributes;
6188 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6190 assembly->installed = TRUE;
6192 r = install_assembly(package, assembly, temppath);
6193 if (r != ERROR_SUCCESS)
6194 ERR("Failed to install assembly\n");
6200 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6203 struct list assemblies = LIST_INIT(assemblies);
6204 MSIASSEMBLY *assembly;
6207 if (!init_functionpointers() || !pCreateAssemblyCache)
6208 return ERROR_FUNCTION_FAILED;
6210 r = load_assemblies(package, &assemblies);
6211 if (r != ERROR_SUCCESS)
6214 if (list_empty(&assemblies))
6217 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6220 r = ERROR_OUTOFMEMORY;
6224 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6226 if (assembly->installed && !mi->is_continuous)
6229 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6230 (assembly->file->IsCompressed && !mi->is_extracted))
6234 r = ready_media(package, assembly->file, mi);
6235 if (r != ERROR_SUCCESS)
6237 ERR("Failed to ready media\n");
6242 data.package = package;
6243 data.cb = installassembly_cb;
6244 data.user = &assemblies;
6246 if (assembly->file->IsCompressed &&
6247 !msi_cabextract(package, mi, &data))
6249 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6250 r = ERROR_FUNCTION_FAILED;
6255 if (!assembly->file->IsCompressed)
6257 LPWSTR source = resolve_file_source(package, assembly->file);
6259 r = install_assembly(package, assembly, source);
6260 if (r != ERROR_SUCCESS)
6261 ERR("Failed to install assembly\n");
6266 /* FIXME: write Installer assembly reg values */
6270 free_assemblies(&assemblies);
6274 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6275 LPCSTR action, LPCWSTR table )
6277 static const WCHAR query[] = {
6278 'S','E','L','E','C','T',' ','*',' ',
6279 'F','R','O','M',' ','`','%','s','`',0 };
6280 MSIQUERY *view = NULL;
6284 r = MSI_OpenQuery( package->db, &view, query, table );
6285 if (r == ERROR_SUCCESS)
6287 r = MSI_IterateRecords(view, &count, NULL, package);
6288 msiobj_release(&view->hdr);
6292 FIXME("%s -> %u ignored %s table values\n",
6293 action, count, debugstr_w(table));
6295 return ERROR_SUCCESS;
6298 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6300 TRACE("%p\n", package);
6301 return ERROR_SUCCESS;
6304 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6306 static const WCHAR table[] =
6307 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6308 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6311 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6313 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6314 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6317 static UINT ACTION_BindImage( MSIPACKAGE *package )
6319 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6320 return msi_unimplemented_action_stub( package, "BindImage", table );
6323 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6325 static const WCHAR table[] = {
6326 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6327 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6330 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6332 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6333 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6336 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
6338 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
6339 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
6342 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6344 static const WCHAR table[] = {
6345 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6346 return msi_unimplemented_action_stub( package, "DeleteServices", table );
6348 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6350 static const WCHAR table[] = {
6351 'P','r','o','d','u','c','t','I','D',0 };
6352 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6355 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6357 static const WCHAR table[] = {
6358 'E','n','v','i','r','o','n','m','e','n','t',0 };
6359 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6362 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6364 static const WCHAR table[] = {
6365 'M','s','i','A','s','s','e','m','b','l','y',0 };
6366 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6369 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6371 static const WCHAR table[] = { 'F','o','n','t',0 };
6372 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6375 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6377 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6378 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6381 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6383 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6384 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6387 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6389 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6390 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6393 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6395 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6396 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6399 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6401 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6402 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6405 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6407 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6408 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6411 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
6413 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
6414 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
6417 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6419 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6420 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6423 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6425 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6426 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6429 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6431 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6432 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6435 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6437 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6438 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6441 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6443 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6444 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6447 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6449 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6450 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6453 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6455 static const WCHAR table[] = { 'M','I','M','E',0 };
6456 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6459 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6461 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6462 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6465 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6467 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6468 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6471 static const struct _actions StandardActions[] = {
6472 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6473 { szAppSearch, ACTION_AppSearch },
6474 { szBindImage, ACTION_BindImage },
6475 { szCCPSearch, ACTION_CCPSearch },
6476 { szCostFinalize, ACTION_CostFinalize },
6477 { szCostInitialize, ACTION_CostInitialize },
6478 { szCreateFolders, ACTION_CreateFolders },
6479 { szCreateShortcuts, ACTION_CreateShortcuts },
6480 { szDeleteServices, ACTION_DeleteServices },
6481 { szDisableRollback, NULL },
6482 { szDuplicateFiles, ACTION_DuplicateFiles },
6483 { szExecuteAction, ACTION_ExecuteAction },
6484 { szFileCost, ACTION_FileCost },
6485 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6486 { szForceReboot, ACTION_ForceReboot },
6487 { szInstallAdminPackage, NULL },
6488 { szInstallExecute, ACTION_InstallExecute },
6489 { szInstallExecuteAgain, ACTION_InstallExecute },
6490 { szInstallFiles, ACTION_InstallFiles},
6491 { szInstallFinalize, ACTION_InstallFinalize },
6492 { szInstallInitialize, ACTION_InstallInitialize },
6493 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6494 { szInstallValidate, ACTION_InstallValidate },
6495 { szIsolateComponents, ACTION_IsolateComponents },
6496 { szLaunchConditions, ACTION_LaunchConditions },
6497 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6498 { szMoveFiles, ACTION_MoveFiles },
6499 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6500 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6501 { szInstallODBC, ACTION_InstallODBC },
6502 { szInstallServices, ACTION_InstallServices },
6503 { szPatchFiles, ACTION_PatchFiles },
6504 { szProcessComponents, ACTION_ProcessComponents },
6505 { szPublishComponents, ACTION_PublishComponents },
6506 { szPublishFeatures, ACTION_PublishFeatures },
6507 { szPublishProduct, ACTION_PublishProduct },
6508 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6509 { szRegisterComPlus, ACTION_RegisterComPlus},
6510 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6511 { szRegisterFonts, ACTION_RegisterFonts },
6512 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6513 { szRegisterProduct, ACTION_RegisterProduct },
6514 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6515 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6516 { szRegisterUser, ACTION_RegisterUser },
6517 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6518 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6519 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6520 { szRemoveFiles, ACTION_RemoveFiles },
6521 { szRemoveFolders, ACTION_RemoveFolders },
6522 { szRemoveIniValues, ACTION_RemoveIniValues },
6523 { szRemoveODBC, ACTION_RemoveODBC },
6524 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6525 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6526 { szResolveSource, ACTION_ResolveSource },
6527 { szRMCCPSearch, ACTION_RMCCPSearch },
6528 { szScheduleReboot, NULL },
6529 { szSelfRegModules, ACTION_SelfRegModules },
6530 { szSelfUnregModules, ACTION_SelfUnregModules },
6531 { szSetODBCFolders, NULL },
6532 { szStartServices, ACTION_StartServices },
6533 { szStopServices, ACTION_StopServices },
6534 { szUnpublishComponents, ACTION_UnpublishComponents },
6535 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6536 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6537 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6538 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6539 { szUnregisterFonts, ACTION_UnregisterFonts },
6540 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6541 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6542 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6543 { szValidateProductID, ACTION_ValidateProductID },
6544 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6545 { szWriteIniValues, ACTION_WriteIniValues },
6546 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6550 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6551 UINT* rc, BOOL force )
6557 if (!run && !package->script->CurrentlyScripting)
6562 if (strcmpW(action,szInstallFinalize) == 0 ||
6563 strcmpW(action,szInstallExecute) == 0 ||
6564 strcmpW(action,szInstallExecuteAgain) == 0)
6569 while (StandardActions[i].action != NULL)
6571 if (strcmpW(StandardActions[i].action, action)==0)
6575 ui_actioninfo(package, action, TRUE, 0);
6576 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6577 ui_actioninfo(package, action, FALSE, *rc);
6581 ui_actionstart(package, action);
6582 if (StandardActions[i].handler)
6584 *rc = StandardActions[i].handler(package);
6588 FIXME("unhandled standard action %s\n",debugstr_w(action));
6589 *rc = ERROR_SUCCESS;