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
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
39 #include "wine/debug.h"
44 #include "wine/unicode.h"
47 #define REG_PROGRESS_VALUE 13200
48 #define COMPONENT_PROGRESS_VALUE 24000
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
55 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
56 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
57 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
60 * consts and values used
62 static const WCHAR c_colon[] = {'C',':','\\',0};
64 static const WCHAR szCreateFolders[] =
65 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
66 static const WCHAR szCostFinalize[] =
67 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
68 const WCHAR szInstallFiles[] =
69 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
70 const WCHAR szDuplicateFiles[] =
71 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
72 static const WCHAR szWriteRegistryValues[] =
73 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
74 'V','a','l','u','e','s',0};
75 static const WCHAR szCostInitialize[] =
76 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
77 static const WCHAR szFileCost[] =
78 {'F','i','l','e','C','o','s','t',0};
79 static const WCHAR szInstallInitialize[] =
80 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
81 static const WCHAR szInstallValidate[] =
82 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
83 static const WCHAR szLaunchConditions[] =
84 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
85 static const WCHAR szProcessComponents[] =
86 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
87 static const WCHAR szRegisterTypeLibraries[] =
88 {'R','e','g','i','s','t','e','r','T','y','p','e',
89 'L','i','b','r','a','r','i','e','s',0};
90 const WCHAR szRegisterClassInfo[] =
91 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
92 const WCHAR szRegisterProgIdInfo[] =
93 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
94 static const WCHAR szCreateShortcuts[] =
95 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
96 static const WCHAR szPublishProduct[] =
97 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
98 static const WCHAR szWriteIniValues[] =
99 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
100 static const WCHAR szSelfRegModules[] =
101 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
102 static const WCHAR szPublishFeatures[] =
103 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
104 static const WCHAR szRegisterProduct[] =
105 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
106 static const WCHAR szInstallExecute[] =
107 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
108 static const WCHAR szInstallExecuteAgain[] =
109 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
110 'A','g','a','i','n',0};
111 static const WCHAR szInstallFinalize[] =
112 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
113 static const WCHAR szForceReboot[] =
114 {'F','o','r','c','e','R','e','b','o','o','t',0};
115 static const WCHAR szResolveSource[] =
116 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
117 static const WCHAR szAppSearch[] =
118 {'A','p','p','S','e','a','r','c','h',0};
119 static const WCHAR szAllocateRegistrySpace[] =
120 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
121 'S','p','a','c','e',0};
122 static const WCHAR szBindImage[] =
123 {'B','i','n','d','I','m','a','g','e',0};
124 static const WCHAR szCCPSearch[] =
125 {'C','C','P','S','e','a','r','c','h',0};
126 static const WCHAR szDeleteServices[] =
127 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
128 static const WCHAR szDisableRollback[] =
129 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
130 static const WCHAR szExecuteAction[] =
131 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
132 const WCHAR szFindRelatedProducts[] =
133 {'F','i','n','d','R','e','l','a','t','e','d',
134 'P','r','o','d','u','c','t','s',0};
135 static const WCHAR szInstallAdminPackage[] =
136 {'I','n','s','t','a','l','l','A','d','m','i','n',
137 'P','a','c','k','a','g','e',0};
138 static const WCHAR szInstallSFPCatalogFile[] =
139 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
141 static const WCHAR szIsolateComponents[] =
142 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
143 const WCHAR szMigrateFeatureStates[] =
144 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
145 'S','t','a','t','e','s',0};
146 const WCHAR szMoveFiles[] =
147 {'M','o','v','e','F','i','l','e','s',0};
148 static const WCHAR szMsiPublishAssemblies[] =
149 {'M','s','i','P','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szMsiUnpublishAssemblies[] =
152 {'M','s','i','U','n','p','u','b','l','i','s','h',
153 'A','s','s','e','m','b','l','i','e','s',0};
154 static const WCHAR szInstallODBC[] =
155 {'I','n','s','t','a','l','l','O','D','B','C',0};
156 static const WCHAR szInstallServices[] =
157 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
158 const WCHAR szPatchFiles[] =
159 {'P','a','t','c','h','F','i','l','e','s',0};
160 static const WCHAR szPublishComponents[] =
161 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
162 static const WCHAR szRegisterComPlus[] =
163 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
164 const WCHAR szRegisterExtensionInfo[] =
165 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
167 static const WCHAR szRegisterFonts[] =
168 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
169 const WCHAR szRegisterMIMEInfo[] =
170 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
171 static const WCHAR szRegisterUser[] =
172 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
173 const WCHAR szRemoveDuplicateFiles[] =
174 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
175 'F','i','l','e','s',0};
176 static const WCHAR szRemoveEnvironmentStrings[] =
177 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
178 'S','t','r','i','n','g','s',0};
179 const WCHAR szRemoveExistingProducts[] =
180 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
181 'P','r','o','d','u','c','t','s',0};
182 const WCHAR szRemoveFiles[] =
183 {'R','e','m','o','v','e','F','i','l','e','s',0};
184 static const WCHAR szRemoveFolders[] =
185 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
186 static const WCHAR szRemoveIniValues[] =
187 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
188 static const WCHAR szRemoveODBC[] =
189 {'R','e','m','o','v','e','O','D','B','C',0};
190 static const WCHAR szRemoveRegistryValues[] =
191 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
192 'V','a','l','u','e','s',0};
193 static const WCHAR szRemoveShortcuts[] =
194 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
195 static const WCHAR szRMCCPSearch[] =
196 {'R','M','C','C','P','S','e','a','r','c','h',0};
197 static const WCHAR szScheduleReboot[] =
198 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
199 static const WCHAR szSelfUnregModules[] =
200 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
201 static const WCHAR szSetODBCFolders[] =
202 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
203 static const WCHAR szStartServices[] =
204 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
205 static const WCHAR szStopServices[] =
206 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
207 static const WCHAR szUnpublishComponents[] =
208 {'U','n','p','u','b','l','i','s','h',
209 'C','o','m','p','o','n','e','n','t','s',0};
210 static const WCHAR szUnpublishFeatures[] =
211 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
212 const WCHAR szUnregisterClassInfo[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
215 static const WCHAR szUnregisterComPlus[] =
216 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
217 const WCHAR szUnregisterExtensionInfo[] =
218 {'U','n','r','e','g','i','s','t','e','r',
219 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
220 static const WCHAR szUnregisterFonts[] =
221 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
222 const WCHAR szUnregisterMIMEInfo[] =
223 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
224 const WCHAR szUnregisterProgIdInfo[] =
225 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
227 static const WCHAR szUnregisterTypeLibraries[] =
228 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
229 'L','i','b','r','a','r','i','e','s',0};
230 static const WCHAR szValidateProductID[] =
231 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
232 static const WCHAR szWriteEnvironmentStrings[] =
233 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
234 'S','t','r','i','n','g','s',0};
236 /* action handlers */
237 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
241 STANDARDACTIONHANDLER handler;
244 static const struct _actions StandardActions[];
247 /********************************************************
249 ********************************************************/
251 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
253 static const WCHAR Query_t[] =
254 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
255 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
256 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
257 ' ','\'','%','s','\'',0};
260 row = MSI_QueryGetRecord( package->db, Query_t, action );
263 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
264 msiobj_release(&row->hdr);
267 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
271 static const WCHAR template_s[]=
272 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
274 static const WCHAR template_e[]=
275 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
276 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
278 static const WCHAR format[] =
279 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
283 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
285 sprintfW(message,template_s,timet,action);
287 sprintfW(message,template_e,timet,action,rc);
289 row = MSI_CreateRecord(1);
290 MSI_RecordSetStringW(row,1,message);
292 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
293 msiobj_release(&row->hdr);
296 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
301 LPWSTR prop = NULL, val = NULL;
304 return ERROR_SUCCESS;
316 TRACE("Looking at %s\n",debugstr_w(ptr));
318 ptr2 = strchrW(ptr,'=');
321 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
328 prop = msi_alloc((len+1)*sizeof(WCHAR));
329 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 WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
405 LPWSTR prod_code, patch_product;
408 prod_code = msi_dup_property( package, szProductCode );
409 patch_product = msi_get_suminfo_product( patch );
411 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
413 if ( strstrW( patch_product, prod_code ) )
416 ret = ERROR_FUNCTION_FAILED;
418 msi_free( patch_product );
419 msi_free( prod_code );
424 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
425 MSIDATABASE *patch_db, LPCWSTR name )
427 UINT ret = ERROR_FUNCTION_FAILED;
428 IStorage *stg = NULL;
431 TRACE("%p %s\n", package, debugstr_w(name) );
435 ERR("expected a colon in %s\n", debugstr_w(name));
436 return ERROR_FUNCTION_FAILED;
439 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
442 ret = msi_check_transform_applicable( package, stg );
443 if (ret == ERROR_SUCCESS)
444 msi_table_apply_transform( package->db, stg );
446 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
447 IStorage_Release( stg );
450 ERR("failed to open substorage %s\n", debugstr_w(name));
452 return ERROR_SUCCESS;
455 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
457 static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
458 LPWSTR guid_list, *guids, product_code;
459 UINT i, ret = ERROR_FUNCTION_FAILED;
461 product_code = msi_dup_property( package, szProdCode );
464 /* FIXME: the property ProductCode should be written into the DB somewhere */
465 ERR("no product code to check\n");
466 return ERROR_SUCCESS;
469 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
470 guids = msi_split_string( guid_list, ';' );
471 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
473 if (!lstrcmpW( guids[i], product_code ))
477 msi_free( guid_list );
478 msi_free( product_code );
483 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
486 LPWSTR str, *substorage;
487 UINT i, r = ERROR_SUCCESS;
489 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
491 return ERROR_FUNCTION_FAILED;
493 msi_check_patch_applicable( package, si );
495 /* enumerate the substorage */
496 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
497 substorage = msi_split_string( str, ';' );
498 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
499 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
500 msi_free( substorage );
503 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
505 msiobj_release( &si->hdr );
510 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
512 MSIDATABASE *patch_db = NULL;
515 TRACE("%p %s\n", package, debugstr_w( file ) );
518 * We probably want to make sure we only open a patch collection here.
519 * Patch collections (.msp) and databases (.msi) have different GUIDs
520 * but currently MSI_OpenDatabaseW will accept both.
522 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
523 if ( r != ERROR_SUCCESS )
525 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
529 msi_parse_patch_summary( package, patch_db );
532 * There might be a CAB file in the patch package,
533 * so append it to the list of storage to search for streams.
535 append_storage_to_db( package->db, patch_db->storage );
537 msiobj_release( &patch_db->hdr );
539 return ERROR_SUCCESS;
542 /* get the PATCH property, and apply all the patches it specifies */
543 static UINT msi_apply_patches( MSIPACKAGE *package )
545 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
546 LPWSTR patch_list, *patches;
547 UINT i, r = ERROR_SUCCESS;
549 patch_list = msi_dup_property( package, szPatch );
551 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
553 patches = msi_split_string( patch_list, ';' );
554 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
555 r = msi_apply_patch_package( package, patches[i] );
558 msi_free( patch_list );
563 static UINT msi_apply_transforms( MSIPACKAGE *package )
565 static const WCHAR szTransforms[] = {
566 'T','R','A','N','S','F','O','R','M','S',0 };
567 LPWSTR xform_list, *xforms;
568 UINT i, r = ERROR_SUCCESS;
570 xform_list = msi_dup_property( package, szTransforms );
571 xforms = msi_split_string( xform_list, ';' );
573 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
575 if (xforms[i][0] == ':')
576 r = msi_apply_substorage_transform( package, package->db, &xforms[i][1] );
578 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
582 msi_free( xform_list );
587 BOOL ui_sequence_exists( MSIPACKAGE *package )
592 static const WCHAR ExecSeqQuery [] =
593 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
594 '`','I','n','s','t','a','l','l',
595 'U','I','S','e','q','u','e','n','c','e','`',
596 ' ','W','H','E','R','E',' ',
597 '`','S','e','q','u','e','n','c','e','`',' ',
598 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
599 '`','S','e','q','u','e','n','c','e','`',0};
601 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
602 if (rc == ERROR_SUCCESS)
604 msiobj_release(&view->hdr);
611 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
614 LPWSTR source, check;
617 static const WCHAR szOriginalDatabase[] =
618 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
620 db = msi_dup_property( package, szOriginalDatabase );
622 return ERROR_OUTOFMEMORY;
624 p = strrchrW( db, '\\' );
627 p = strrchrW( db, '/' );
631 return ERROR_SUCCESS;
636 source = msi_alloc( len * sizeof(WCHAR) );
637 lstrcpynW( source, db, len );
639 check = msi_dup_property( package, cszSourceDir );
640 if (!check || replace)
641 MSI_SetPropertyW( package, cszSourceDir, source );
645 check = msi_dup_property( package, cszSOURCEDIR );
646 if (!check || replace)
647 MSI_SetPropertyW( package, cszSOURCEDIR, source );
653 return ERROR_SUCCESS;
656 /****************************************************
657 * TOP level entry points
658 *****************************************************/
660 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
661 LPCWSTR szCommandLine )
664 BOOL ui = FALSE, ui_exists;
665 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
666 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
667 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
669 MSI_SetPropertyW(package, szAction, szInstall);
671 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
673 package->script->InWhatSequence = SEQUENCE_INSTALL;
680 dir = strdupW(szPackagePath);
681 p = strrchrW(dir, '\\');
685 file = szPackagePath + (p - dir);
690 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
691 GetCurrentDirectoryW(MAX_PATH, dir);
692 lstrcatW(dir, cszbs);
693 file = szPackagePath;
696 msi_free( package->PackagePath );
697 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
698 if (!package->PackagePath)
701 return ERROR_OUTOFMEMORY;
704 lstrcpyW(package->PackagePath, dir);
705 lstrcatW(package->PackagePath, file);
708 msi_set_sourcedir_props(package, FALSE);
711 msi_parse_command_line( package, szCommandLine );
713 msi_apply_transforms( package );
714 msi_apply_patches( package );
716 /* properties may have been added by a transform */
717 msi_clone_properties( package );
719 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
721 package->script->InWhatSequence |= SEQUENCE_UI;
722 rc = ACTION_ProcessUISequence(package);
724 ui_exists = ui_sequence_exists(package);
725 if (rc == ERROR_SUCCESS || !ui_exists)
727 package->script->InWhatSequence |= SEQUENCE_EXEC;
728 rc = ACTION_ProcessExecSequence(package,ui_exists);
732 rc = ACTION_ProcessExecSequence(package,FALSE);
734 package->script->CurrentlyScripting= FALSE;
736 /* process the ending type action */
737 if (rc == ERROR_SUCCESS)
738 ACTION_PerformActionSequence(package,-1,ui);
739 else if (rc == ERROR_INSTALL_USEREXIT)
740 ACTION_PerformActionSequence(package,-2,ui);
741 else if (rc == ERROR_INSTALL_SUSPEND)
742 ACTION_PerformActionSequence(package,-4,ui);
744 ACTION_PerformActionSequence(package,-3,ui);
746 /* finish up running custom actions */
747 ACTION_FinishCustomActions(package);
752 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
754 UINT rc = ERROR_SUCCESS;
756 static const WCHAR ExecSeqQuery[] =
757 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
758 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
759 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
760 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
762 static const WCHAR UISeqQuery[] =
763 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
764 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
765 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
766 ' ', '=',' ','%','i',0};
769 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
771 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
775 LPCWSTR action, cond;
777 TRACE("Running the actions\n");
779 /* check conditions */
780 cond = MSI_RecordGetString(row,2);
782 /* this is a hack to skip errors in the condition code */
783 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
786 action = MSI_RecordGetString(row,1);
789 ERR("failed to fetch action\n");
790 rc = ERROR_FUNCTION_FAILED;
795 rc = ACTION_PerformUIAction(package,action,-1);
797 rc = ACTION_PerformAction(package,action,-1,FALSE);
799 msiobj_release(&row->hdr);
810 } iterate_action_param;
812 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
814 iterate_action_param *iap= (iterate_action_param*)param;
816 LPCWSTR cond, action;
818 action = MSI_RecordGetString(row,1);
821 ERR("Error is retrieving action name\n");
822 return ERROR_FUNCTION_FAILED;
825 /* check conditions */
826 cond = MSI_RecordGetString(row,2);
828 /* this is a hack to skip errors in the condition code */
829 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
831 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
832 return ERROR_SUCCESS;
836 rc = ACTION_PerformUIAction(iap->package,action,-1);
838 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
840 msi_dialog_check_messages( NULL );
842 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
843 rc = iap->package->CurrentInstallState;
845 if (rc == ERROR_FUNCTION_NOT_CALLED)
848 if (rc != ERROR_SUCCESS)
849 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
854 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
858 static const WCHAR query[] =
859 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
861 ' ','W','H','E','R','E',' ',
862 '`','S','e','q','u','e','n','c','e','`',' ',
863 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
864 '`','S','e','q','u','e','n','c','e','`',0};
865 iterate_action_param iap;
868 * FIXME: probably should be checking UILevel in the
869 * ACTION_PerformUIAction/ACTION_PerformAction
870 * rather than saving the UI level here. Those
871 * two functions can be merged too.
873 iap.package = package;
876 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
878 r = MSI_OpenQuery( package->db, &view, query, szTable );
879 if (r == ERROR_SUCCESS)
881 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
882 msiobj_release(&view->hdr);
888 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
892 static const WCHAR ExecSeqQuery[] =
893 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
894 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
895 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
896 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
897 'O','R','D','E','R',' ', 'B','Y',' ',
898 '`','S','e','q','u','e','n','c','e','`',0 };
900 static const WCHAR IVQuery[] =
901 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
902 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
903 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
904 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
905 ' ','\'', 'I','n','s','t','a','l','l',
906 'V','a','l','i','d','a','t','e','\'', 0};
908 iterate_action_param iap;
910 iap.package = package;
913 if (package->script->ExecuteSequenceRun)
915 TRACE("Execute Sequence already Run\n");
916 return ERROR_SUCCESS;
919 package->script->ExecuteSequenceRun = TRUE;
921 /* get the sequence number */
924 row = MSI_QueryGetRecord(package->db, IVQuery);
926 return ERROR_FUNCTION_FAILED;
927 seq = MSI_RecordGetInteger(row,1);
928 msiobj_release(&row->hdr);
931 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
932 if (rc == ERROR_SUCCESS)
934 TRACE("Running the actions\n");
936 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
937 msiobj_release(&view->hdr);
943 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
947 static const WCHAR ExecSeqQuery [] =
948 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
949 '`','I','n','s','t','a','l','l',
950 'U','I','S','e','q','u','e','n','c','e','`',
951 ' ','W','H','E','R','E',' ',
952 '`','S','e','q','u','e','n','c','e','`',' ',
953 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
954 '`','S','e','q','u','e','n','c','e','`',0};
955 iterate_action_param iap;
957 iap.package = package;
960 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
962 if (rc == ERROR_SUCCESS)
964 TRACE("Running the actions\n");
966 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
967 msiobj_release(&view->hdr);
973 /********************************************************
974 * ACTION helper functions and functions that perform the actions
975 *******************************************************/
976 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
977 UINT* rc, BOOL force )
983 if (!run && !package->script->CurrentlyScripting)
988 if (strcmpW(action,szInstallFinalize) == 0 ||
989 strcmpW(action,szInstallExecute) == 0 ||
990 strcmpW(action,szInstallExecuteAgain) == 0)
995 while (StandardActions[i].action != NULL)
997 if (strcmpW(StandardActions[i].action, action)==0)
1001 ui_actioninfo(package, action, TRUE, 0);
1002 *rc = schedule_action(package,INSTALL_SCRIPT,action);
1003 ui_actioninfo(package, action, FALSE, *rc);
1007 ui_actionstart(package, action);
1008 if (StandardActions[i].handler)
1010 *rc = StandardActions[i].handler(package);
1014 FIXME("unhandled standard action %s\n",debugstr_w(action));
1015 *rc = ERROR_SUCCESS;
1026 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1027 UINT* rc, UINT script, BOOL force )
1032 arc = ACTION_CustomAction(package, action, script, force);
1034 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1043 * A lot of actions are really important even if they don't do anything
1044 * explicit... Lots of properties are set at the beginning of the installation
1045 * CostFinalize does a bunch of work to translate the directories and such
1047 * But until I get write access to the database that is hard, so I am going to
1048 * hack it to see if I can get something to run.
1050 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1052 UINT rc = ERROR_SUCCESS;
1055 TRACE("Performing action (%s)\n",debugstr_w(action));
1057 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1060 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1064 FIXME("unhandled msi action %s\n",debugstr_w(action));
1065 rc = ERROR_FUNCTION_NOT_CALLED;
1071 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1073 UINT rc = ERROR_SUCCESS;
1074 BOOL handled = FALSE;
1076 TRACE("Performing action (%s)\n",debugstr_w(action));
1078 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1081 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1083 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1088 FIXME("unhandled msi action %s\n",debugstr_w(action));
1089 rc = ERROR_FUNCTION_NOT_CALLED;
1097 * Actual Action Handlers
1100 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1102 MSIPACKAGE *package = (MSIPACKAGE*)param;
1108 dir = MSI_RecordGetString(row,1);
1111 ERR("Unable to get folder id\n");
1112 return ERROR_SUCCESS;
1115 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1118 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1119 return ERROR_SUCCESS;
1122 TRACE("Folder is %s\n",debugstr_w(full_path));
1125 uirow = MSI_CreateRecord(1);
1126 MSI_RecordSetStringW(uirow,1,full_path);
1127 ui_actiondata(package,szCreateFolders,uirow);
1128 msiobj_release( &uirow->hdr );
1130 if (folder->State == 0)
1131 create_full_pathW(full_path);
1135 msi_free(full_path);
1136 return ERROR_SUCCESS;
1139 /* FIXME: probably should merge this with the above function */
1140 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1142 UINT rc = ERROR_SUCCESS;
1144 LPWSTR install_path;
1146 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1148 return ERROR_FUNCTION_FAILED;
1150 /* create the path */
1151 if (folder->State == 0)
1153 create_full_pathW(install_path);
1156 msi_free(install_path);
1161 UINT msi_create_component_directories( MSIPACKAGE *package )
1165 /* create all the folders required by the components are going to install */
1166 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1168 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1170 msi_create_directory( package, comp->Directory );
1173 return ERROR_SUCCESS;
1177 * Also we cannot enable/disable components either, so for now I am just going
1178 * to do all the directories for all the components.
1180 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1182 static const WCHAR ExecSeqQuery[] =
1183 {'S','E','L','E','C','T',' ',
1184 '`','D','i','r','e','c','t','o','r','y','_','`',
1185 ' ','F','R','O','M',' ',
1186 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1190 /* create all the empty folders specified in the CreateFolder table */
1191 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1192 if (rc != ERROR_SUCCESS)
1193 return ERROR_SUCCESS;
1195 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1196 msiobj_release(&view->hdr);
1198 msi_create_component_directories( package );
1203 static UINT load_component( MSIRECORD *row, LPVOID param )
1205 MSIPACKAGE *package = param;
1208 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1210 return ERROR_FUNCTION_FAILED;
1212 list_add_tail( &package->components, &comp->entry );
1214 /* fill in the data */
1215 comp->Component = msi_dup_record_field( row, 1 );
1217 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1219 comp->ComponentId = msi_dup_record_field( row, 2 );
1220 comp->Directory = msi_dup_record_field( row, 3 );
1221 comp->Attributes = MSI_RecordGetInteger(row,4);
1222 comp->Condition = msi_dup_record_field( row, 5 );
1223 comp->KeyPath = msi_dup_record_field( row, 6 );
1225 comp->Installed = INSTALLSTATE_UNKNOWN;
1226 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1228 return ERROR_SUCCESS;
1231 static UINT load_all_components( MSIPACKAGE *package )
1233 static const WCHAR query[] = {
1234 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1235 '`','C','o','m','p','o','n','e','n','t','`',0 };
1239 if (!list_empty(&package->components))
1240 return ERROR_SUCCESS;
1242 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1243 if (r != ERROR_SUCCESS)
1246 r = MSI_IterateRecords(view, NULL, load_component, package);
1247 msiobj_release(&view->hdr);
1252 MSIPACKAGE *package;
1253 MSIFEATURE *feature;
1256 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1260 cl = msi_alloc( sizeof (*cl) );
1262 return ERROR_NOT_ENOUGH_MEMORY;
1263 cl->component = comp;
1264 list_add_tail( &feature->Components, &cl->entry );
1266 return ERROR_SUCCESS;
1269 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1273 fl = msi_alloc( sizeof(*fl) );
1275 return ERROR_NOT_ENOUGH_MEMORY;
1276 fl->feature = child;
1277 list_add_tail( &parent->Children, &fl->entry );
1279 return ERROR_SUCCESS;
1282 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1284 _ilfs* ilfs= (_ilfs*)param;
1288 component = MSI_RecordGetString(row,1);
1290 /* check to see if the component is already loaded */
1291 comp = get_loaded_component( ilfs->package, component );
1294 ERR("unknown component %s\n", debugstr_w(component));
1295 return ERROR_FUNCTION_FAILED;
1298 add_feature_component( ilfs->feature, comp );
1299 comp->Enabled = TRUE;
1301 return ERROR_SUCCESS;
1304 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1306 MSIFEATURE *feature;
1308 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1310 if ( !lstrcmpW( feature->Feature, name ) )
1317 static UINT load_feature(MSIRECORD * row, LPVOID param)
1319 MSIPACKAGE* package = (MSIPACKAGE*)param;
1320 MSIFEATURE* feature;
1321 static const WCHAR Query1[] =
1322 {'S','E','L','E','C','T',' ',
1323 '`','C','o','m','p','o','n','e','n','t','_','`',
1324 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1325 'C','o','m','p','o','n','e','n','t','s','`',' ',
1326 'W','H','E','R','E',' ',
1327 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1332 /* fill in the data */
1334 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1336 return ERROR_NOT_ENOUGH_MEMORY;
1338 list_init( &feature->Children );
1339 list_init( &feature->Components );
1341 feature->Feature = msi_dup_record_field( row, 1 );
1343 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1345 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1346 feature->Title = msi_dup_record_field( row, 3 );
1347 feature->Description = msi_dup_record_field( row, 4 );
1349 if (!MSI_RecordIsNull(row,5))
1350 feature->Display = MSI_RecordGetInteger(row,5);
1352 feature->Level= MSI_RecordGetInteger(row,6);
1353 feature->Directory = msi_dup_record_field( row, 7 );
1354 feature->Attributes = MSI_RecordGetInteger(row,8);
1356 feature->Installed = INSTALLSTATE_UNKNOWN;
1357 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1359 list_add_tail( &package->features, &feature->entry );
1361 /* load feature components */
1363 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1364 if (rc != ERROR_SUCCESS)
1365 return ERROR_SUCCESS;
1367 ilfs.package = package;
1368 ilfs.feature = feature;
1370 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1371 msiobj_release(&view->hdr);
1373 return ERROR_SUCCESS;
1376 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1378 MSIPACKAGE* package = (MSIPACKAGE*)param;
1379 MSIFEATURE *parent, *child;
1381 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1383 return ERROR_FUNCTION_FAILED;
1385 if (!child->Feature_Parent)
1386 return ERROR_SUCCESS;
1388 parent = find_feature_by_name( package, child->Feature_Parent );
1390 return ERROR_FUNCTION_FAILED;
1392 add_feature_child( parent, child );
1393 return ERROR_SUCCESS;
1396 static UINT load_all_features( MSIPACKAGE *package )
1398 static const WCHAR query[] = {
1399 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1400 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1401 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1405 if (!list_empty(&package->features))
1406 return ERROR_SUCCESS;
1408 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1409 if (r != ERROR_SUCCESS)
1412 r = MSI_IterateRecords( view, NULL, load_feature, package );
1413 if (r != ERROR_SUCCESS)
1416 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1417 msiobj_release( &view->hdr );
1422 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1433 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1435 static const WCHAR query[] = {
1436 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1437 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1438 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1439 MSIQUERY *view = NULL;
1440 MSIRECORD *row = NULL;
1443 TRACE("%s\n", debugstr_w(file->File));
1445 r = MSI_OpenQuery(package->db, &view, query, file->File);
1446 if (r != ERROR_SUCCESS)
1449 r = MSI_ViewExecute(view, NULL);
1450 if (r != ERROR_SUCCESS)
1453 r = MSI_ViewFetch(view, &row);
1454 if (r != ERROR_SUCCESS)
1457 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1458 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1459 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1460 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1461 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1464 if (view) msiobj_release(&view->hdr);
1465 if (row) msiobj_release(&row->hdr);
1469 static UINT load_file(MSIRECORD *row, LPVOID param)
1471 MSIPACKAGE* package = (MSIPACKAGE*)param;
1475 /* fill in the data */
1477 file = msi_alloc_zero( sizeof (MSIFILE) );
1479 return ERROR_NOT_ENOUGH_MEMORY;
1481 file->File = msi_dup_record_field( row, 1 );
1483 component = MSI_RecordGetString( row, 2 );
1484 file->Component = get_loaded_component( package, component );
1486 if (!file->Component)
1487 ERR("Unfound Component %s\n",debugstr_w(component));
1489 file->FileName = msi_dup_record_field( row, 3 );
1490 reduce_to_longfilename( file->FileName );
1492 file->ShortName = msi_dup_record_field( row, 3 );
1493 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1495 file->FileSize = MSI_RecordGetInteger( row, 4 );
1496 file->Version = msi_dup_record_field( row, 5 );
1497 file->Language = msi_dup_record_field( row, 6 );
1498 file->Attributes = MSI_RecordGetInteger( row, 7 );
1499 file->Sequence = MSI_RecordGetInteger( row, 8 );
1501 file->state = msifs_invalid;
1503 /* if the compressed bits are not set in the file attributes,
1504 * then read the information from the package word count property
1506 if (file->Attributes & msidbFileAttributesCompressed)
1508 file->IsCompressed = TRUE;
1510 else if (file->Attributes & msidbFileAttributesNoncompressed)
1512 file->IsCompressed = FALSE;
1516 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1519 load_file_hash(package, file);
1521 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1523 list_add_tail( &package->files, &file->entry );
1525 return ERROR_SUCCESS;
1528 static UINT load_all_files(MSIPACKAGE *package)
1532 static const WCHAR Query[] =
1533 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1534 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1535 '`','S','e','q','u','e','n','c','e','`', 0};
1537 if (!list_empty(&package->files))
1538 return ERROR_SUCCESS;
1540 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1541 if (rc != ERROR_SUCCESS)
1542 return ERROR_SUCCESS;
1544 rc = MSI_IterateRecords(view, NULL, load_file, package);
1545 msiobj_release(&view->hdr);
1547 return ERROR_SUCCESS;
1550 static UINT load_folder( MSIRECORD *row, LPVOID param )
1552 MSIPACKAGE *package = param;
1553 static const WCHAR szDot[] = { '.',0 };
1554 static WCHAR szEmpty[] = { 0 };
1555 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1558 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1560 return ERROR_NOT_ENOUGH_MEMORY;
1562 folder->Directory = msi_dup_record_field( row, 1 );
1564 TRACE("%s\n", debugstr_w(folder->Directory));
1566 p = msi_dup_record_field(row, 3);
1568 /* split src and target dir */
1570 src_short = folder_split_path( p, ':' );
1572 /* split the long and short paths */
1573 tgt_long = folder_split_path( tgt_short, '|' );
1574 src_long = folder_split_path( src_short, '|' );
1576 /* check for no-op dirs */
1577 if (!lstrcmpW(szDot, tgt_short))
1578 tgt_short = szEmpty;
1579 if (!lstrcmpW(szDot, src_short))
1580 src_short = szEmpty;
1583 tgt_long = tgt_short;
1586 src_short = tgt_short;
1587 src_long = tgt_long;
1591 src_long = src_short;
1593 /* FIXME: use the target short path too */
1594 folder->TargetDefault = strdupW(tgt_long);
1595 folder->SourceShortPath = strdupW(src_short);
1596 folder->SourceLongPath = strdupW(src_long);
1599 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1600 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1601 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1603 folder->Parent = msi_dup_record_field( row, 2 );
1605 folder->Property = msi_dup_property( package, folder->Directory );
1607 list_add_tail( &package->folders, &folder->entry );
1609 TRACE("returning %p\n", folder);
1611 return ERROR_SUCCESS;
1614 static UINT load_all_folders( MSIPACKAGE *package )
1616 static const WCHAR query[] = {
1617 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1618 '`','D','i','r','e','c','t','o','r','y','`',0 };
1622 if (!list_empty(&package->folders))
1623 return ERROR_SUCCESS;
1625 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1626 if (r != ERROR_SUCCESS)
1629 r = MSI_IterateRecords(view, NULL, load_folder, package);
1630 msiobj_release(&view->hdr);
1635 * I am not doing any of the costing functionality yet.
1636 * Mostly looking at doing the Component and Feature loading
1638 * The native MSI does A LOT of modification to tables here. Mostly adding
1639 * a lot of temporary columns to the Feature and Component tables.
1641 * note: Native msi also tracks the short filename. But I am only going to
1642 * track the long ones. Also looking at this directory table
1643 * it appears that the directory table does not get the parents
1644 * resolved base on property only based on their entries in the
1647 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1649 static const WCHAR szCosting[] =
1650 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1651 static const WCHAR szZero[] = { '0', 0 };
1653 MSI_SetPropertyW(package, szCosting, szZero);
1654 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1656 load_all_components( package );
1657 load_all_features( package );
1658 load_all_files( package );
1659 load_all_folders( package );
1661 return ERROR_SUCCESS;
1664 static UINT execute_script(MSIPACKAGE *package, UINT script )
1667 UINT rc = ERROR_SUCCESS;
1669 TRACE("Executing Script %i\n",script);
1671 if (!package->script)
1673 ERR("no script!\n");
1674 return ERROR_FUNCTION_FAILED;
1677 for (i = 0; i < package->script->ActionCount[script]; i++)
1680 action = package->script->Actions[script][i];
1681 ui_actionstart(package, action);
1682 TRACE("Executing Action (%s)\n",debugstr_w(action));
1683 rc = ACTION_PerformAction(package, action, script, TRUE);
1684 if (rc != ERROR_SUCCESS)
1687 msi_free_action_script(package, script);
1691 static UINT ACTION_FileCost(MSIPACKAGE *package)
1693 return ERROR_SUCCESS;
1696 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1700 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1704 if (!comp->ComponentId)
1707 res = MsiGetComponentPathW( package->ProductCode,
1708 comp->ComponentId, NULL, NULL);
1710 res = INSTALLSTATE_ABSENT;
1711 comp->Installed = res;
1715 /* scan for and update current install states */
1716 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1719 MSIFEATURE *feature;
1721 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1724 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1726 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1728 comp= cl->component;
1730 if (!comp->ComponentId)
1732 res = INSTALLSTATE_ABSENT;
1736 if (res == INSTALLSTATE_ABSENT)
1737 res = comp->Installed;
1740 if (res == comp->Installed)
1743 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1744 res != INSTALLSTATE_SOURCE)
1746 res = INSTALLSTATE_INCOMPLETE;
1750 feature->Installed = res;
1754 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1757 static const WCHAR all[]={'A','L','L',0};
1759 MSIFEATURE *feature;
1761 override = msi_dup_property( package, property );
1765 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1767 if (strcmpiW(override,all)==0)
1768 msi_feature_set_state( feature, state );
1771 LPWSTR ptr = override;
1772 LPWSTR ptr2 = strchrW(override,',');
1776 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1777 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1779 msi_feature_set_state( feature, state );
1785 ptr2 = strchrW(ptr,',');
1797 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1800 static const WCHAR szlevel[] =
1801 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1802 static const WCHAR szAddLocal[] =
1803 {'A','D','D','L','O','C','A','L',0};
1804 static const WCHAR szAddSource[] =
1805 {'A','D','D','S','O','U','R','C','E',0};
1806 static const WCHAR szRemove[] =
1807 {'R','E','M','O','V','E',0};
1808 static const WCHAR szReinstall[] =
1809 {'R','E','I','N','S','T','A','L','L',0};
1810 BOOL override = FALSE;
1811 MSICOMPONENT* component;
1812 MSIFEATURE *feature;
1815 /* I do not know if this is where it should happen.. but */
1817 TRACE("Checking Install Level\n");
1819 install_level = msi_get_property_int( package, szlevel, 1 );
1821 /* ok here is the _real_ rub
1822 * all these activation/deactivation things happen in order and things
1823 * later on the list override things earlier on the list.
1824 * 1) INSTALLLEVEL processing
1834 * 11) FILEADDDEFAULT
1835 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1836 * ignored for all the features. seems strange, especially since it is not
1837 * documented anywhere, but it is how it works.
1839 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1840 * REMOVE are the big ones, since we don't handle administrative installs
1843 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1844 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1845 override |= process_state_property(package,szAddSource,INSTALLSTATE_SOURCE);
1846 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1850 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1852 BOOL feature_state = ((feature->Level > 0) &&
1853 (feature->Level <= install_level));
1855 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1857 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1858 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1859 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1860 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1862 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1866 /* disable child features of unselected parent features */
1867 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1871 if (feature->Level > 0 && feature->Level <= install_level)
1874 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1875 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1880 /* set the Preselected Property */
1881 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1882 static const WCHAR szOne[] = { '1', 0 };
1884 MSI_SetPropertyW(package,szPreselected,szOne);
1888 * now we want to enable or disable components base on feature
1891 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1895 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1896 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1898 /* features with components that have compressed files are made local */
1899 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1901 if (cl->component->Enabled &&
1902 cl->component->ForceLocalState &&
1903 feature->Action == INSTALLSTATE_SOURCE)
1905 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1910 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1912 component = cl->component;
1914 if (!component->Enabled)
1917 switch (feature->Action)
1919 case INSTALLSTATE_ABSENT:
1920 component->anyAbsent = 1;
1922 case INSTALLSTATE_ADVERTISED:
1923 component->hasAdvertiseFeature = 1;
1925 case INSTALLSTATE_SOURCE:
1926 component->hasSourceFeature = 1;
1928 case INSTALLSTATE_LOCAL:
1929 component->hasLocalFeature = 1;
1931 case INSTALLSTATE_DEFAULT:
1932 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1933 component->hasAdvertiseFeature = 1;
1934 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1935 component->hasSourceFeature = 1;
1937 component->hasLocalFeature = 1;
1945 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1947 /* if the component isn't enabled, leave it alone */
1948 if (!component->Enabled)
1951 /* check if it's local or source */
1952 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1953 (component->hasLocalFeature || component->hasSourceFeature))
1955 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1956 !component->ForceLocalState)
1957 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1959 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1963 /* if any feature is local, the component must be local too */
1964 if (component->hasLocalFeature)
1966 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1970 if (component->hasSourceFeature)
1972 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1976 if (component->hasAdvertiseFeature)
1978 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1982 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1983 if (component->anyAbsent)
1984 msi_component_set_state(component, INSTALLSTATE_ABSENT);
1987 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1989 if (component->Action == INSTALLSTATE_DEFAULT)
1991 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1992 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1995 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1996 debugstr_w(component->Component), component->Installed, component->Action);
2000 return ERROR_SUCCESS;
2003 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
2005 MSIPACKAGE *package = (MSIPACKAGE*)param;
2010 name = MSI_RecordGetString(row,1);
2012 f = get_loaded_folder(package, name);
2013 if (!f) return ERROR_SUCCESS;
2015 /* reset the ResolvedTarget */
2016 msi_free(f->ResolvedTarget);
2017 f->ResolvedTarget = NULL;
2019 /* This helper function now does ALL the work */
2020 TRACE("Dir %s ...\n",debugstr_w(name));
2021 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2022 TRACE("resolves to %s\n",debugstr_w(path));
2025 return ERROR_SUCCESS;
2028 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2030 MSIPACKAGE *package = (MSIPACKAGE*)param;
2032 MSIFEATURE *feature;
2034 name = MSI_RecordGetString( row, 1 );
2036 feature = get_loaded_feature( package, name );
2038 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2042 Condition = MSI_RecordGetString(row,3);
2044 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2046 int level = MSI_RecordGetInteger(row,2);
2047 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
2048 feature->Level = level;
2051 return ERROR_SUCCESS;
2054 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2056 static const WCHAR name_fmt[] =
2057 {'%','u','.','%','u','.','%','u','.','%','u',0};
2058 static WCHAR name[] = {'\\',0};
2059 VS_FIXEDFILEINFO *lpVer;
2060 WCHAR filever[0x100];
2066 TRACE("%s\n", debugstr_w(filename));
2068 versize = GetFileVersionInfoSizeW( filename, &handle );
2072 version = msi_alloc( versize );
2073 GetFileVersionInfoW( filename, 0, versize, version );
2075 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2077 msi_free( version );
2081 sprintfW( filever, name_fmt,
2082 HIWORD(lpVer->dwFileVersionMS),
2083 LOWORD(lpVer->dwFileVersionMS),
2084 HIWORD(lpVer->dwFileVersionLS),
2085 LOWORD(lpVer->dwFileVersionLS));
2087 msi_free( version );
2089 return strdupW( filever );
2092 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2094 LPWSTR file_version;
2097 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2099 MSICOMPONENT* comp = file->Component;
2105 if (file->IsCompressed)
2106 comp->ForceLocalState = TRUE;
2108 /* calculate target */
2109 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2111 msi_free(file->TargetPath);
2113 TRACE("file %s is named %s\n",
2114 debugstr_w(file->File), debugstr_w(file->FileName));
2116 file->TargetPath = build_directory_name(2, p, file->FileName);
2120 TRACE("file %s resolves to %s\n",
2121 debugstr_w(file->File), debugstr_w(file->TargetPath));
2123 /* don't check files of components that aren't installed */
2124 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2125 comp->Installed == INSTALLSTATE_ABSENT)
2127 file->state = msifs_missing; /* assume files are missing */
2131 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2133 file->state = msifs_missing;
2134 comp->Cost += file->FileSize;
2135 comp->Installed = INSTALLSTATE_INCOMPLETE;
2139 if (file->Version &&
2140 (file_version = msi_get_disk_file_version( file->TargetPath )))
2142 TRACE("new %s old %s\n", debugstr_w(file->Version),
2143 debugstr_w(file_version));
2144 /* FIXME: seems like a bad way to compare version numbers */
2145 if (lstrcmpiW(file_version, file->Version)<0)
2147 file->state = msifs_overwrite;
2148 comp->Cost += file->FileSize;
2149 comp->Installed = INSTALLSTATE_INCOMPLETE;
2152 file->state = msifs_present;
2153 msi_free( file_version );
2156 file->state = msifs_present;
2159 return ERROR_SUCCESS;
2163 * A lot is done in this function aside from just the costing.
2164 * The costing needs to be implemented at some point but for now I am going
2165 * to focus on the directory building
2168 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2170 static const WCHAR ExecSeqQuery[] =
2171 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2172 '`','D','i','r','e','c','t','o','r','y','`',0};
2173 static const WCHAR ConditionQuery[] =
2174 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2175 '`','C','o','n','d','i','t','i','o','n','`',0};
2176 static const WCHAR szCosting[] =
2177 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2178 static const WCHAR szlevel[] =
2179 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2180 static const WCHAR szOne[] = { '1', 0 };
2186 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2187 return ERROR_SUCCESS;
2189 TRACE("Building Directory properties\n");
2191 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2192 if (rc == ERROR_SUCCESS)
2194 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2196 msiobj_release(&view->hdr);
2199 /* read components states from the registry */
2200 ACTION_GetComponentInstallStates(package);
2202 TRACE("File calculations\n");
2203 msi_check_file_install_states( package );
2205 TRACE("Evaluating Condition Table\n");
2207 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2208 if (rc == ERROR_SUCCESS)
2210 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2212 msiobj_release(&view->hdr);
2215 TRACE("Enabling or Disabling Components\n");
2216 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2218 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2220 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2221 comp->Enabled = FALSE;
2225 MSI_SetPropertyW(package,szCosting,szOne);
2226 /* set default run level if not set */
2227 level = msi_dup_property( package, szlevel );
2229 MSI_SetPropertyW(package,szlevel, szOne);
2232 ACTION_UpdateFeatureInstallStates(package);
2234 return MSI_SetFeatureStates(package);
2237 /* OK this value is "interpreted" and then formatted based on the
2238 first few characters */
2239 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2244 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2250 LPWSTR deformated = NULL;
2253 deformat_string(package, &value[2], &deformated);
2255 /* binary value type */
2259 *size = (strlenW(ptr)/2)+1;
2261 *size = strlenW(ptr)/2;
2263 data = msi_alloc(*size);
2269 /* if uneven pad with a zero in front */
2275 data[count] = (BYTE)strtol(byte,NULL,0);
2277 TRACE("Uneven byte count\n");
2285 data[count] = (BYTE)strtol(byte,NULL,0);
2288 msi_free(deformated);
2290 TRACE("Data %i bytes(%i)\n",*size,count);
2297 deformat_string(package, &value[1], &deformated);
2300 *size = sizeof(DWORD);
2301 data = msi_alloc(*size);
2307 if ( (*p < '0') || (*p > '9') )
2313 if (deformated[0] == '-')
2316 TRACE("DWORD %i\n",*(LPDWORD)data);
2318 msi_free(deformated);
2323 static const WCHAR szMulti[] = {'[','~',']',0};
2332 *type=REG_EXPAND_SZ;
2340 if (strstrW(value,szMulti))
2341 *type = REG_MULTI_SZ;
2343 /* remove initial delimiter */
2344 if (!strncmpW(value, szMulti, 3))
2347 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2349 /* add double NULL terminator */
2350 if (*type == REG_MULTI_SZ)
2352 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2353 data = msi_realloc_zero(data, *size);
2359 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2361 MSIPACKAGE *package = (MSIPACKAGE*)param;
2362 static const WCHAR szHCR[] =
2363 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2364 'R','O','O','T','\\',0};
2365 static const WCHAR szHCU[] =
2366 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2367 'U','S','E','R','\\',0};
2368 static const WCHAR szHLM[] =
2369 {'H','K','E','Y','_','L','O','C','A','L','_',
2370 'M','A','C','H','I','N','E','\\',0};
2371 static const WCHAR szHU[] =
2372 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2374 LPSTR value_data = NULL;
2375 HKEY root_key, hkey;
2378 LPCWSTR szRoot, component, name, key, value;
2383 BOOL check_first = FALSE;
2386 ui_progress(package,2,0,0,0);
2393 component = MSI_RecordGetString(row, 6);
2394 comp = get_loaded_component(package,component);
2396 return ERROR_SUCCESS;
2398 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2400 TRACE("Skipping write due to disabled component %s\n",
2401 debugstr_w(component));
2403 comp->Action = comp->Installed;
2405 return ERROR_SUCCESS;
2408 comp->Action = INSTALLSTATE_LOCAL;
2410 name = MSI_RecordGetString(row, 4);
2411 if( MSI_RecordIsNull(row,5) && name )
2413 /* null values can have special meanings */
2414 if (name[0]=='-' && name[1] == 0)
2415 return ERROR_SUCCESS;
2416 else if ((name[0]=='+' && name[1] == 0) ||
2417 (name[0] == '*' && name[1] == 0))
2422 root = MSI_RecordGetInteger(row,2);
2423 key = MSI_RecordGetString(row, 3);
2425 /* get the root key */
2430 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2431 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2432 if (all_users && all_users[0] == '1')
2434 root_key = HKEY_LOCAL_MACHINE;
2439 root_key = HKEY_CURRENT_USER;
2442 msi_free(all_users);
2445 case 0: root_key = HKEY_CLASSES_ROOT;
2448 case 1: root_key = HKEY_CURRENT_USER;
2451 case 2: root_key = HKEY_LOCAL_MACHINE;
2454 case 3: root_key = HKEY_USERS;
2458 ERR("Unknown root %i\n",root);
2464 return ERROR_SUCCESS;
2466 deformat_string(package, key , &deformated);
2467 size = strlenW(deformated) + strlenW(szRoot) + 1;
2468 uikey = msi_alloc(size*sizeof(WCHAR));
2469 strcpyW(uikey,szRoot);
2470 strcatW(uikey,deformated);
2472 if (RegCreateKeyW( root_key, deformated, &hkey))
2474 ERR("Could not create key %s\n",debugstr_w(deformated));
2475 msi_free(deformated);
2477 return ERROR_SUCCESS;
2479 msi_free(deformated);
2481 value = MSI_RecordGetString(row,5);
2483 value_data = parse_value(package, value, &type, &size);
2486 static const WCHAR szEmpty[] = {0};
2487 value_data = (LPSTR)strdupW(szEmpty);
2492 deformat_string(package, name, &deformated);
2496 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2498 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2503 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2504 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2506 TRACE("value %s of %s checked already exists\n",
2507 debugstr_w(deformated), debugstr_w(uikey));
2511 TRACE("Checked and setting value %s of %s\n",
2512 debugstr_w(deformated), debugstr_w(uikey));
2513 if (deformated || size)
2514 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2519 uirow = MSI_CreateRecord(3);
2520 MSI_RecordSetStringW(uirow,2,deformated);
2521 MSI_RecordSetStringW(uirow,1,uikey);
2524 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2526 MSI_RecordSetStringW(uirow,3,value);
2528 ui_actiondata(package,szWriteRegistryValues,uirow);
2529 msiobj_release( &uirow->hdr );
2531 msi_free(value_data);
2532 msi_free(deformated);
2535 return ERROR_SUCCESS;
2538 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2542 static const WCHAR ExecSeqQuery[] =
2543 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2544 '`','R','e','g','i','s','t','r','y','`',0 };
2546 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2547 if (rc != ERROR_SUCCESS)
2548 return ERROR_SUCCESS;
2550 /* increment progress bar each time action data is sent */
2551 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2553 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2555 msiobj_release(&view->hdr);
2559 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2561 package->script->CurrentlyScripting = TRUE;
2563 return ERROR_SUCCESS;
2567 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2572 static const WCHAR q1[]=
2573 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2574 '`','R','e','g','i','s','t','r','y','`',0};
2577 MSIFEATURE *feature;
2580 TRACE("InstallValidate\n");
2582 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2583 if (rc == ERROR_SUCCESS)
2585 MSI_IterateRecords( view, &progress, NULL, package );
2586 msiobj_release( &view->hdr );
2587 total += progress * REG_PROGRESS_VALUE;
2590 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2591 total += COMPONENT_PROGRESS_VALUE;
2593 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2594 total += file->FileSize;
2596 ui_progress(package,0,total,0,0);
2598 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2600 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2601 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2602 feature->ActionRequest);
2605 return ERROR_SUCCESS;
2608 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2610 MSIPACKAGE* package = (MSIPACKAGE*)param;
2611 LPCWSTR cond = NULL;
2612 LPCWSTR message = NULL;
2615 static const WCHAR title[]=
2616 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2618 cond = MSI_RecordGetString(row,1);
2620 r = MSI_EvaluateConditionW(package,cond);
2621 if (r == MSICONDITION_FALSE)
2623 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2626 message = MSI_RecordGetString(row,2);
2627 deformat_string(package,message,&deformated);
2628 MessageBoxW(NULL,deformated,title,MB_OK);
2629 msi_free(deformated);
2632 return ERROR_INSTALL_FAILURE;
2635 return ERROR_SUCCESS;
2638 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2641 MSIQUERY * view = NULL;
2642 static const WCHAR ExecSeqQuery[] =
2643 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2644 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2646 TRACE("Checking launch conditions\n");
2648 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2649 if (rc != ERROR_SUCCESS)
2650 return ERROR_SUCCESS;
2652 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2653 msiobj_release(&view->hdr);
2658 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2662 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2664 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2666 MSIRECORD * row = 0;
2668 LPWSTR deformated,buffer,deformated_name;
2670 static const WCHAR ExecSeqQuery[] =
2671 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2672 '`','R','e','g','i','s','t','r','y','`',' ',
2673 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2674 ' ','=',' ' ,'\'','%','s','\'',0 };
2675 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2676 static const WCHAR fmt2[]=
2677 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2679 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2683 root = MSI_RecordGetInteger(row,2);
2684 key = MSI_RecordGetString(row, 3);
2685 name = MSI_RecordGetString(row, 4);
2686 deformat_string(package, key , &deformated);
2687 deformat_string(package, name, &deformated_name);
2689 len = strlenW(deformated) + 6;
2690 if (deformated_name)
2691 len+=strlenW(deformated_name);
2693 buffer = msi_alloc( len *sizeof(WCHAR));
2695 if (deformated_name)
2696 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2698 sprintfW(buffer,fmt,root,deformated);
2700 msi_free(deformated);
2701 msi_free(deformated_name);
2702 msiobj_release(&row->hdr);
2706 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2708 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2713 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2716 return strdupW( file->TargetPath );
2721 static HKEY openSharedDLLsKey(void)
2724 static const WCHAR path[] =
2725 {'S','o','f','t','w','a','r','e','\\',
2726 'M','i','c','r','o','s','o','f','t','\\',
2727 'W','i','n','d','o','w','s','\\',
2728 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2729 'S','h','a','r','e','d','D','L','L','s',0};
2731 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2735 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2740 DWORD sz = sizeof(count);
2743 hkey = openSharedDLLsKey();
2744 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2745 if (rc != ERROR_SUCCESS)
2751 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2755 hkey = openSharedDLLsKey();
2757 msi_reg_set_val_dword( hkey, path, count );
2759 RegDeleteValueW(hkey,path);
2765 * Return TRUE if the count should be written out and FALSE if not
2767 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2769 MSIFEATURE *feature;
2773 /* only refcount DLLs */
2774 if (comp->KeyPath == NULL ||
2775 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2776 comp->Attributes & msidbComponentAttributesODBCDataSource)
2780 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2781 write = (count > 0);
2783 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2787 /* increment counts */
2788 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2792 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2795 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2797 if ( cl->component == comp )
2802 /* decrement counts */
2803 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2807 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2810 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2812 if ( cl->component == comp )
2817 /* ref count all the files in the component */
2822 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2824 if (file->Component == comp)
2825 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2829 /* add a count for permenent */
2830 if (comp->Attributes & msidbComponentAttributesPermanent)
2833 comp->RefCount = count;
2836 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2840 * Ok further analysis makes me think that this work is
2841 * actually done in the PublishComponents and PublishFeatures
2842 * step, and not here. It appears like the keypath and all that is
2843 * resolved in this step, however actually written in the Publish steps.
2844 * But we will leave it here for now because it is unclear
2846 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2848 WCHAR squished_pc[GUID_SIZE];
2849 WCHAR squished_cc[GUID_SIZE];
2852 HKEY hkey=0,hkey2=0;
2856 /* writes the Component and Features values to the registry */
2858 rc = MSIREG_OpenComponents(&hkey);
2859 if (rc != ERROR_SUCCESS)
2862 squash_guid(package->ProductCode,squished_pc);
2863 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2865 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2869 ui_progress(package,2,0,0,0);
2870 if (!comp->ComponentId)
2873 squash_guid(comp->ComponentId,squished_cc);
2875 msi_free(comp->FullKeypath);
2876 comp->FullKeypath = resolve_keypath( package, comp );
2878 /* do the refcounting */
2879 ACTION_RefCountComponent( package, comp );
2881 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2882 debugstr_w(comp->Component),
2883 debugstr_w(squished_cc),
2884 debugstr_w(comp->FullKeypath),
2887 * Write the keypath out if the component is to be registered
2888 * and delete the key if the component is to be deregistered
2890 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2892 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2893 if (rc != ERROR_SUCCESS)
2896 if (!comp->FullKeypath)
2899 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2901 if (comp->Attributes & msidbComponentAttributesPermanent)
2903 static const WCHAR szPermKey[] =
2904 { '0','0','0','0','0','0','0','0','0','0','0','0',
2905 '0','0','0','0','0','0','0','0','0','0','0','0',
2906 '0','0','0','0','0','0','0','0',0 };
2908 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2913 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey2, TRUE);
2914 if (rc != ERROR_SUCCESS)
2917 msi_reg_set_val_str(hkey2, squished_pc, comp->FullKeypath);
2920 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2924 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2925 if (rc != ERROR_SUCCESS)
2928 RegDeleteValueW(hkey2,squished_pc);
2930 /* if the key is empty delete it */
2931 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2933 if (res == ERROR_NO_MORE_ITEMS)
2934 RegDeleteKeyW(hkey,squished_cc);
2936 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2940 uirow = MSI_CreateRecord(3);
2941 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2942 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2943 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2944 ui_actiondata(package,szProcessComponents,uirow);
2945 msiobj_release( &uirow->hdr );
2959 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2960 LPWSTR lpszName, LONG_PTR lParam)
2963 typelib_struct *tl_struct = (typelib_struct*) lParam;
2964 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2968 if (!IS_INTRESOURCE(lpszName))
2970 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2974 sz = strlenW(tl_struct->source)+4;
2975 sz *= sizeof(WCHAR);
2977 if ((INT_PTR)lpszName == 1)
2978 tl_struct->path = strdupW(tl_struct->source);
2981 tl_struct->path = msi_alloc(sz);
2982 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2985 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2986 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2987 if (!SUCCEEDED(res))
2989 msi_free(tl_struct->path);
2990 tl_struct->path = NULL;
2995 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2996 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2998 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3002 msi_free(tl_struct->path);
3003 tl_struct->path = NULL;
3005 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3006 ITypeLib_Release(tl_struct->ptLib);
3011 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3013 MSIPACKAGE* package = (MSIPACKAGE*)param;
3017 typelib_struct tl_struct;
3019 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3021 component = MSI_RecordGetString(row,3);
3022 comp = get_loaded_component(package,component);
3024 return ERROR_SUCCESS;
3026 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3028 TRACE("Skipping typelib reg due to disabled component\n");
3030 comp->Action = comp->Installed;
3032 return ERROR_SUCCESS;
3035 comp->Action = INSTALLSTATE_LOCAL;
3037 file = get_loaded_file( package, comp->KeyPath );
3039 return ERROR_SUCCESS;
3041 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3045 guid = MSI_RecordGetString(row,1);
3046 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3047 tl_struct.source = strdupW( file->TargetPath );
3048 tl_struct.path = NULL;
3050 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3051 (LONG_PTR)&tl_struct);
3059 helpid = MSI_RecordGetString(row,6);
3062 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3063 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3066 if (!SUCCEEDED(res))
3067 ERR("Failed to register type library %s\n",
3068 debugstr_w(tl_struct.path));
3071 ui_actiondata(package,szRegisterTypeLibraries,row);
3073 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3076 ITypeLib_Release(tl_struct.ptLib);
3077 msi_free(tl_struct.path);
3080 ERR("Failed to load type library %s\n",
3081 debugstr_w(tl_struct.source));
3083 FreeLibrary(module);
3084 msi_free(tl_struct.source);
3087 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3089 return ERROR_SUCCESS;
3092 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3095 * OK this is a bit confusing.. I am given a _Component key and I believe
3096 * that the file that is being registered as a type library is the "key file
3097 * of that component" which I interpret to mean "The file in the KeyPath of
3102 static const WCHAR Query[] =
3103 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3104 '`','T','y','p','e','L','i','b','`',0};
3106 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3107 if (rc != ERROR_SUCCESS)
3108 return ERROR_SUCCESS;
3110 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3111 msiobj_release(&view->hdr);
3115 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3117 MSIPACKAGE *package = (MSIPACKAGE*)param;
3118 LPWSTR target_file, target_folder, filename;
3119 LPCWSTR buffer, extension;
3121 static const WCHAR szlnk[]={'.','l','n','k',0};
3122 IShellLinkW *sl = NULL;
3123 IPersistFile *pf = NULL;
3126 buffer = MSI_RecordGetString(row,4);
3127 comp = get_loaded_component(package,buffer);
3129 return ERROR_SUCCESS;
3131 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3133 TRACE("Skipping shortcut creation due to disabled component\n");
3135 comp->Action = comp->Installed;
3137 return ERROR_SUCCESS;
3140 comp->Action = INSTALLSTATE_LOCAL;
3142 ui_actiondata(package,szCreateShortcuts,row);
3144 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3145 &IID_IShellLinkW, (LPVOID *) &sl );
3149 ERR("CLSID_ShellLink not available\n");
3153 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3156 ERR("QueryInterface(IID_IPersistFile) failed\n");
3160 buffer = MSI_RecordGetString(row,2);
3161 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3163 /* may be needed because of a bug somehwere else */
3164 create_full_pathW(target_folder);
3166 filename = msi_dup_record_field( row, 3 );
3167 reduce_to_longfilename(filename);
3169 extension = strchrW(filename,'.');
3170 if (!extension || strcmpiW(extension,szlnk))
3172 int len = strlenW(filename);
3173 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3174 memcpy(filename + len, szlnk, sizeof(szlnk));
3176 target_file = build_directory_name(2, target_folder, filename);
3177 msi_free(target_folder);
3180 buffer = MSI_RecordGetString(row,5);
3181 if (strchrW(buffer,'['))
3184 deformat_string(package,buffer,&deformated);
3185 IShellLinkW_SetPath(sl,deformated);
3186 msi_free(deformated);
3190 FIXME("poorly handled shortcut format, advertised shortcut\n");
3191 IShellLinkW_SetPath(sl,comp->FullKeypath);
3194 if (!MSI_RecordIsNull(row,6))
3197 buffer = MSI_RecordGetString(row,6);
3198 deformat_string(package,buffer,&deformated);
3199 IShellLinkW_SetArguments(sl,deformated);
3200 msi_free(deformated);
3203 if (!MSI_RecordIsNull(row,7))
3205 buffer = MSI_RecordGetString(row,7);
3206 IShellLinkW_SetDescription(sl,buffer);
3209 if (!MSI_RecordIsNull(row,8))
3210 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3212 if (!MSI_RecordIsNull(row,9))
3217 buffer = MSI_RecordGetString(row,9);
3219 Path = build_icon_path(package,buffer);
3220 index = MSI_RecordGetInteger(row,10);
3222 /* no value means 0 */
3223 if (index == MSI_NULL_INTEGER)
3226 IShellLinkW_SetIconLocation(sl,Path,index);
3230 if (!MSI_RecordIsNull(row,11))
3231 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3233 if (!MSI_RecordIsNull(row,12))
3236 buffer = MSI_RecordGetString(row,12);
3237 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3239 IShellLinkW_SetWorkingDirectory(sl,Path);
3243 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3244 IPersistFile_Save(pf,target_file,FALSE);
3246 msi_free(target_file);
3250 IPersistFile_Release( pf );
3252 IShellLinkW_Release( sl );
3254 return ERROR_SUCCESS;
3257 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3262 static const WCHAR Query[] =
3263 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3264 '`','S','h','o','r','t','c','u','t','`',0};
3266 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3267 if (rc != ERROR_SUCCESS)
3268 return ERROR_SUCCESS;
3270 res = CoInitialize( NULL );
3273 ERR("CoInitialize failed\n");
3274 return ERROR_FUNCTION_FAILED;
3277 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3278 msiobj_release(&view->hdr);
3285 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3287 MSIPACKAGE* package = (MSIPACKAGE*)param;
3296 FileName = MSI_RecordGetString(row,1);
3299 ERR("Unable to get FileName\n");
3300 return ERROR_SUCCESS;
3303 FilePath = build_icon_path(package,FileName);
3305 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3307 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3308 FILE_ATTRIBUTE_NORMAL, NULL);
3310 if (the_file == INVALID_HANDLE_VALUE)
3312 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3314 return ERROR_SUCCESS;
3321 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3322 if (rc != ERROR_SUCCESS)
3324 ERR("Failed to get stream\n");
3325 CloseHandle(the_file);
3326 DeleteFileW(FilePath);
3329 WriteFile(the_file,buffer,sz,&write,NULL);
3330 } while (sz == 1024);
3334 CloseHandle(the_file);
3336 uirow = MSI_CreateRecord(1);
3337 MSI_RecordSetStringW(uirow,1,FileName);
3338 ui_actiondata(package,szPublishProduct,uirow);
3339 msiobj_release( &uirow->hdr );
3341 return ERROR_SUCCESS;
3344 static BOOL msi_check_publish(MSIPACKAGE *package)
3346 MSIFEATURE *feature;
3348 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3350 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3358 * 99% of the work done here is only done for
3359 * advertised installs. However this is where the
3360 * Icon table is processed and written out
3361 * so that is what I am going to do here.
3363 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3367 MSISOURCELISTINFO *info;
3369 static const WCHAR Query[]=
3370 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3371 '`','I','c','o','n','`',0};
3372 /* for registry stuff */
3375 HKEY hudkey=0, props=0;
3377 static const WCHAR szProductLanguage[] =
3378 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3379 static const WCHAR szARPProductIcon[] =
3380 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3381 static const WCHAR szProductVersion[] =
3382 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3383 static const WCHAR szSourceList[] =
3384 {'S','o','u','r','c','e','L','i','s','t',0};
3385 static const WCHAR szEmpty[] = {0};
3389 MSIHANDLE hDb, hSumInfo;
3391 /* FIXME: also need to publish if the product is in advertise mode */
3392 if (!msi_check_publish(package))
3393 return ERROR_SUCCESS;
3395 /* write out icon files */
3397 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3398 if (rc == ERROR_SUCCESS)
3400 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3401 msiobj_release(&view->hdr);
3404 /* ok there is a lot more done here but i need to figure out what */
3406 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3407 if (rc != ERROR_SUCCESS)
3410 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3411 if (rc != ERROR_SUCCESS)
3414 rc = RegCreateKeyW(hukey, szSourceList, &source);
3415 if (rc != ERROR_SUCCESS)
3418 RegCloseKey(source);
3420 rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3421 if (rc != ERROR_SUCCESS)
3424 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode,&props,TRUE);
3425 if (rc != ERROR_SUCCESS)
3428 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3429 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3432 langid = msi_get_property_int( package, szProductLanguage, 0 );
3433 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3435 buffer = msi_dup_property( package, szARPProductIcon );
3438 LPWSTR path = build_icon_path(package,buffer);
3439 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3444 buffer = msi_dup_property( package, szProductVersion );
3447 DWORD verdword = msi_version_str_to_dword(buffer);
3448 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3452 buffer = strrchrW( package->PackagePath, '\\') + 1;
3453 rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3454 MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
3455 INSTALLPROPERTY_PACKAGENAMEW, buffer );
3456 if (rc != ERROR_SUCCESS)
3459 rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3460 MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
3461 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty );
3462 if (rc != ERROR_SUCCESS)
3465 rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3466 MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
3467 INSTALLPROPERTY_DISKPROMPTW, szEmpty );
3468 if (rc != ERROR_SUCCESS)
3471 /* FIXME: Need to write more keys to the user registry */
3473 hDb= alloc_msihandle( &package->db->hdr );
3475 rc = ERROR_NOT_ENOUGH_MEMORY;
3478 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3479 MsiCloseHandle(hDb);
3480 if (rc == ERROR_SUCCESS)
3482 WCHAR guidbuffer[0x200];
3484 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3486 if (rc == ERROR_SUCCESS)
3488 WCHAR squashed[GUID_SIZE];
3489 /* for now we only care about the first guid */
3490 LPWSTR ptr = strchrW(guidbuffer,';');
3492 squash_guid(guidbuffer,squashed);
3493 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3497 ERR("Unable to query Revision_Number...\n");
3500 MsiCloseHandle(hSumInfo);
3504 ERR("Unable to open Summary Information\n");
3508 /* publish the SourceList info */
3509 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3511 MsiSourceListSetInfoW(package->ProductCode, NULL,
3512 info->context, info->options,
3513 info->property, info->value);
3516 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3518 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3519 disk->context, disk->options,
3520 disk->disk_id, disk->volume_label, disk->disk_prompt);
3526 RegCloseKey(hudkey);
3532 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3534 MSIPACKAGE *package = (MSIPACKAGE*)param;
3535 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3536 LPWSTR deformated_section, deformated_key, deformated_value;
3537 LPWSTR folder, fullname = NULL;
3541 static const WCHAR szWindowsFolder[] =
3542 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3544 component = MSI_RecordGetString(row, 8);
3545 comp = get_loaded_component(package,component);
3547 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3549 TRACE("Skipping ini file due to disabled component %s\n",
3550 debugstr_w(component));
3552 comp->Action = comp->Installed;
3554 return ERROR_SUCCESS;
3557 comp->Action = INSTALLSTATE_LOCAL;
3559 identifier = MSI_RecordGetString(row,1);
3560 filename = MSI_RecordGetString(row,2);
3561 dirproperty = MSI_RecordGetString(row,3);
3562 section = MSI_RecordGetString(row,4);
3563 key = MSI_RecordGetString(row,5);
3564 value = MSI_RecordGetString(row,6);
3565 action = MSI_RecordGetInteger(row,7);
3567 deformat_string(package,section,&deformated_section);
3568 deformat_string(package,key,&deformated_key);
3569 deformat_string(package,value,&deformated_value);
3573 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3575 folder = msi_dup_property( package, dirproperty );
3578 folder = msi_dup_property( package, szWindowsFolder );
3582 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3586 fullname = build_directory_name(2, folder, filename);
3590 TRACE("Adding value %s to section %s in %s\n",
3591 debugstr_w(deformated_key), debugstr_w(deformated_section),
3592 debugstr_w(fullname));
3593 WritePrivateProfileStringW(deformated_section, deformated_key,
3594 deformated_value, fullname);
3596 else if (action == 1)
3599 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3600 returned, 10, fullname);
3601 if (returned[0] == 0)
3603 TRACE("Adding value %s to section %s in %s\n",
3604 debugstr_w(deformated_key), debugstr_w(deformated_section),
3605 debugstr_w(fullname));
3607 WritePrivateProfileStringW(deformated_section, deformated_key,
3608 deformated_value, fullname);
3611 else if (action == 3)
3612 FIXME("Append to existing section not yet implemented\n");
3614 uirow = MSI_CreateRecord(4);
3615 MSI_RecordSetStringW(uirow,1,identifier);
3616 MSI_RecordSetStringW(uirow,2,deformated_section);
3617 MSI_RecordSetStringW(uirow,3,deformated_key);
3618 MSI_RecordSetStringW(uirow,4,deformated_value);
3619 ui_actiondata(package,szWriteIniValues,uirow);
3620 msiobj_release( &uirow->hdr );
3624 msi_free(deformated_key);
3625 msi_free(deformated_value);
3626 msi_free(deformated_section);
3627 return ERROR_SUCCESS;
3630 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3634 static const WCHAR ExecSeqQuery[] =
3635 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3636 '`','I','n','i','F','i','l','e','`',0};
3638 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3639 if (rc != ERROR_SUCCESS)
3641 TRACE("no IniFile table\n");
3642 return ERROR_SUCCESS;
3645 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3646 msiobj_release(&view->hdr);
3650 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3652 MSIPACKAGE *package = (MSIPACKAGE*)param;
3657 static const WCHAR ExeStr[] =
3658 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3659 static const WCHAR close[] = {'\"',0};
3661 PROCESS_INFORMATION info;
3666 memset(&si,0,sizeof(STARTUPINFOW));
3668 filename = MSI_RecordGetString(row,1);
3669 file = get_loaded_file( package, filename );
3673 ERR("Unable to find file id %s\n",debugstr_w(filename));
3674 return ERROR_SUCCESS;
3677 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3679 FullName = msi_alloc(len*sizeof(WCHAR));
3680 strcpyW(FullName,ExeStr);
3681 strcatW( FullName, file->TargetPath );
3682 strcatW(FullName,close);
3684 TRACE("Registering %s\n",debugstr_w(FullName));
3685 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3689 msi_dialog_check_messages(info.hProcess);
3694 uirow = MSI_CreateRecord( 2 );
3695 uipath = strdupW( file->TargetPath );
3696 p = strrchrW(uipath,'\\');
3699 MSI_RecordSetStringW( uirow, 1, &p[1] );
3700 MSI_RecordSetStringW( uirow, 2, uipath);
3701 ui_actiondata( package, szSelfRegModules, uirow);
3702 msiobj_release( &uirow->hdr );
3704 /* FIXME: call ui_progress? */
3706 return ERROR_SUCCESS;
3709 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3713 static const WCHAR ExecSeqQuery[] =
3714 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3715 '`','S','e','l','f','R','e','g','`',0};
3717 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3718 if (rc != ERROR_SUCCESS)
3720 TRACE("no SelfReg table\n");
3721 return ERROR_SUCCESS;
3724 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3725 msiobj_release(&view->hdr);
3727 return ERROR_SUCCESS;
3730 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3732 MSIFEATURE *feature;
3738 if (!msi_check_publish(package))
3739 return ERROR_SUCCESS;
3741 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3742 if (rc != ERROR_SUCCESS)
3745 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3746 if (rc != ERROR_SUCCESS)
3749 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &userdata, TRUE);
3750 if (rc != ERROR_SUCCESS)
3753 /* here the guids are base 85 encoded */
3754 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3760 BOOL absent = FALSE;
3763 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3764 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3765 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3769 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3773 if (feature->Feature_Parent)
3774 size += strlenW( feature->Feature_Parent )+2;
3776 data = msi_alloc(size * sizeof(WCHAR));
3779 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3781 MSICOMPONENT* component = cl->component;
3785 if (component->ComponentId)
3787 TRACE("From %s\n",debugstr_w(component->ComponentId));
3788 CLSIDFromString(component->ComponentId, &clsid);
3789 encode_base85_guid(&clsid,buf);
3790 TRACE("to %s\n",debugstr_w(buf));
3795 if (feature->Feature_Parent)
3797 static const WCHAR sep[] = {'\2',0};
3799 strcatW(data,feature->Feature_Parent);
3802 msi_reg_set_val_str( hkey, feature->Feature, data );
3803 msi_reg_set_val_str( userdata, feature->Feature, data );
3807 if (feature->Feature_Parent)
3808 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3811 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3812 (LPBYTE)feature->Feature_Parent,size);
3816 size += 2*sizeof(WCHAR);
3817 data = msi_alloc(size);
3820 if (feature->Feature_Parent)
3821 strcpyW( &data[1], feature->Feature_Parent );
3822 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3828 uirow = MSI_CreateRecord( 1 );
3829 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3830 ui_actiondata( package, szPublishFeatures, uirow);
3831 msiobj_release( &uirow->hdr );
3832 /* FIXME: call ui_progress? */
3841 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3846 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3848 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3849 if (r == ERROR_SUCCESS)
3851 RegDeleteValueW(hkey, feature->Feature);
3855 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3856 if (r == ERROR_SUCCESS)
3858 RegDeleteValueW(hkey, feature->Feature);
3862 return ERROR_SUCCESS;
3865 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3867 MSIFEATURE *feature;
3869 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3871 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3878 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3880 MSIFEATURE *feature;
3882 if (!msi_check_unpublish(package))
3883 return ERROR_SUCCESS;
3885 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3887 msi_unpublish_feature(package, feature);
3890 return ERROR_SUCCESS;
3893 static UINT msi_get_local_package_name( LPWSTR path )
3895 static const WCHAR szInstaller[] = {
3896 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3897 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3901 time = GetTickCount();
3902 GetWindowsDirectoryW( path, MAX_PATH );
3903 lstrcatW( path, szInstaller );
3904 CreateDirectoryW( path, NULL );
3906 len = lstrlenW(path);
3907 for (i=0; i<0x10000; i++)
3909 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3910 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3911 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3912 if (handle != INVALID_HANDLE_VALUE)
3914 CloseHandle(handle);
3917 if (GetLastError() != ERROR_FILE_EXISTS &&
3918 GetLastError() != ERROR_SHARING_VIOLATION)
3919 return ERROR_FUNCTION_FAILED;
3922 return ERROR_SUCCESS;
3925 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3927 WCHAR packagefile[MAX_PATH];
3931 r = msi_get_local_package_name( packagefile );
3932 if (r != ERROR_SUCCESS)
3935 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3937 r = CopyFileW( package->db->path, packagefile, FALSE);
3941 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3942 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
3943 return ERROR_FUNCTION_FAILED;
3946 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3948 r = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3949 if (r != ERROR_SUCCESS)
3952 msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3954 return ERROR_SUCCESS;
3957 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3959 LPWSTR prop, val, key;
3960 static const LPCSTR propval[] = {
3961 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3962 "ARPCONTACT", "Contact",
3963 "ARPCOMMENTS", "Comments",
3964 "ProductName", "DisplayName",
3965 "ProductVersion", "DisplayVersion",
3966 "ARPHELPLINK", "HelpLink",
3967 "ARPHELPTELEPHONE", "HelpTelephone",
3968 "ARPINSTALLLOCATION", "InstallLocation",
3969 "SourceDir", "InstallSource",
3970 "Manufacturer", "Publisher",
3971 "ARPREADME", "Readme",
3973 "ARPURLINFOABOUT", "URLInfoAbout",
3974 "ARPURLUPDATEINFO", "URLUpdateInfo",
3977 const LPCSTR *p = propval;
3981 prop = strdupAtoW( *p++ );
3982 key = strdupAtoW( *p++ );
3983 val = msi_dup_property( package, prop );
3984 msi_reg_set_val_str( hkey, key, val );
3989 return ERROR_SUCCESS;
3992 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3995 HKEY hudkey=0, props=0;
3996 LPWSTR buffer = NULL;
3999 static const WCHAR szWindowsInstaller[] =
4000 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4001 static const WCHAR szUpgradeCode[] =
4002 {'U','p','g','r','a','d','e','C','o','d','e',0};
4003 static const WCHAR modpath_fmt[] =
4004 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4005 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4006 static const WCHAR szModifyPath[] =
4007 {'M','o','d','i','f','y','P','a','t','h',0};
4008 static const WCHAR szUninstallString[] =
4009 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4010 static const WCHAR szEstimatedSize[] =
4011 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4012 static const WCHAR szProductLanguage[] =
4013 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4014 static const WCHAR szProductVersion[] =
4015 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4018 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4019 LPWSTR upgrade_code;
4022 /* FIXME: also need to publish if the product is in advertise mode */
4023 if (!msi_check_publish(package))
4024 return ERROR_SUCCESS;
4026 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4027 if (rc != ERROR_SUCCESS)
4030 /* dump all the info i can grab */
4031 /* FIXME: Flesh out more information */
4033 msi_write_uninstall_property_vals( package, hkey );
4035 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
4037 msi_make_package_local( package, hkey );
4039 /* do ModifyPath and UninstallString */
4040 size = deformat_string(package,modpath_fmt,&buffer);
4041 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4042 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4045 /* FIXME: Write real Estimated Size when we have it */
4046 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
4048 GetLocalTime(&systime);
4049 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
4050 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
4052 langid = msi_get_property_int( package, szProductLanguage, 0 );
4053 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
4055 buffer = msi_dup_property( package, szProductVersion );
4058 DWORD verdword = msi_version_str_to_dword(buffer);
4060 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
4061 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
4062 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
4066 /* Handle Upgrade Codes */
4067 upgrade_code = msi_dup_property( package, szUpgradeCode );
4072 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4073 squash_guid(package->ProductCode,squashed);
4074 msi_reg_set_val_str( hkey2, squashed, NULL );
4076 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4077 squash_guid(package->ProductCode,squashed);
4078 msi_reg_set_val_str( hkey2, squashed, NULL );
4081 msi_free(upgrade_code);
4086 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
4087 if (rc != ERROR_SUCCESS)
4090 RegCloseKey(hudkey);
4092 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
4093 if (rc != ERROR_SUCCESS)
4096 msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
4099 return ERROR_SUCCESS;
4102 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4104 return execute_script(package,INSTALL_SCRIPT);
4107 static UINT msi_unpublish_product(MSIPACKAGE *package)
4109 LPWSTR remove = NULL;
4110 LPWSTR *features = NULL;
4111 BOOL full_uninstall = TRUE;
4112 MSIFEATURE *feature;
4114 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4115 static const WCHAR szAll[] = {'A','L','L',0};
4117 remove = msi_dup_property(package, szRemove);
4119 return ERROR_SUCCESS;
4121 features = msi_split_string(remove, ',');
4125 ERR("REMOVE feature list is empty!\n");
4126 return ERROR_FUNCTION_FAILED;
4129 if (!lstrcmpW(features[0], szAll))
4130 full_uninstall = TRUE;
4133 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4135 if (feature->Action != INSTALLSTATE_ABSENT)
4136 full_uninstall = FALSE;
4140 if (!full_uninstall)
4143 MSIREG_DeleteProductKey(package->ProductCode);
4144 MSIREG_DeleteUserProductKey(package->ProductCode);
4145 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4146 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4147 MSIREG_DeleteUninstallKey(package->ProductCode);
4152 return ERROR_SUCCESS;
4155 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4159 rc = msi_unpublish_product(package);
4160 if (rc != ERROR_SUCCESS)
4163 /* turn off scheduling */
4164 package->script->CurrentlyScripting= FALSE;
4166 /* first do the same as an InstallExecute */
4167 rc = ACTION_InstallExecute(package);
4168 if (rc != ERROR_SUCCESS)
4171 /* then handle Commit Actions */
4172 rc = execute_script(package,COMMIT_SCRIPT);
4177 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4179 static const WCHAR RunOnce[] = {
4180 'S','o','f','t','w','a','r','e','\\',
4181 'M','i','c','r','o','s','o','f','t','\\',
4182 'W','i','n','d','o','w','s','\\',
4183 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4184 'R','u','n','O','n','c','e',0};
4185 static const WCHAR InstallRunOnce[] = {
4186 'S','o','f','t','w','a','r','e','\\',
4187 'M','i','c','r','o','s','o','f','t','\\',
4188 'W','i','n','d','o','w','s','\\',
4189 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4190 'I','n','s','t','a','l','l','e','r','\\',
4191 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4193 static const WCHAR msiexec_fmt[] = {
4195 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4196 '\"','%','s','\"',0};
4197 static const WCHAR install_fmt[] = {
4198 '/','I',' ','\"','%','s','\"',' ',
4199 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4200 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4201 WCHAR buffer[256], sysdir[MAX_PATH];
4203 WCHAR squished_pc[100];
4205 squash_guid(package->ProductCode,squished_pc);
4207 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4208 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4209 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4212 msi_reg_set_val_str( hkey, squished_pc, buffer );
4215 TRACE("Reboot command %s\n",debugstr_w(buffer));
4217 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4218 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4220 msi_reg_set_val_str( hkey, squished_pc, buffer );
4223 return ERROR_INSTALL_SUSPEND;
4226 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4232 * We are currently doing what should be done here in the top level Install
4233 * however for Administrative and uninstalls this step will be needed
4235 if (!package->PackagePath)
4236 return ERROR_SUCCESS;
4238 msi_set_sourcedir_props(package, TRUE);
4240 attrib = GetFileAttributesW(package->db->path);
4241 if (attrib == INVALID_FILE_ATTRIBUTES)
4247 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4248 MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
4249 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4250 if (rc == ERROR_MORE_DATA)
4252 prompt = msi_alloc(size * sizeof(WCHAR));
4253 MsiSourceListGetInfoW(package->ProductCode, NULL,
4254 MSIINSTALLCONTEXT_USERUNMANAGED, MSICODE_PRODUCT,
4255 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4258 prompt = strdupW(package->db->path);
4260 msg = generate_error_string(package,1302,1,prompt);
4261 while(attrib == INVALID_FILE_ATTRIBUTES)
4263 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4266 rc = ERROR_INSTALL_USEREXIT;
4269 attrib = GetFileAttributesW(package->db->path);
4275 return ERROR_SUCCESS;
4280 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4287 static const WCHAR szPropKeys[][80] =
4289 {'P','r','o','d','u','c','t','I','D',0},
4290 {'U','S','E','R','N','A','M','E',0},
4291 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4295 static const WCHAR szRegKeys[][80] =
4297 {'P','r','o','d','u','c','t','I','D',0},
4298 {'R','e','g','O','w','n','e','r',0},
4299 {'R','e','g','C','o','m','p','a','n','y',0},
4303 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4305 return ERROR_SUCCESS;
4307 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4308 if (rc != ERROR_SUCCESS)
4311 for( i = 0; szPropKeys[i][0]; i++ )
4313 buffer = msi_dup_property( package, szPropKeys[i] );
4314 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4319 msi_free(productid);
4322 /* FIXME: call ui_actiondata */
4324 return ERROR_SUCCESS;
4328 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4332 package->script->InWhatSequence |= SEQUENCE_EXEC;
4333 rc = ACTION_ProcessExecSequence(package,FALSE);
4338 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4340 MSIPACKAGE *package = (MSIPACKAGE*)param;
4341 LPCWSTR compgroupid=NULL;
4342 LPCWSTR feature=NULL;
4343 LPCWSTR text = NULL;
4344 LPCWSTR qualifier = NULL;
4345 LPCWSTR component = NULL;
4346 LPWSTR advertise = NULL;
4347 LPWSTR output = NULL;
4349 UINT rc = ERROR_SUCCESS;
4354 component = MSI_RecordGetString(rec,3);
4355 comp = get_loaded_component(package,component);
4357 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4358 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4359 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4361 TRACE("Skipping: Component %s not scheduled for install\n",
4362 debugstr_w(component));
4364 return ERROR_SUCCESS;
4367 compgroupid = MSI_RecordGetString(rec,1);
4368 qualifier = MSI_RecordGetString(rec,2);
4370 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4371 if (rc != ERROR_SUCCESS)
4374 text = MSI_RecordGetString(rec,4);
4375 feature = MSI_RecordGetString(rec,5);
4377 advertise = create_component_advertise_string(package, comp, feature);
4379 sz = strlenW(advertise);
4382 sz += lstrlenW(text);
4385 sz *= sizeof(WCHAR);
4387 output = msi_alloc_zero(sz);
4388 strcpyW(output,advertise);
4389 msi_free(advertise);
4392 strcatW(output,text);
4394 msi_reg_set_val_multi_str( hkey, qualifier, output );
4401 uirow = MSI_CreateRecord( 2 );
4402 MSI_RecordSetStringW( uirow, 1, compgroupid );
4403 MSI_RecordSetStringW( uirow, 2, qualifier);
4404 ui_actiondata( package, szPublishComponents, uirow);
4405 msiobj_release( &uirow->hdr );
4406 /* FIXME: call ui_progress? */
4412 * At present I am ignorning the advertised components part of this and only
4413 * focusing on the qualified component sets
4415 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4419 static const WCHAR ExecSeqQuery[] =
4420 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4421 '`','P','u','b','l','i','s','h',
4422 'C','o','m','p','o','n','e','n','t','`',0};
4424 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4425 if (rc != ERROR_SUCCESS)
4426 return ERROR_SUCCESS;
4428 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4429 msiobj_release(&view->hdr);
4434 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4436 MSIPACKAGE *package = (MSIPACKAGE*)param;
4439 SC_HANDLE hscm, service = NULL;
4440 LPCWSTR name, disp, comp, depends, pass;
4441 LPCWSTR load_order, serv_name, key;
4442 DWORD serv_type, start_type;
4445 static const WCHAR query[] =
4446 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4447 '`','C','o','m','p','o','n','e','n','t','`',' ',
4448 'W','H','E','R','E',' ',
4449 '`','C','o','m','p','o','n','e','n','t','`',' ',
4450 '=','\'','%','s','\'',0};
4452 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4455 ERR("Failed to open the SC Manager!\n");
4459 start_type = MSI_RecordGetInteger(rec, 5);
4460 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4463 depends = MSI_RecordGetString(rec, 8);
4464 if (depends && *depends)
4465 FIXME("Dependency list unhandled!\n");
4467 name = MSI_RecordGetString(rec, 2);
4468 disp = MSI_RecordGetString(rec, 3);
4469 serv_type = MSI_RecordGetInteger(rec, 4);
4470 err_control = MSI_RecordGetInteger(rec, 6);
4471 load_order = MSI_RecordGetString(rec, 7);
4472 serv_name = MSI_RecordGetString(rec, 9);
4473 pass = MSI_RecordGetString(rec, 10);
4474 comp = MSI_RecordGetString(rec, 12);
4476 /* fetch the service path */
4477 row = MSI_QueryGetRecord(package->db, query, comp);
4480 ERR("Control query failed!\n");
4484 key = MSI_RecordGetString(row, 6);
4486 file = get_loaded_file(package, key);
4487 msiobj_release(&row->hdr);
4490 ERR("Failed to load the service file\n");
4494 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4495 start_type, err_control, file->TargetPath,
4496 load_order, NULL, NULL, serv_name, pass);
4499 if (GetLastError() != ERROR_SERVICE_EXISTS)
4500 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4504 CloseServiceHandle(service);
4505 CloseServiceHandle(hscm);
4507 return ERROR_SUCCESS;
4510 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4514 static const WCHAR ExecSeqQuery[] =
4515 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4516 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4518 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4519 if (rc != ERROR_SUCCESS)
4520 return ERROR_SUCCESS;
4522 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4523 msiobj_release(&view->hdr);
4528 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4529 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4531 LPCWSTR *vector, *temp_vector;
4535 static const WCHAR separator[] = {'[','~',']',0};
4538 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4543 vector = msi_alloc(sizeof(LPWSTR));
4551 vector[*numargs - 1] = p;
4553 if ((q = strstrW(p, separator)))
4557 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4563 vector = temp_vector;
4572 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4574 MSIPACKAGE *package = (MSIPACKAGE *)param;
4576 SC_HANDLE scm, service = NULL;
4577 LPCWSTR name, *vector = NULL;
4579 DWORD event, numargs;
4580 UINT r = ERROR_FUNCTION_FAILED;
4582 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4583 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4584 return ERROR_SUCCESS;
4586 name = MSI_RecordGetString(rec, 2);
4587 event = MSI_RecordGetInteger(rec, 3);
4588 args = strdupW(MSI_RecordGetString(rec, 4));
4590 if (!(event & msidbServiceControlEventStart))
4591 return ERROR_SUCCESS;
4593 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4596 ERR("Failed to open the service control manager\n");
4600 service = OpenServiceW(scm, name, SERVICE_START);
4603 ERR("Failed to open service %s\n", debugstr_w(name));
4607 vector = msi_service_args_to_vector(args, &numargs);
4609 if (!StartServiceW(service, numargs, vector))
4611 ERR("Failed to start service %s\n", debugstr_w(name));
4618 CloseServiceHandle(service);
4619 CloseServiceHandle(scm);
4626 static UINT ACTION_StartServices( MSIPACKAGE *package )
4631 static const WCHAR query[] = {
4632 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4633 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4635 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4636 if (rc != ERROR_SUCCESS)
4637 return ERROR_SUCCESS;
4639 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4640 msiobj_release(&view->hdr);
4645 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4649 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4651 if (!lstrcmpW(file->File, filename))
4658 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4660 MSIPACKAGE *package = (MSIPACKAGE*)param;
4661 LPWSTR driver, driver_path, ptr;
4662 WCHAR outpath[MAX_PATH];
4663 MSIFILE *driver_file, *setup_file;
4666 UINT r = ERROR_SUCCESS;
4668 static const WCHAR driver_fmt[] = {
4669 'D','r','i','v','e','r','=','%','s',0};
4670 static const WCHAR setup_fmt[] = {
4671 'S','e','t','u','p','=','%','s',0};
4672 static const WCHAR usage_fmt[] = {
4673 'F','i','l','e','U','s','a','g','e','=','1',0};
4675 desc = MSI_RecordGetString(rec, 3);
4677 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4678 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4680 if (!driver_file || !setup_file)
4682 ERR("ODBC Driver entry not found!\n");
4683 return ERROR_FUNCTION_FAILED;
4686 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4687 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4688 lstrlenW(usage_fmt) + 1;
4689 driver = msi_alloc(len * sizeof(WCHAR));
4691 return ERROR_OUTOFMEMORY;
4694 lstrcpyW(ptr, desc);
4695 ptr += lstrlenW(ptr) + 1;
4697 sprintfW(ptr, driver_fmt, driver_file->FileName);
4698 ptr += lstrlenW(ptr) + 1;
4700 sprintfW(ptr, setup_fmt, setup_file->FileName);
4701 ptr += lstrlenW(ptr) + 1;
4703 lstrcpyW(ptr, usage_fmt);
4704 ptr += lstrlenW(ptr) + 1;
4707 driver_path = strdupW(driver_file->TargetPath);
4708 ptr = strrchrW(driver_path, '\\');
4709 if (ptr) *ptr = '\0';
4711 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4712 NULL, ODBC_INSTALL_COMPLETE, &usage))
4714 ERR("Failed to install SQL driver!\n");
4715 r = ERROR_FUNCTION_FAILED;
4719 msi_free(driver_path);
4724 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4726 MSIPACKAGE *package = (MSIPACKAGE*)param;
4727 LPWSTR translator, translator_path, ptr;
4728 WCHAR outpath[MAX_PATH];
4729 MSIFILE *translator_file, *setup_file;
4732 UINT r = ERROR_SUCCESS;
4734 static const WCHAR translator_fmt[] = {
4735 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4736 static const WCHAR setup_fmt[] = {
4737 'S','e','t','u','p','=','%','s',0};
4739 desc = MSI_RecordGetString(rec, 3);
4741 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4742 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4744 if (!translator_file || !setup_file)
4746 ERR("ODBC Translator entry not found!\n");
4747 return ERROR_FUNCTION_FAILED;
4750 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4751 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4752 translator = msi_alloc(len * sizeof(WCHAR));
4754 return ERROR_OUTOFMEMORY;
4757 lstrcpyW(ptr, desc);
4758 ptr += lstrlenW(ptr) + 1;
4760 sprintfW(ptr, translator_fmt, translator_file->FileName);
4761 ptr += lstrlenW(ptr) + 1;
4763 sprintfW(ptr, setup_fmt, setup_file->FileName);
4764 ptr += lstrlenW(ptr) + 1;
4767 translator_path = strdupW(translator_file->TargetPath);
4768 ptr = strrchrW(translator_path, '\\');
4769 if (ptr) *ptr = '\0';
4771 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4772 NULL, ODBC_INSTALL_COMPLETE, &usage))
4774 ERR("Failed to install SQL translator!\n");
4775 r = ERROR_FUNCTION_FAILED;
4778 msi_free(translator);
4779 msi_free(translator_path);
4784 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4787 LPCWSTR desc, driver;
4788 WORD request = ODBC_ADD_SYS_DSN;
4791 UINT r = ERROR_SUCCESS;
4793 static const WCHAR attrs_fmt[] = {
4794 'D','S','N','=','%','s',0 };
4796 desc = MSI_RecordGetString(rec, 3);
4797 driver = MSI_RecordGetString(rec, 4);
4798 registration = MSI_RecordGetInteger(rec, 5);
4800 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4801 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4803 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4804 attrs = msi_alloc(len * sizeof(WCHAR));
4806 return ERROR_OUTOFMEMORY;
4808 sprintfW(attrs, attrs_fmt, desc);
4809 attrs[len - 1] = '\0';
4811 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4813 ERR("Failed to install SQL data source!\n");
4814 r = ERROR_FUNCTION_FAILED;
4822 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4827 static const WCHAR driver_query[] = {
4828 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4829 'O','D','B','C','D','r','i','v','e','r',0 };
4831 static const WCHAR translator_query[] = {
4832 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4833 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4835 static const WCHAR source_query[] = {
4836 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4837 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4839 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4840 if (rc != ERROR_SUCCESS)
4841 return ERROR_SUCCESS;
4843 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4844 msiobj_release(&view->hdr);
4846 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4847 if (rc != ERROR_SUCCESS)
4848 return ERROR_SUCCESS;
4850 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4851 msiobj_release(&view->hdr);
4853 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4854 if (rc != ERROR_SUCCESS)
4855 return ERROR_SUCCESS;
4857 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4858 msiobj_release(&view->hdr);
4863 #define ENV_ACT_SETALWAYS 0x1
4864 #define ENV_ACT_SETABSENT 0x2
4865 #define ENV_ACT_REMOVE 0x4
4866 #define ENV_ACT_REMOVEMATCH 0x8
4868 #define ENV_MOD_MACHINE 0x20000000
4869 #define ENV_MOD_APPEND 0x40000000
4870 #define ENV_MOD_PREFIX 0x80000000
4871 #define ENV_MOD_MASK 0xC0000000
4873 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4875 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4877 LPCWSTR cptr = *name;
4878 LPCWSTR ptr = *value;
4880 static const WCHAR prefix[] = {'[','~',']',0};
4881 static const int prefix_len = 3;
4887 *flags |= ENV_ACT_SETALWAYS;
4888 else if (*cptr == '+')
4889 *flags |= ENV_ACT_SETABSENT;
4890 else if (*cptr == '-')
4891 *flags |= ENV_ACT_REMOVE;
4892 else if (*cptr == '!')
4893 *flags |= ENV_ACT_REMOVEMATCH;
4894 else if (*cptr == '*')
4895 *flags |= ENV_MOD_MACHINE;
4905 ERR("Missing environment variable\n");
4906 return ERROR_FUNCTION_FAILED;
4909 if (!strncmpW(ptr, prefix, prefix_len))
4911 *flags |= ENV_MOD_APPEND;
4912 *value += lstrlenW(prefix);
4914 else if (lstrlenW(*value) >= prefix_len)
4916 ptr += lstrlenW(ptr) - prefix_len;
4917 if (!lstrcmpW(ptr, prefix))
4919 *flags |= ENV_MOD_PREFIX;
4920 /* the "[~]" will be removed by deformat_string */;
4925 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4926 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4927 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4928 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4930 ERR("Invalid flags: %08x\n", *flags);
4931 return ERROR_FUNCTION_FAILED;
4934 return ERROR_SUCCESS;
4937 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4939 MSIPACKAGE *package = param;
4940 LPCWSTR name, value, comp;
4941 LPWSTR data = NULL, newval = NULL;
4942 LPWSTR deformatted = NULL, ptr;
4943 DWORD flags, type, size;
4945 HKEY env = NULL, root;
4946 LPCWSTR environment;
4948 static const WCHAR user_env[] =
4949 {'E','n','v','i','r','o','n','m','e','n','t',0};
4950 static const WCHAR machine_env[] =
4951 {'S','y','s','t','e','m','\\',
4952 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4953 'C','o','n','t','r','o','l','\\',
4954 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4955 'E','n','v','i','r','o','n','m','e','n','t',0};
4956 static const WCHAR semicolon[] = {';',0};
4958 name = MSI_RecordGetString(rec, 2);
4959 value = MSI_RecordGetString(rec, 3);
4960 comp = MSI_RecordGetString(rec, 4);
4962 res = env_set_flags(&name, &value, &flags);
4963 if (res != ERROR_SUCCESS)
4966 deformat_string(package, value, &deformatted);
4969 res = ERROR_OUTOFMEMORY;
4973 value = deformatted;
4975 if (flags & ENV_MOD_MACHINE)
4977 environment = machine_env;
4978 root = HKEY_LOCAL_MACHINE;
4982 environment = user_env;
4983 root = HKEY_CURRENT_USER;
4986 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
4987 KEY_ALL_ACCESS, NULL, &env, NULL);
4988 if (res != ERROR_SUCCESS)
4991 if (flags & ENV_ACT_REMOVE)
4992 FIXME("Not removing environment variable on uninstall!\n");
4995 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
4996 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
4997 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5000 if (res != ERROR_FILE_NOT_FOUND)
5002 if (flags & ENV_ACT_SETABSENT)
5004 res = ERROR_SUCCESS;
5008 data = msi_alloc(size);
5012 return ERROR_OUTOFMEMORY;
5015 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5016 if (res != ERROR_SUCCESS)
5019 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5021 res = RegDeleteKeyW(env, name);
5025 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5026 newval = msi_alloc(size);
5030 res = ERROR_OUTOFMEMORY;
5034 if (!(flags & ENV_MOD_MASK))
5035 lstrcpyW(newval, value);
5038 if (flags & ENV_MOD_PREFIX)
5040 lstrcpyW(newval, value);
5041 lstrcatW(newval, semicolon);
5042 ptr = newval + lstrlenW(value) + 1;
5045 lstrcpyW(ptr, data);
5047 if (flags & ENV_MOD_APPEND)
5049 lstrcatW(newval, semicolon);
5050 lstrcatW(newval, value);
5056 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5057 newval = msi_alloc(size);
5060 res = ERROR_OUTOFMEMORY;
5064 lstrcpyW(newval, value);
5067 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5068 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5071 if (env) RegCloseKey(env);
5072 msi_free(deformatted);
5078 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5082 static const WCHAR ExecSeqQuery[] =
5083 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5084 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5085 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5086 if (rc != ERROR_SUCCESS)
5087 return ERROR_SUCCESS;
5089 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5090 msiobj_release(&view->hdr);
5095 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5106 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5110 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5111 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5113 WARN("Source or dest is directory, not moving\n");
5117 if (options == msidbMoveFileOptionsMove)
5119 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5120 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5123 WARN("MoveFile failed: %d\n", GetLastError());
5129 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5130 ret = CopyFileW(source, dest, FALSE);
5133 WARN("CopyFile failed: %d\n", GetLastError());
5141 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5144 DWORD dirlen, pathlen;
5146 ptr = strrchrW(wildcard, '\\');
5147 dirlen = ptr - wildcard + 1;
5149 pathlen = dirlen + lstrlenW(filename) + 1;
5150 path = msi_alloc(pathlen * sizeof(WCHAR));
5152 lstrcpynW(path, wildcard, dirlen + 1);
5153 lstrcatW(path, filename);
5158 static void free_file_entry(FILE_LIST *file)
5160 msi_free(file->source);
5161 msi_free(file->dest);
5165 static void free_list(FILE_LIST *list)
5167 while (!list_empty(&list->entry))
5169 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5171 list_remove(&file->entry);
5172 free_file_entry(file);
5176 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5178 FILE_LIST *new, *file;
5179 LPWSTR ptr, filename;
5182 new = msi_alloc_zero(sizeof(FILE_LIST));
5186 new->source = strdupW(source);
5187 ptr = strrchrW(dest, '\\') + 1;
5188 filename = strrchrW(new->source, '\\') + 1;
5190 new->sourcename = filename;
5193 new->destname = ptr;
5195 new->destname = new->sourcename;
5197 size = (ptr - dest) + lstrlenW(filename) + 1;
5198 new->dest = msi_alloc(size * sizeof(WCHAR));
5201 free_file_entry(new);
5205 lstrcpynW(new->dest, dest, ptr - dest + 1);
5206 lstrcatW(new->dest, filename);
5208 if (list_empty(&files->entry))
5210 list_add_head(&files->entry, &new->entry);
5214 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5216 if (lstrcmpW(source, file->source) < 0)
5218 list_add_before(&file->entry, &new->entry);
5223 list_add_after(&file->entry, &new->entry);
5227 BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5229 WIN32_FIND_DATAW wfd;
5233 FILE_LIST files, *file;
5236 hfile = FindFirstFileW(source, &wfd);
5237 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5239 list_init(&files.entry);
5241 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5243 if (is_dot_dir(wfd.cFileName)) continue;
5245 path = wildcard_to_file(source, wfd.cFileName);
5252 add_wildcard(&files, path, dest);
5256 /* only the first wildcard match gets renamed to dest */
5257 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5258 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5259 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5266 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5268 while (!list_empty(&files.entry))
5270 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5272 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5274 list_remove(&file->entry);
5275 free_file_entry(file);
5286 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5288 MSIPACKAGE *package = param;
5290 LPCWSTR sourcename, destname;
5291 LPWSTR sourcedir = NULL, destdir = NULL;
5292 LPWSTR source = NULL, dest = NULL;
5295 BOOL ret, wildcards;
5297 static const WCHAR backslash[] = {'\\',0};
5299 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5300 if (!comp || !comp->Enabled ||
5301 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5303 TRACE("Component not set for install, not moving file\n");
5304 return ERROR_SUCCESS;
5307 sourcename = MSI_RecordGetString(rec, 3);
5308 destname = MSI_RecordGetString(rec, 4);
5309 options = MSI_RecordGetInteger(rec, 7);
5311 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5315 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5321 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5324 source = strdupW(sourcedir);
5330 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5331 source = msi_alloc(size * sizeof(WCHAR));
5335 lstrcpyW(source, sourcedir);
5336 if (source[lstrlenW(source) - 1] != '\\')
5337 lstrcatW(source, backslash);
5338 lstrcatW(source, sourcename);
5341 wildcards = strchrW(source, '*') || strchrW(source, '?');
5343 if (!destname && !wildcards)
5345 destname = strdupW(sourcename);
5352 size = lstrlenW(destname);
5354 size += lstrlenW(destdir) + 2;
5355 dest = msi_alloc(size * sizeof(WCHAR));
5359 lstrcpyW(dest, destdir);
5360 if (dest[lstrlenW(dest) - 1] != '\\')
5361 lstrcatW(dest, backslash);
5364 lstrcatW(dest, destname);
5366 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5368 ret = CreateDirectoryW(destdir, NULL);
5371 WARN("CreateDirectory failed: %d\n", GetLastError());
5372 return ERROR_SUCCESS;
5377 msi_move_file(source, dest, options);
5379 move_files_wildcard(source, dest, options);
5382 msi_free(sourcedir);
5387 return ERROR_SUCCESS;
5390 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5395 static const WCHAR ExecSeqQuery[] =
5396 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5397 '`','M','o','v','e','F','i','l','e','`',0};
5399 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5400 if (rc != ERROR_SUCCESS)
5401 return ERROR_SUCCESS;
5403 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5404 msiobj_release(&view->hdr);
5409 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5410 LPCSTR action, LPCWSTR table )
5412 static const WCHAR query[] = {
5413 'S','E','L','E','C','T',' ','*',' ',
5414 'F','R','O','M',' ','`','%','s','`',0 };
5415 MSIQUERY *view = NULL;
5419 r = MSI_OpenQuery( package->db, &view, query, table );
5420 if (r == ERROR_SUCCESS)
5422 r = MSI_IterateRecords(view, &count, NULL, package);
5423 msiobj_release(&view->hdr);
5427 FIXME("%s -> %u ignored %s table values\n",
5428 action, count, debugstr_w(table));
5430 return ERROR_SUCCESS;
5433 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5435 TRACE("%p\n", package);
5436 return ERROR_SUCCESS;
5439 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5441 static const WCHAR table[] =
5442 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5443 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5446 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5448 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5449 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5452 static UINT ACTION_BindImage( MSIPACKAGE *package )
5454 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5455 return msi_unimplemented_action_stub( package, "BindImage", table );
5458 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5460 static const WCHAR table[] = {
5461 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5462 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5465 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5467 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5468 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5471 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5473 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5474 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5477 static UINT ACTION_StopServices( MSIPACKAGE *package )
5479 static const WCHAR table[] = {
5480 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5481 return msi_unimplemented_action_stub( package, "StopServices", table );
5484 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5486 static const WCHAR table[] = {
5487 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5488 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5490 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5492 static const WCHAR table[] = {
5493 'P','r','o','d','u','c','t','I','D',0 };
5494 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5497 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5499 static const WCHAR table[] = {
5500 'E','n','v','i','r','o','n','m','e','n','t',0 };
5501 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5504 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5506 static const WCHAR table[] = {
5507 'M','s','i','A','s','s','e','m','b','l','y',0 };
5508 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5511 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5513 static const WCHAR table[] = {
5514 'M','s','i','A','s','s','e','m','b','l','y',0 };
5515 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5518 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5520 static const WCHAR table[] = { 'F','o','n','t',0 };
5521 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5524 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5526 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5527 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5530 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5532 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5533 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5536 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5538 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5539 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5542 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5544 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5545 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5548 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5550 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5551 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5554 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5556 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5557 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5560 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5562 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5563 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5566 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5568 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5569 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5572 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5574 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5575 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5578 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5580 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5581 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5584 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5586 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5587 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5590 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5592 static const WCHAR table[] = { 'A','p','p','I','d',0 };
5593 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5596 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5598 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5599 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5602 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5604 static const WCHAR table[] = { 'M','I','M','E',0 };
5605 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5608 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5610 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5611 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5614 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5616 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5617 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5620 static const struct _actions StandardActions[] = {
5621 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5622 { szAppSearch, ACTION_AppSearch },
5623 { szBindImage, ACTION_BindImage },
5624 { szCCPSearch, ACTION_CCPSearch },
5625 { szCostFinalize, ACTION_CostFinalize },
5626 { szCostInitialize, ACTION_CostInitialize },
5627 { szCreateFolders, ACTION_CreateFolders },
5628 { szCreateShortcuts, ACTION_CreateShortcuts },
5629 { szDeleteServices, ACTION_DeleteServices },
5630 { szDisableRollback, NULL },
5631 { szDuplicateFiles, ACTION_DuplicateFiles },
5632 { szExecuteAction, ACTION_ExecuteAction },
5633 { szFileCost, ACTION_FileCost },
5634 { szFindRelatedProducts, ACTION_FindRelatedProducts },
5635 { szForceReboot, ACTION_ForceReboot },
5636 { szInstallAdminPackage, NULL },
5637 { szInstallExecute, ACTION_InstallExecute },
5638 { szInstallExecuteAgain, ACTION_InstallExecute },
5639 { szInstallFiles, ACTION_InstallFiles},
5640 { szInstallFinalize, ACTION_InstallFinalize },
5641 { szInstallInitialize, ACTION_InstallInitialize },
5642 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5643 { szInstallValidate, ACTION_InstallValidate },
5644 { szIsolateComponents, ACTION_IsolateComponents },
5645 { szLaunchConditions, ACTION_LaunchConditions },
5646 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5647 { szMoveFiles, ACTION_MoveFiles },
5648 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5649 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5650 { szInstallODBC, ACTION_InstallODBC },
5651 { szInstallServices, ACTION_InstallServices },
5652 { szPatchFiles, ACTION_PatchFiles },
5653 { szProcessComponents, ACTION_ProcessComponents },
5654 { szPublishComponents, ACTION_PublishComponents },
5655 { szPublishFeatures, ACTION_PublishFeatures },
5656 { szPublishProduct, ACTION_PublishProduct },
5657 { szRegisterClassInfo, ACTION_RegisterClassInfo },
5658 { szRegisterComPlus, ACTION_RegisterComPlus},
5659 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5660 { szRegisterFonts, ACTION_RegisterFonts },
5661 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5662 { szRegisterProduct, ACTION_RegisterProduct },
5663 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5664 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5665 { szRegisterUser, ACTION_RegisterUser },
5666 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5667 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5668 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5669 { szRemoveFiles, ACTION_RemoveFiles },
5670 { szRemoveFolders, ACTION_RemoveFolders },
5671 { szRemoveIniValues, ACTION_RemoveIniValues },
5672 { szRemoveODBC, ACTION_RemoveODBC },
5673 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5674 { szRemoveShortcuts, ACTION_RemoveShortcuts },
5675 { szResolveSource, ACTION_ResolveSource },
5676 { szRMCCPSearch, ACTION_RMCCPSearch },
5677 { szScheduleReboot, NULL },
5678 { szSelfRegModules, ACTION_SelfRegModules },
5679 { szSelfUnregModules, ACTION_SelfUnregModules },
5680 { szSetODBCFolders, NULL },
5681 { szStartServices, ACTION_StartServices },
5682 { szStopServices, ACTION_StopServices },
5683 { szUnpublishComponents, ACTION_UnpublishComponents },
5684 { szUnpublishFeatures, ACTION_UnpublishFeatures },
5685 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5686 { szUnregisterComPlus, ACTION_UnregisterComPlus },
5687 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5688 { szUnregisterFonts, ACTION_UnregisterFonts },
5689 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5690 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5691 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5692 { szValidateProductID, ACTION_ValidateProductID },
5693 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5694 { szWriteIniValues, ACTION_WriteIniValues },
5695 { szWriteRegistryValues, ACTION_WriteRegistryValues },