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};
2333 *type=REG_EXPAND_SZ;
2341 if (strstrW(value,szMulti))
2342 *type = REG_MULTI_SZ;
2344 /* remove initial delimiter */
2345 if (!strncmpW(value, szMulti, 3))
2348 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2350 /* add double NULL terminator */
2351 if (*type == REG_MULTI_SZ)
2353 *size += sizeof(WCHAR);
2354 newdata = msi_alloc(*size);
2361 memcpy(newdata, data, *size - 1);
2362 newdata[*size] = '\0';
2365 data = (LPSTR)newdata;
2371 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2373 MSIPACKAGE *package = (MSIPACKAGE*)param;
2374 static const WCHAR szHCR[] =
2375 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2376 'R','O','O','T','\\',0};
2377 static const WCHAR szHCU[] =
2378 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2379 'U','S','E','R','\\',0};
2380 static const WCHAR szHLM[] =
2381 {'H','K','E','Y','_','L','O','C','A','L','_',
2382 'M','A','C','H','I','N','E','\\',0};
2383 static const WCHAR szHU[] =
2384 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2386 LPSTR value_data = NULL;
2387 HKEY root_key, hkey;
2390 LPCWSTR szRoot, component, name, key, value;
2395 BOOL check_first = FALSE;
2398 ui_progress(package,2,0,0,0);
2405 component = MSI_RecordGetString(row, 6);
2406 comp = get_loaded_component(package,component);
2408 return ERROR_SUCCESS;
2410 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2412 TRACE("Skipping write due to disabled component %s\n",
2413 debugstr_w(component));
2415 comp->Action = comp->Installed;
2417 return ERROR_SUCCESS;
2420 comp->Action = INSTALLSTATE_LOCAL;
2422 name = MSI_RecordGetString(row, 4);
2423 if( MSI_RecordIsNull(row,5) && name )
2425 /* null values can have special meanings */
2426 if (name[0]=='-' && name[1] == 0)
2427 return ERROR_SUCCESS;
2428 else if ((name[0]=='+' && name[1] == 0) ||
2429 (name[0] == '*' && name[1] == 0))
2434 root = MSI_RecordGetInteger(row,2);
2435 key = MSI_RecordGetString(row, 3);
2437 /* get the root key */
2442 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2443 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2444 if (all_users && all_users[0] == '1')
2446 root_key = HKEY_LOCAL_MACHINE;
2451 root_key = HKEY_CURRENT_USER;
2454 msi_free(all_users);
2457 case 0: root_key = HKEY_CLASSES_ROOT;
2460 case 1: root_key = HKEY_CURRENT_USER;
2463 case 2: root_key = HKEY_LOCAL_MACHINE;
2466 case 3: root_key = HKEY_USERS;
2470 ERR("Unknown root %i\n",root);
2476 return ERROR_SUCCESS;
2478 deformat_string(package, key , &deformated);
2479 size = strlenW(deformated) + strlenW(szRoot) + 1;
2480 uikey = msi_alloc(size*sizeof(WCHAR));
2481 strcpyW(uikey,szRoot);
2482 strcatW(uikey,deformated);
2484 if (RegCreateKeyW( root_key, deformated, &hkey))
2486 ERR("Could not create key %s\n",debugstr_w(deformated));
2487 msi_free(deformated);
2489 return ERROR_SUCCESS;
2491 msi_free(deformated);
2493 value = MSI_RecordGetString(row,5);
2495 value_data = parse_value(package, value, &type, &size);
2498 static const WCHAR szEmpty[] = {0};
2499 value_data = (LPSTR)strdupW(szEmpty);
2504 deformat_string(package, name, &deformated);
2506 /* get the double nulls to terminate SZ_MULTI */
2507 if (type == REG_MULTI_SZ)
2508 size +=sizeof(WCHAR);
2512 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2514 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2519 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2520 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2522 TRACE("value %s of %s checked already exists\n",
2523 debugstr_w(deformated), debugstr_w(uikey));
2527 TRACE("Checked and setting value %s of %s\n",
2528 debugstr_w(deformated), debugstr_w(uikey));
2529 if (deformated || size)
2530 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2535 uirow = MSI_CreateRecord(3);
2536 MSI_RecordSetStringW(uirow,2,deformated);
2537 MSI_RecordSetStringW(uirow,1,uikey);
2540 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2542 MSI_RecordSetStringW(uirow,3,value);
2544 ui_actiondata(package,szWriteRegistryValues,uirow);
2545 msiobj_release( &uirow->hdr );
2547 msi_free(value_data);
2548 msi_free(deformated);
2551 return ERROR_SUCCESS;
2554 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2558 static const WCHAR ExecSeqQuery[] =
2559 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2560 '`','R','e','g','i','s','t','r','y','`',0 };
2562 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2563 if (rc != ERROR_SUCCESS)
2564 return ERROR_SUCCESS;
2566 /* increment progress bar each time action data is sent */
2567 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2569 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2571 msiobj_release(&view->hdr);
2575 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2577 package->script->CurrentlyScripting = TRUE;
2579 return ERROR_SUCCESS;
2583 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2588 static const WCHAR q1[]=
2589 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2590 '`','R','e','g','i','s','t','r','y','`',0};
2593 MSIFEATURE *feature;
2596 TRACE("InstallValidate\n");
2598 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2599 if (rc == ERROR_SUCCESS)
2601 MSI_IterateRecords( view, &progress, NULL, package );
2602 msiobj_release( &view->hdr );
2603 total += progress * REG_PROGRESS_VALUE;
2606 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2607 total += COMPONENT_PROGRESS_VALUE;
2609 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2610 total += file->FileSize;
2612 ui_progress(package,0,total,0,0);
2614 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2616 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2617 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2618 feature->ActionRequest);
2621 return ERROR_SUCCESS;
2624 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2626 MSIPACKAGE* package = (MSIPACKAGE*)param;
2627 LPCWSTR cond = NULL;
2628 LPCWSTR message = NULL;
2631 static const WCHAR title[]=
2632 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2634 cond = MSI_RecordGetString(row,1);
2636 r = MSI_EvaluateConditionW(package,cond);
2637 if (r == MSICONDITION_FALSE)
2639 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2642 message = MSI_RecordGetString(row,2);
2643 deformat_string(package,message,&deformated);
2644 MessageBoxW(NULL,deformated,title,MB_OK);
2645 msi_free(deformated);
2648 return ERROR_INSTALL_FAILURE;
2651 return ERROR_SUCCESS;
2654 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2657 MSIQUERY * view = NULL;
2658 static const WCHAR ExecSeqQuery[] =
2659 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2660 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2662 TRACE("Checking launch conditions\n");
2664 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2665 if (rc != ERROR_SUCCESS)
2666 return ERROR_SUCCESS;
2668 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2669 msiobj_release(&view->hdr);
2674 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2678 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2680 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2682 MSIRECORD * row = 0;
2684 LPWSTR deformated,buffer,deformated_name;
2686 static const WCHAR ExecSeqQuery[] =
2687 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2688 '`','R','e','g','i','s','t','r','y','`',' ',
2689 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2690 ' ','=',' ' ,'\'','%','s','\'',0 };
2691 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2692 static const WCHAR fmt2[]=
2693 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2695 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2699 root = MSI_RecordGetInteger(row,2);
2700 key = MSI_RecordGetString(row, 3);
2701 name = MSI_RecordGetString(row, 4);
2702 deformat_string(package, key , &deformated);
2703 deformat_string(package, name, &deformated_name);
2705 len = strlenW(deformated) + 6;
2706 if (deformated_name)
2707 len+=strlenW(deformated_name);
2709 buffer = msi_alloc( len *sizeof(WCHAR));
2711 if (deformated_name)
2712 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2714 sprintfW(buffer,fmt,root,deformated);
2716 msi_free(deformated);
2717 msi_free(deformated_name);
2718 msiobj_release(&row->hdr);
2722 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2724 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2729 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2732 return strdupW( file->TargetPath );
2737 static HKEY openSharedDLLsKey(void)
2740 static const WCHAR path[] =
2741 {'S','o','f','t','w','a','r','e','\\',
2742 'M','i','c','r','o','s','o','f','t','\\',
2743 'W','i','n','d','o','w','s','\\',
2744 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2745 'S','h','a','r','e','d','D','L','L','s',0};
2747 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2751 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2756 DWORD sz = sizeof(count);
2759 hkey = openSharedDLLsKey();
2760 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2761 if (rc != ERROR_SUCCESS)
2767 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2771 hkey = openSharedDLLsKey();
2773 msi_reg_set_val_dword( hkey, path, count );
2775 RegDeleteValueW(hkey,path);
2781 * Return TRUE if the count should be written out and FALSE if not
2783 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2785 MSIFEATURE *feature;
2789 /* only refcount DLLs */
2790 if (comp->KeyPath == NULL ||
2791 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2792 comp->Attributes & msidbComponentAttributesODBCDataSource)
2796 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2797 write = (count > 0);
2799 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2803 /* increment counts */
2804 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2808 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2811 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2813 if ( cl->component == comp )
2818 /* decrement counts */
2819 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2823 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2826 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2828 if ( cl->component == comp )
2833 /* ref count all the files in the component */
2838 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2840 if (file->Component == comp)
2841 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2845 /* add a count for permenent */
2846 if (comp->Attributes & msidbComponentAttributesPermanent)
2849 comp->RefCount = count;
2852 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2856 * Ok further analysis makes me think that this work is
2857 * actually done in the PublishComponents and PublishFeatures
2858 * step, and not here. It appears like the keypath and all that is
2859 * resolved in this step, however actually written in the Publish steps.
2860 * But we will leave it here for now because it is unclear
2862 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2864 WCHAR squished_pc[GUID_SIZE];
2865 WCHAR squished_cc[GUID_SIZE];
2868 HKEY hkey=0,hkey2=0;
2872 /* writes the Component and Features values to the registry */
2874 rc = MSIREG_OpenComponents(&hkey);
2875 if (rc != ERROR_SUCCESS)
2878 squash_guid(package->ProductCode,squished_pc);
2879 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2881 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2885 ui_progress(package,2,0,0,0);
2886 if (!comp->ComponentId)
2889 squash_guid(comp->ComponentId,squished_cc);
2891 msi_free(comp->FullKeypath);
2892 comp->FullKeypath = resolve_keypath( package, comp );
2894 /* do the refcounting */
2895 ACTION_RefCountComponent( package, comp );
2897 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2898 debugstr_w(comp->Component),
2899 debugstr_w(squished_cc),
2900 debugstr_w(comp->FullKeypath),
2903 * Write the keypath out if the component is to be registered
2904 * and delete the key if the component is to be deregistered
2906 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2908 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2909 if (rc != ERROR_SUCCESS)
2912 if (!comp->FullKeypath)
2915 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2917 if (comp->Attributes & msidbComponentAttributesPermanent)
2919 static const WCHAR szPermKey[] =
2920 { '0','0','0','0','0','0','0','0','0','0','0','0',
2921 '0','0','0','0','0','0','0','0','0','0','0','0',
2922 '0','0','0','0','0','0','0','0',0 };
2924 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2929 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey2, TRUE);
2930 if (rc != ERROR_SUCCESS)
2933 msi_reg_set_val_str(hkey2, squished_pc, comp->FullKeypath);
2936 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2940 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2941 if (rc != ERROR_SUCCESS)
2944 RegDeleteValueW(hkey2,squished_pc);
2946 /* if the key is empty delete it */
2947 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2949 if (res == ERROR_NO_MORE_ITEMS)
2950 RegDeleteKeyW(hkey,squished_cc);
2952 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2956 uirow = MSI_CreateRecord(3);
2957 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2958 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2959 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2960 ui_actiondata(package,szProcessComponents,uirow);
2961 msiobj_release( &uirow->hdr );
2975 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2976 LPWSTR lpszName, LONG_PTR lParam)
2979 typelib_struct *tl_struct = (typelib_struct*) lParam;
2980 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2984 if (!IS_INTRESOURCE(lpszName))
2986 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2990 sz = strlenW(tl_struct->source)+4;
2991 sz *= sizeof(WCHAR);
2993 if ((INT_PTR)lpszName == 1)
2994 tl_struct->path = strdupW(tl_struct->source);
2997 tl_struct->path = msi_alloc(sz);
2998 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3001 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3002 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3003 if (!SUCCEEDED(res))
3005 msi_free(tl_struct->path);
3006 tl_struct->path = NULL;
3011 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3012 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3014 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3018 msi_free(tl_struct->path);
3019 tl_struct->path = NULL;
3021 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3022 ITypeLib_Release(tl_struct->ptLib);
3027 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3029 MSIPACKAGE* package = (MSIPACKAGE*)param;
3033 typelib_struct tl_struct;
3035 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3037 component = MSI_RecordGetString(row,3);
3038 comp = get_loaded_component(package,component);
3040 return ERROR_SUCCESS;
3042 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3044 TRACE("Skipping typelib reg due to disabled component\n");
3046 comp->Action = comp->Installed;
3048 return ERROR_SUCCESS;
3051 comp->Action = INSTALLSTATE_LOCAL;
3053 file = get_loaded_file( package, comp->KeyPath );
3055 return ERROR_SUCCESS;
3057 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3061 guid = MSI_RecordGetString(row,1);
3062 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3063 tl_struct.source = strdupW( file->TargetPath );
3064 tl_struct.path = NULL;
3066 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3067 (LONG_PTR)&tl_struct);
3075 helpid = MSI_RecordGetString(row,6);
3078 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3079 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3082 if (!SUCCEEDED(res))
3083 ERR("Failed to register type library %s\n",
3084 debugstr_w(tl_struct.path));
3087 ui_actiondata(package,szRegisterTypeLibraries,row);
3089 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3092 ITypeLib_Release(tl_struct.ptLib);
3093 msi_free(tl_struct.path);
3096 ERR("Failed to load type library %s\n",
3097 debugstr_w(tl_struct.source));
3099 FreeLibrary(module);
3100 msi_free(tl_struct.source);
3103 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3105 return ERROR_SUCCESS;
3108 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3111 * OK this is a bit confusing.. I am given a _Component key and I believe
3112 * that the file that is being registered as a type library is the "key file
3113 * of that component" which I interpret to mean "The file in the KeyPath of
3118 static const WCHAR Query[] =
3119 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3120 '`','T','y','p','e','L','i','b','`',0};
3122 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3123 if (rc != ERROR_SUCCESS)
3124 return ERROR_SUCCESS;
3126 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3127 msiobj_release(&view->hdr);
3131 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3133 MSIPACKAGE *package = (MSIPACKAGE*)param;
3134 LPWSTR target_file, target_folder, filename;
3135 LPCWSTR buffer, extension;
3137 static const WCHAR szlnk[]={'.','l','n','k',0};
3138 IShellLinkW *sl = NULL;
3139 IPersistFile *pf = NULL;
3142 buffer = MSI_RecordGetString(row,4);
3143 comp = get_loaded_component(package,buffer);
3145 return ERROR_SUCCESS;
3147 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3149 TRACE("Skipping shortcut creation due to disabled component\n");
3151 comp->Action = comp->Installed;
3153 return ERROR_SUCCESS;
3156 comp->Action = INSTALLSTATE_LOCAL;
3158 ui_actiondata(package,szCreateShortcuts,row);
3160 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3161 &IID_IShellLinkW, (LPVOID *) &sl );
3165 ERR("CLSID_ShellLink not available\n");
3169 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3172 ERR("QueryInterface(IID_IPersistFile) failed\n");
3176 buffer = MSI_RecordGetString(row,2);
3177 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3179 /* may be needed because of a bug somehwere else */
3180 create_full_pathW(target_folder);
3182 filename = msi_dup_record_field( row, 3 );
3183 reduce_to_longfilename(filename);
3185 extension = strchrW(filename,'.');
3186 if (!extension || strcmpiW(extension,szlnk))
3188 int len = strlenW(filename);
3189 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3190 memcpy(filename + len, szlnk, sizeof(szlnk));
3192 target_file = build_directory_name(2, target_folder, filename);
3193 msi_free(target_folder);
3196 buffer = MSI_RecordGetString(row,5);
3197 if (strchrW(buffer,'['))
3200 deformat_string(package,buffer,&deformated);
3201 IShellLinkW_SetPath(sl,deformated);
3202 msi_free(deformated);
3206 FIXME("poorly handled shortcut format, advertised shortcut\n");
3207 IShellLinkW_SetPath(sl,comp->FullKeypath);
3210 if (!MSI_RecordIsNull(row,6))
3213 buffer = MSI_RecordGetString(row,6);
3214 deformat_string(package,buffer,&deformated);
3215 IShellLinkW_SetArguments(sl,deformated);
3216 msi_free(deformated);
3219 if (!MSI_RecordIsNull(row,7))
3221 buffer = MSI_RecordGetString(row,7);
3222 IShellLinkW_SetDescription(sl,buffer);
3225 if (!MSI_RecordIsNull(row,8))
3226 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3228 if (!MSI_RecordIsNull(row,9))
3233 buffer = MSI_RecordGetString(row,9);
3235 Path = build_icon_path(package,buffer);
3236 index = MSI_RecordGetInteger(row,10);
3238 /* no value means 0 */
3239 if (index == MSI_NULL_INTEGER)
3242 IShellLinkW_SetIconLocation(sl,Path,index);
3246 if (!MSI_RecordIsNull(row,11))
3247 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3249 if (!MSI_RecordIsNull(row,12))
3252 buffer = MSI_RecordGetString(row,12);
3253 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3255 IShellLinkW_SetWorkingDirectory(sl,Path);
3259 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3260 IPersistFile_Save(pf,target_file,FALSE);
3262 msi_free(target_file);
3266 IPersistFile_Release( pf );
3268 IShellLinkW_Release( sl );
3270 return ERROR_SUCCESS;
3273 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3278 static const WCHAR Query[] =
3279 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3280 '`','S','h','o','r','t','c','u','t','`',0};
3282 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3283 if (rc != ERROR_SUCCESS)
3284 return ERROR_SUCCESS;
3286 res = CoInitialize( NULL );
3289 ERR("CoInitialize failed\n");
3290 return ERROR_FUNCTION_FAILED;
3293 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3294 msiobj_release(&view->hdr);
3301 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3303 MSIPACKAGE* package = (MSIPACKAGE*)param;
3312 FileName = MSI_RecordGetString(row,1);
3315 ERR("Unable to get FileName\n");
3316 return ERROR_SUCCESS;
3319 FilePath = build_icon_path(package,FileName);
3321 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3323 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3324 FILE_ATTRIBUTE_NORMAL, NULL);
3326 if (the_file == INVALID_HANDLE_VALUE)
3328 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3330 return ERROR_SUCCESS;
3337 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3338 if (rc != ERROR_SUCCESS)
3340 ERR("Failed to get stream\n");
3341 CloseHandle(the_file);
3342 DeleteFileW(FilePath);
3345 WriteFile(the_file,buffer,sz,&write,NULL);
3346 } while (sz == 1024);
3350 CloseHandle(the_file);
3352 uirow = MSI_CreateRecord(1);
3353 MSI_RecordSetStringW(uirow,1,FileName);
3354 ui_actiondata(package,szPublishProduct,uirow);
3355 msiobj_release( &uirow->hdr );
3357 return ERROR_SUCCESS;
3360 static BOOL msi_check_publish(MSIPACKAGE *package)
3362 MSIFEATURE *feature;
3364 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3366 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3374 * 99% of the work done here is only done for
3375 * advertised installs. However this is where the
3376 * Icon table is processed and written out
3377 * so that is what I am going to do here.
3379 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3383 MSISOURCELISTINFO *info;
3385 static const WCHAR Query[]=
3386 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3387 '`','I','c','o','n','`',0};
3388 /* for registry stuff */
3391 HKEY hudkey=0, props=0;
3392 static const WCHAR szProductLanguage[] =
3393 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3394 static const WCHAR szARPProductIcon[] =
3395 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3396 static const WCHAR szProductVersion[] =
3397 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3401 MSIHANDLE hDb, hSumInfo;
3403 /* FIXME: also need to publish if the product is in advertise mode */
3404 if (!msi_check_publish(package))
3405 return ERROR_SUCCESS;
3407 /* write out icon files */
3409 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3410 if (rc == ERROR_SUCCESS)
3412 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3413 msiobj_release(&view->hdr);
3416 /* ok there is a lot more done here but i need to figure out what */
3418 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3419 if (rc != ERROR_SUCCESS)
3422 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3423 if (rc != ERROR_SUCCESS)
3426 rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3427 if (rc != ERROR_SUCCESS)
3430 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode,&props,TRUE);
3431 if (rc != ERROR_SUCCESS)
3434 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3435 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3438 langid = msi_get_property_int( package, szProductLanguage, 0 );
3439 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3441 buffer = msi_dup_property( package, szARPProductIcon );
3444 LPWSTR path = build_icon_path(package,buffer);
3445 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3450 buffer = msi_dup_property( package, szProductVersion );
3453 DWORD verdword = msi_version_str_to_dword(buffer);
3454 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3458 /* FIXME: Need to write more keys to the user registry */
3460 hDb= alloc_msihandle( &package->db->hdr );
3462 rc = ERROR_NOT_ENOUGH_MEMORY;
3465 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3466 MsiCloseHandle(hDb);
3467 if (rc == ERROR_SUCCESS)
3469 WCHAR guidbuffer[0x200];
3471 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3473 if (rc == ERROR_SUCCESS)
3475 WCHAR squashed[GUID_SIZE];
3476 /* for now we only care about the first guid */
3477 LPWSTR ptr = strchrW(guidbuffer,';');
3479 squash_guid(guidbuffer,squashed);
3480 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3484 ERR("Unable to query Revision_Number...\n");
3487 MsiCloseHandle(hSumInfo);
3491 ERR("Unable to open Summary Information\n");
3495 /* publish the SourceList info */
3496 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3498 MsiSourceListSetInfoW(package->ProductCode, NULL,
3499 info->context, info->options,
3500 info->property, info->value);
3503 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3505 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3506 disk->context, disk->options,
3507 disk->disk_id, disk->volume_label, disk->disk_prompt);
3513 RegCloseKey(hudkey);
3519 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3521 MSIPACKAGE *package = (MSIPACKAGE*)param;
3522 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3523 LPWSTR deformated_section, deformated_key, deformated_value;
3524 LPWSTR folder, fullname = NULL;
3528 static const WCHAR szWindowsFolder[] =
3529 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3531 component = MSI_RecordGetString(row, 8);
3532 comp = get_loaded_component(package,component);
3534 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3536 TRACE("Skipping ini file due to disabled component %s\n",
3537 debugstr_w(component));
3539 comp->Action = comp->Installed;
3541 return ERROR_SUCCESS;
3544 comp->Action = INSTALLSTATE_LOCAL;
3546 identifier = MSI_RecordGetString(row,1);
3547 filename = MSI_RecordGetString(row,2);
3548 dirproperty = MSI_RecordGetString(row,3);
3549 section = MSI_RecordGetString(row,4);
3550 key = MSI_RecordGetString(row,5);
3551 value = MSI_RecordGetString(row,6);
3552 action = MSI_RecordGetInteger(row,7);
3554 deformat_string(package,section,&deformated_section);
3555 deformat_string(package,key,&deformated_key);
3556 deformat_string(package,value,&deformated_value);
3560 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3562 folder = msi_dup_property( package, dirproperty );
3565 folder = msi_dup_property( package, szWindowsFolder );
3569 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3573 fullname = build_directory_name(2, folder, filename);
3577 TRACE("Adding value %s to section %s in %s\n",
3578 debugstr_w(deformated_key), debugstr_w(deformated_section),
3579 debugstr_w(fullname));
3580 WritePrivateProfileStringW(deformated_section, deformated_key,
3581 deformated_value, fullname);
3583 else if (action == 1)
3586 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3587 returned, 10, fullname);
3588 if (returned[0] == 0)
3590 TRACE("Adding value %s to section %s in %s\n",
3591 debugstr_w(deformated_key), debugstr_w(deformated_section),
3592 debugstr_w(fullname));
3594 WritePrivateProfileStringW(deformated_section, deformated_key,
3595 deformated_value, fullname);
3598 else if (action == 3)
3599 FIXME("Append to existing section not yet implemented\n");
3601 uirow = MSI_CreateRecord(4);
3602 MSI_RecordSetStringW(uirow,1,identifier);
3603 MSI_RecordSetStringW(uirow,2,deformated_section);
3604 MSI_RecordSetStringW(uirow,3,deformated_key);
3605 MSI_RecordSetStringW(uirow,4,deformated_value);
3606 ui_actiondata(package,szWriteIniValues,uirow);
3607 msiobj_release( &uirow->hdr );
3611 msi_free(deformated_key);
3612 msi_free(deformated_value);
3613 msi_free(deformated_section);
3614 return ERROR_SUCCESS;
3617 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3621 static const WCHAR ExecSeqQuery[] =
3622 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3623 '`','I','n','i','F','i','l','e','`',0};
3625 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3626 if (rc != ERROR_SUCCESS)
3628 TRACE("no IniFile table\n");
3629 return ERROR_SUCCESS;
3632 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3633 msiobj_release(&view->hdr);
3637 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3639 MSIPACKAGE *package = (MSIPACKAGE*)param;
3644 static const WCHAR ExeStr[] =
3645 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3646 static const WCHAR close[] = {'\"',0};
3648 PROCESS_INFORMATION info;
3653 memset(&si,0,sizeof(STARTUPINFOW));
3655 filename = MSI_RecordGetString(row,1);
3656 file = get_loaded_file( package, filename );
3660 ERR("Unable to find file id %s\n",debugstr_w(filename));
3661 return ERROR_SUCCESS;
3664 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3666 FullName = msi_alloc(len*sizeof(WCHAR));
3667 strcpyW(FullName,ExeStr);
3668 strcatW( FullName, file->TargetPath );
3669 strcatW(FullName,close);
3671 TRACE("Registering %s\n",debugstr_w(FullName));
3672 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3676 msi_dialog_check_messages(info.hProcess);
3681 uirow = MSI_CreateRecord( 2 );
3682 uipath = strdupW( file->TargetPath );
3683 p = strrchrW(uipath,'\\');
3686 MSI_RecordSetStringW( uirow, 1, &p[1] );
3687 MSI_RecordSetStringW( uirow, 2, uipath);
3688 ui_actiondata( package, szSelfRegModules, uirow);
3689 msiobj_release( &uirow->hdr );
3691 /* FIXME: call ui_progress? */
3693 return ERROR_SUCCESS;
3696 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3700 static const WCHAR ExecSeqQuery[] =
3701 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3702 '`','S','e','l','f','R','e','g','`',0};
3704 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3705 if (rc != ERROR_SUCCESS)
3707 TRACE("no SelfReg table\n");
3708 return ERROR_SUCCESS;
3711 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3712 msiobj_release(&view->hdr);
3714 return ERROR_SUCCESS;
3717 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3719 MSIFEATURE *feature;
3725 if (!msi_check_publish(package))
3726 return ERROR_SUCCESS;
3728 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3729 if (rc != ERROR_SUCCESS)
3732 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3733 if (rc != ERROR_SUCCESS)
3736 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &userdata, TRUE);
3737 if (rc != ERROR_SUCCESS)
3740 /* here the guids are base 85 encoded */
3741 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3747 BOOL absent = FALSE;
3750 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3751 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3752 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3756 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3760 if (feature->Feature_Parent)
3761 size += strlenW( feature->Feature_Parent )+2;
3763 data = msi_alloc(size * sizeof(WCHAR));
3766 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3768 MSICOMPONENT* component = cl->component;
3772 if (component->ComponentId)
3774 TRACE("From %s\n",debugstr_w(component->ComponentId));
3775 CLSIDFromString(component->ComponentId, &clsid);
3776 encode_base85_guid(&clsid,buf);
3777 TRACE("to %s\n",debugstr_w(buf));
3782 if (feature->Feature_Parent)
3784 static const WCHAR sep[] = {'\2',0};
3786 strcatW(data,feature->Feature_Parent);
3789 msi_reg_set_val_str( hkey, feature->Feature, data );
3790 msi_reg_set_val_str( userdata, feature->Feature, data );
3794 if (feature->Feature_Parent)
3795 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3798 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3799 (LPBYTE)feature->Feature_Parent,size);
3803 size += 2*sizeof(WCHAR);
3804 data = msi_alloc(size);
3807 if (feature->Feature_Parent)
3808 strcpyW( &data[1], feature->Feature_Parent );
3809 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3815 uirow = MSI_CreateRecord( 1 );
3816 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3817 ui_actiondata( package, szPublishFeatures, uirow);
3818 msiobj_release( &uirow->hdr );
3819 /* FIXME: call ui_progress? */
3828 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3833 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3835 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3836 if (r == ERROR_SUCCESS)
3838 RegDeleteValueW(hkey, feature->Feature);
3842 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3843 if (r == ERROR_SUCCESS)
3845 RegDeleteValueW(hkey, feature->Feature);
3849 return ERROR_SUCCESS;
3852 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3854 MSIFEATURE *feature;
3856 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3858 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3865 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3867 MSIFEATURE *feature;
3869 if (!msi_check_unpublish(package))
3870 return ERROR_SUCCESS;
3872 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3874 msi_unpublish_feature(package, feature);
3877 return ERROR_SUCCESS;
3880 static UINT msi_get_local_package_name( LPWSTR path )
3882 static const WCHAR szInstaller[] = {
3883 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3884 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3888 time = GetTickCount();
3889 GetWindowsDirectoryW( path, MAX_PATH );
3890 lstrcatW( path, szInstaller );
3891 CreateDirectoryW( path, NULL );
3893 len = lstrlenW(path);
3894 for (i=0; i<0x10000; i++)
3896 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3897 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3898 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3899 if (handle != INVALID_HANDLE_VALUE)
3901 CloseHandle(handle);
3904 if (GetLastError() != ERROR_FILE_EXISTS &&
3905 GetLastError() != ERROR_SHARING_VIOLATION)
3906 return ERROR_FUNCTION_FAILED;
3909 return ERROR_SUCCESS;
3912 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3914 WCHAR packagefile[MAX_PATH];
3918 r = msi_get_local_package_name( packagefile );
3919 if (r != ERROR_SUCCESS)
3922 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3924 r = CopyFileW( package->db->path, packagefile, FALSE);
3928 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3929 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
3930 return ERROR_FUNCTION_FAILED;
3933 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3935 r = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3936 if (r != ERROR_SUCCESS)
3939 msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3941 return ERROR_SUCCESS;
3944 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3946 LPWSTR prop, val, key;
3947 static const LPCSTR propval[] = {
3948 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3949 "ARPCONTACT", "Contact",
3950 "ARPCOMMENTS", "Comments",
3951 "ProductName", "DisplayName",
3952 "ProductVersion", "DisplayVersion",
3953 "ARPHELPLINK", "HelpLink",
3954 "ARPHELPTELEPHONE", "HelpTelephone",
3955 "ARPINSTALLLOCATION", "InstallLocation",
3956 "SourceDir", "InstallSource",
3957 "Manufacturer", "Publisher",
3958 "ARPREADME", "Readme",
3960 "ARPURLINFOABOUT", "URLInfoAbout",
3961 "ARPURLUPDATEINFO", "URLUpdateInfo",
3964 const LPCSTR *p = propval;
3968 prop = strdupAtoW( *p++ );
3969 key = strdupAtoW( *p++ );
3970 val = msi_dup_property( package, prop );
3971 msi_reg_set_val_str( hkey, key, val );
3976 return ERROR_SUCCESS;
3979 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3982 HKEY hudkey=0, props=0;
3983 LPWSTR buffer = NULL;
3986 static const WCHAR szWindowsInstaller[] =
3987 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3988 static const WCHAR szUpgradeCode[] =
3989 {'U','p','g','r','a','d','e','C','o','d','e',0};
3990 static const WCHAR modpath_fmt[] =
3991 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3992 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3993 static const WCHAR szModifyPath[] =
3994 {'M','o','d','i','f','y','P','a','t','h',0};
3995 static const WCHAR szUninstallString[] =
3996 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3997 static const WCHAR szEstimatedSize[] =
3998 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3999 static const WCHAR szProductLanguage[] =
4000 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4001 static const WCHAR szProductVersion[] =
4002 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4005 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4006 LPWSTR upgrade_code;
4009 /* FIXME: also need to publish if the product is in advertise mode */
4010 if (!msi_check_publish(package))
4011 return ERROR_SUCCESS;
4013 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4014 if (rc != ERROR_SUCCESS)
4017 /* dump all the info i can grab */
4018 /* FIXME: Flesh out more information */
4020 msi_write_uninstall_property_vals( package, hkey );
4022 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
4024 msi_make_package_local( package, hkey );
4026 /* do ModifyPath and UninstallString */
4027 size = deformat_string(package,modpath_fmt,&buffer);
4028 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4029 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4032 /* FIXME: Write real Estimated Size when we have it */
4033 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
4035 GetLocalTime(&systime);
4036 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
4037 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
4039 langid = msi_get_property_int( package, szProductLanguage, 0 );
4040 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
4042 buffer = msi_dup_property( package, szProductVersion );
4045 DWORD verdword = msi_version_str_to_dword(buffer);
4047 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
4048 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
4049 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
4053 /* Handle Upgrade Codes */
4054 upgrade_code = msi_dup_property( package, szUpgradeCode );
4059 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4060 squash_guid(package->ProductCode,squashed);
4061 msi_reg_set_val_str( hkey2, squashed, NULL );
4063 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4064 squash_guid(package->ProductCode,squashed);
4065 msi_reg_set_val_str( hkey2, squashed, NULL );
4068 msi_free(upgrade_code);
4073 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
4074 if (rc != ERROR_SUCCESS)
4077 RegCloseKey(hudkey);
4079 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
4080 if (rc != ERROR_SUCCESS)
4083 msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
4086 return ERROR_SUCCESS;
4089 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4091 return execute_script(package,INSTALL_SCRIPT);
4094 static UINT msi_unpublish_product(MSIPACKAGE *package)
4096 LPWSTR remove = NULL;
4097 LPWSTR *features = NULL;
4098 BOOL full_uninstall = TRUE;
4099 MSIFEATURE *feature;
4101 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4102 static const WCHAR szAll[] = {'A','L','L',0};
4104 remove = msi_dup_property(package, szRemove);
4106 return ERROR_SUCCESS;
4108 features = msi_split_string(remove, ',');
4112 ERR("REMOVE feature list is empty!\n");
4113 return ERROR_FUNCTION_FAILED;
4116 if (!lstrcmpW(features[0], szAll))
4117 full_uninstall = TRUE;
4120 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4122 if (feature->Action != INSTALLSTATE_ABSENT)
4123 full_uninstall = FALSE;
4127 if (!full_uninstall)
4130 MSIREG_DeleteProductKey(package->ProductCode);
4131 MSIREG_DeleteUserProductKey(package->ProductCode);
4132 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4133 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4134 MSIREG_DeleteUninstallKey(package->ProductCode);
4139 return ERROR_SUCCESS;
4142 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4146 rc = msi_unpublish_product(package);
4147 if (rc != ERROR_SUCCESS)
4150 /* turn off scheduling */
4151 package->script->CurrentlyScripting= FALSE;
4153 /* first do the same as an InstallExecute */
4154 rc = ACTION_InstallExecute(package);
4155 if (rc != ERROR_SUCCESS)
4158 /* then handle Commit Actions */
4159 rc = execute_script(package,COMMIT_SCRIPT);
4164 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4166 static const WCHAR RunOnce[] = {
4167 'S','o','f','t','w','a','r','e','\\',
4168 'M','i','c','r','o','s','o','f','t','\\',
4169 'W','i','n','d','o','w','s','\\',
4170 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4171 'R','u','n','O','n','c','e',0};
4172 static const WCHAR InstallRunOnce[] = {
4173 'S','o','f','t','w','a','r','e','\\',
4174 'M','i','c','r','o','s','o','f','t','\\',
4175 'W','i','n','d','o','w','s','\\',
4176 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4177 'I','n','s','t','a','l','l','e','r','\\',
4178 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4180 static const WCHAR msiexec_fmt[] = {
4182 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4183 '\"','%','s','\"',0};
4184 static const WCHAR install_fmt[] = {
4185 '/','I',' ','\"','%','s','\"',' ',
4186 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4187 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4188 WCHAR buffer[256], sysdir[MAX_PATH];
4190 WCHAR squished_pc[100];
4192 squash_guid(package->ProductCode,squished_pc);
4194 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4195 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4196 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4199 msi_reg_set_val_str( hkey, squished_pc, buffer );
4202 TRACE("Reboot command %s\n",debugstr_w(buffer));
4204 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4205 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4207 msi_reg_set_val_str( hkey, squished_pc, buffer );
4210 return ERROR_INSTALL_SUSPEND;
4213 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4219 * We are currently doing what should be done here in the top level Install
4220 * however for Administrative and uninstalls this step will be needed
4222 if (!package->PackagePath)
4223 return ERROR_SUCCESS;
4225 msi_set_sourcedir_props(package, TRUE);
4227 attrib = GetFileAttributesW(package->db->path);
4228 if (attrib == INVALID_FILE_ATTRIBUTES)
4234 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4235 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4236 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4237 if (rc == ERROR_MORE_DATA)
4239 prompt = msi_alloc(size * sizeof(WCHAR));
4240 MsiSourceListGetInfoW(package->ProductCode, NULL,
4241 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4242 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4245 prompt = strdupW(package->db->path);
4247 msg = generate_error_string(package,1302,1,prompt);
4248 while(attrib == INVALID_FILE_ATTRIBUTES)
4250 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4253 rc = ERROR_INSTALL_USEREXIT;
4256 attrib = GetFileAttributesW(package->db->path);
4262 return ERROR_SUCCESS;
4267 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4274 static const WCHAR szPropKeys[][80] =
4276 {'P','r','o','d','u','c','t','I','D',0},
4277 {'U','S','E','R','N','A','M','E',0},
4278 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4282 static const WCHAR szRegKeys[][80] =
4284 {'P','r','o','d','u','c','t','I','D',0},
4285 {'R','e','g','O','w','n','e','r',0},
4286 {'R','e','g','C','o','m','p','a','n','y',0},
4290 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4292 return ERROR_SUCCESS;
4294 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4295 if (rc != ERROR_SUCCESS)
4298 for( i = 0; szPropKeys[i][0]; i++ )
4300 buffer = msi_dup_property( package, szPropKeys[i] );
4301 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4306 msi_free(productid);
4309 /* FIXME: call ui_actiondata */
4311 return ERROR_SUCCESS;
4315 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4319 package->script->InWhatSequence |= SEQUENCE_EXEC;
4320 rc = ACTION_ProcessExecSequence(package,FALSE);
4325 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4327 MSIPACKAGE *package = (MSIPACKAGE*)param;
4328 LPCWSTR compgroupid=NULL;
4329 LPCWSTR feature=NULL;
4330 LPCWSTR text = NULL;
4331 LPCWSTR qualifier = NULL;
4332 LPCWSTR component = NULL;
4333 LPWSTR advertise = NULL;
4334 LPWSTR output = NULL;
4336 UINT rc = ERROR_SUCCESS;
4341 component = MSI_RecordGetString(rec,3);
4342 comp = get_loaded_component(package,component);
4344 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4345 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4346 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4348 TRACE("Skipping: Component %s not scheduled for install\n",
4349 debugstr_w(component));
4351 return ERROR_SUCCESS;
4354 compgroupid = MSI_RecordGetString(rec,1);
4355 qualifier = MSI_RecordGetString(rec,2);
4357 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4358 if (rc != ERROR_SUCCESS)
4361 text = MSI_RecordGetString(rec,4);
4362 feature = MSI_RecordGetString(rec,5);
4364 advertise = create_component_advertise_string(package, comp, feature);
4366 sz = strlenW(advertise);
4369 sz += lstrlenW(text);
4372 sz *= sizeof(WCHAR);
4374 output = msi_alloc_zero(sz);
4375 strcpyW(output,advertise);
4376 msi_free(advertise);
4379 strcatW(output,text);
4381 msi_reg_set_val_multi_str( hkey, qualifier, output );
4388 uirow = MSI_CreateRecord( 2 );
4389 MSI_RecordSetStringW( uirow, 1, compgroupid );
4390 MSI_RecordSetStringW( uirow, 2, qualifier);
4391 ui_actiondata( package, szPublishComponents, uirow);
4392 msiobj_release( &uirow->hdr );
4393 /* FIXME: call ui_progress? */
4399 * At present I am ignorning the advertised components part of this and only
4400 * focusing on the qualified component sets
4402 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4406 static const WCHAR ExecSeqQuery[] =
4407 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4408 '`','P','u','b','l','i','s','h',
4409 'C','o','m','p','o','n','e','n','t','`',0};
4411 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4412 if (rc != ERROR_SUCCESS)
4413 return ERROR_SUCCESS;
4415 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4416 msiobj_release(&view->hdr);
4421 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4423 MSIPACKAGE *package = (MSIPACKAGE*)param;
4426 SC_HANDLE hscm, service = NULL;
4427 LPCWSTR name, disp, comp, depends, pass;
4428 LPCWSTR load_order, serv_name, key;
4429 DWORD serv_type, start_type;
4432 static const WCHAR query[] =
4433 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4434 '`','C','o','m','p','o','n','e','n','t','`',' ',
4435 'W','H','E','R','E',' ',
4436 '`','C','o','m','p','o','n','e','n','t','`',' ',
4437 '=','\'','%','s','\'',0};
4439 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4442 ERR("Failed to open the SC Manager!\n");
4446 start_type = MSI_RecordGetInteger(rec, 5);
4447 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4450 depends = MSI_RecordGetString(rec, 8);
4451 if (depends && *depends)
4452 FIXME("Dependency list unhandled!\n");
4454 name = MSI_RecordGetString(rec, 2);
4455 disp = MSI_RecordGetString(rec, 3);
4456 serv_type = MSI_RecordGetInteger(rec, 4);
4457 err_control = MSI_RecordGetInteger(rec, 6);
4458 load_order = MSI_RecordGetString(rec, 7);
4459 serv_name = MSI_RecordGetString(rec, 9);
4460 pass = MSI_RecordGetString(rec, 10);
4461 comp = MSI_RecordGetString(rec, 12);
4463 /* fetch the service path */
4464 row = MSI_QueryGetRecord(package->db, query, comp);
4467 ERR("Control query failed!\n");
4471 key = MSI_RecordGetString(row, 6);
4473 file = get_loaded_file(package, key);
4474 msiobj_release(&row->hdr);
4477 ERR("Failed to load the service file\n");
4481 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4482 start_type, err_control, file->TargetPath,
4483 load_order, NULL, NULL, serv_name, pass);
4486 if (GetLastError() != ERROR_SERVICE_EXISTS)
4487 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4491 CloseServiceHandle(service);
4492 CloseServiceHandle(hscm);
4494 return ERROR_SUCCESS;
4497 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4501 static const WCHAR ExecSeqQuery[] =
4502 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4503 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4505 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4506 if (rc != ERROR_SUCCESS)
4507 return ERROR_SUCCESS;
4509 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4510 msiobj_release(&view->hdr);
4515 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4516 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4518 LPCWSTR *vector, *temp_vector;
4522 static const WCHAR separator[] = {'[','~',']',0};
4525 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4530 vector = msi_alloc(sizeof(LPWSTR));
4538 vector[*numargs - 1] = p;
4540 if ((q = strstrW(p, separator)))
4544 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4550 vector = temp_vector;
4559 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4561 MSIPACKAGE *package = (MSIPACKAGE *)param;
4563 SC_HANDLE scm, service = NULL;
4564 LPCWSTR name, *vector = NULL;
4566 DWORD event, numargs;
4567 UINT r = ERROR_FUNCTION_FAILED;
4569 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4570 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4571 return ERROR_SUCCESS;
4573 name = MSI_RecordGetString(rec, 2);
4574 event = MSI_RecordGetInteger(rec, 3);
4575 args = strdupW(MSI_RecordGetString(rec, 4));
4577 if (!(event & msidbServiceControlEventStart))
4578 return ERROR_SUCCESS;
4580 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4583 ERR("Failed to open the service control manager\n");
4587 service = OpenServiceW(scm, name, SERVICE_START);
4590 ERR("Failed to open service %s\n", debugstr_w(name));
4594 vector = msi_service_args_to_vector(args, &numargs);
4596 if (!StartServiceW(service, numargs, vector))
4598 ERR("Failed to start service %s\n", debugstr_w(name));
4605 CloseServiceHandle(service);
4606 CloseServiceHandle(scm);
4613 static UINT ACTION_StartServices( MSIPACKAGE *package )
4618 static const WCHAR query[] = {
4619 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4620 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4622 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4623 if (rc != ERROR_SUCCESS)
4624 return ERROR_SUCCESS;
4626 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4627 msiobj_release(&view->hdr);
4632 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4636 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4638 if (!lstrcmpW(file->File, filename))
4645 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4647 MSIPACKAGE *package = (MSIPACKAGE*)param;
4648 LPWSTR driver, driver_path, ptr;
4649 WCHAR outpath[MAX_PATH];
4650 MSIFILE *driver_file, *setup_file;
4653 UINT r = ERROR_SUCCESS;
4655 static const WCHAR driver_fmt[] = {
4656 'D','r','i','v','e','r','=','%','s',0};
4657 static const WCHAR setup_fmt[] = {
4658 'S','e','t','u','p','=','%','s',0};
4659 static const WCHAR usage_fmt[] = {
4660 'F','i','l','e','U','s','a','g','e','=','1',0};
4662 desc = MSI_RecordGetString(rec, 3);
4664 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4665 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4667 if (!driver_file || !setup_file)
4669 ERR("ODBC Driver entry not found!\n");
4670 return ERROR_FUNCTION_FAILED;
4673 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4674 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4675 lstrlenW(usage_fmt) + 1;
4676 driver = msi_alloc(len * sizeof(WCHAR));
4678 return ERROR_OUTOFMEMORY;
4681 lstrcpyW(ptr, desc);
4682 ptr += lstrlenW(ptr) + 1;
4684 sprintfW(ptr, driver_fmt, driver_file->FileName);
4685 ptr += lstrlenW(ptr) + 1;
4687 sprintfW(ptr, setup_fmt, setup_file->FileName);
4688 ptr += lstrlenW(ptr) + 1;
4690 lstrcpyW(ptr, usage_fmt);
4691 ptr += lstrlenW(ptr) + 1;
4694 driver_path = strdupW(driver_file->TargetPath);
4695 ptr = strrchrW(driver_path, '\\');
4696 if (ptr) *ptr = '\0';
4698 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4699 NULL, ODBC_INSTALL_COMPLETE, &usage))
4701 ERR("Failed to install SQL driver!\n");
4702 r = ERROR_FUNCTION_FAILED;
4706 msi_free(driver_path);
4711 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4713 MSIPACKAGE *package = (MSIPACKAGE*)param;
4714 LPWSTR translator, translator_path, ptr;
4715 WCHAR outpath[MAX_PATH];
4716 MSIFILE *translator_file, *setup_file;
4719 UINT r = ERROR_SUCCESS;
4721 static const WCHAR translator_fmt[] = {
4722 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4723 static const WCHAR setup_fmt[] = {
4724 'S','e','t','u','p','=','%','s',0};
4726 desc = MSI_RecordGetString(rec, 3);
4728 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4729 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4731 if (!translator_file || !setup_file)
4733 ERR("ODBC Translator entry not found!\n");
4734 return ERROR_FUNCTION_FAILED;
4737 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4738 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4739 translator = msi_alloc(len * sizeof(WCHAR));
4741 return ERROR_OUTOFMEMORY;
4744 lstrcpyW(ptr, desc);
4745 ptr += lstrlenW(ptr) + 1;
4747 sprintfW(ptr, translator_fmt, translator_file->FileName);
4748 ptr += lstrlenW(ptr) + 1;
4750 sprintfW(ptr, setup_fmt, setup_file->FileName);
4751 ptr += lstrlenW(ptr) + 1;
4754 translator_path = strdupW(translator_file->TargetPath);
4755 ptr = strrchrW(translator_path, '\\');
4756 if (ptr) *ptr = '\0';
4758 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4759 NULL, ODBC_INSTALL_COMPLETE, &usage))
4761 ERR("Failed to install SQL translator!\n");
4762 r = ERROR_FUNCTION_FAILED;
4765 msi_free(translator);
4766 msi_free(translator_path);
4771 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4774 LPCWSTR desc, driver;
4775 WORD request = ODBC_ADD_SYS_DSN;
4778 UINT r = ERROR_SUCCESS;
4780 static const WCHAR attrs_fmt[] = {
4781 'D','S','N','=','%','s',0 };
4783 desc = MSI_RecordGetString(rec, 3);
4784 driver = MSI_RecordGetString(rec, 4);
4785 registration = MSI_RecordGetInteger(rec, 5);
4787 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4788 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4790 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4791 attrs = msi_alloc(len * sizeof(WCHAR));
4793 return ERROR_OUTOFMEMORY;
4795 sprintfW(attrs, attrs_fmt, desc);
4796 attrs[len - 1] = '\0';
4798 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4800 ERR("Failed to install SQL data source!\n");
4801 r = ERROR_FUNCTION_FAILED;
4809 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4814 static const WCHAR driver_query[] = {
4815 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4816 'O','D','B','C','D','r','i','v','e','r',0 };
4818 static const WCHAR translator_query[] = {
4819 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4820 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4822 static const WCHAR source_query[] = {
4823 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4824 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4826 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4827 if (rc != ERROR_SUCCESS)
4828 return ERROR_SUCCESS;
4830 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4831 msiobj_release(&view->hdr);
4833 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4834 if (rc != ERROR_SUCCESS)
4835 return ERROR_SUCCESS;
4837 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4838 msiobj_release(&view->hdr);
4840 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4841 if (rc != ERROR_SUCCESS)
4842 return ERROR_SUCCESS;
4844 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4845 msiobj_release(&view->hdr);
4850 #define ENV_ACT_SETALWAYS 0x1
4851 #define ENV_ACT_SETABSENT 0x2
4852 #define ENV_ACT_REMOVE 0x4
4853 #define ENV_ACT_REMOVEMATCH 0x8
4855 #define ENV_MOD_MACHINE 0x20000000
4856 #define ENV_MOD_APPEND 0x40000000
4857 #define ENV_MOD_PREFIX 0x80000000
4858 #define ENV_MOD_MASK 0xC0000000
4860 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4862 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4864 LPCWSTR cptr = *name;
4865 LPCWSTR ptr = *value;
4867 static const WCHAR prefix[] = {'[','~',']',0};
4868 static const int prefix_len = 3;
4874 *flags |= ENV_ACT_SETALWAYS;
4875 else if (*cptr == '+')
4876 *flags |= ENV_ACT_SETABSENT;
4877 else if (*cptr == '-')
4878 *flags |= ENV_ACT_REMOVE;
4879 else if (*cptr == '!')
4880 *flags |= ENV_ACT_REMOVEMATCH;
4881 else if (*cptr == '*')
4882 *flags |= ENV_MOD_MACHINE;
4892 ERR("Missing environment variable\n");
4893 return ERROR_FUNCTION_FAILED;
4896 if (!strncmpW(ptr, prefix, prefix_len))
4898 *flags |= ENV_MOD_APPEND;
4899 *value += lstrlenW(prefix);
4901 else if (lstrlenW(*value) >= prefix_len)
4903 ptr += lstrlenW(ptr) - prefix_len;
4904 if (!lstrcmpW(ptr, prefix))
4906 *flags |= ENV_MOD_PREFIX;
4907 /* the "[~]" will be removed by deformat_string */;
4912 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4913 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4914 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4915 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4917 ERR("Invalid flags: %08x\n", *flags);
4918 return ERROR_FUNCTION_FAILED;
4921 return ERROR_SUCCESS;
4924 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4926 MSIPACKAGE *package = param;
4927 LPCWSTR name, value, comp;
4928 LPWSTR data = NULL, newval = NULL;
4929 LPWSTR deformatted = NULL, ptr;
4930 DWORD flags, type, size;
4932 HKEY env = NULL, root;
4933 LPCWSTR environment;
4935 static const WCHAR user_env[] =
4936 {'E','n','v','i','r','o','n','m','e','n','t',0};
4937 static const WCHAR machine_env[] =
4938 {'S','y','s','t','e','m','\\',
4939 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4940 'C','o','n','t','r','o','l','\\',
4941 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4942 'E','n','v','i','r','o','n','m','e','n','t',0};
4943 static const WCHAR semicolon[] = {';',0};
4945 name = MSI_RecordGetString(rec, 2);
4946 value = MSI_RecordGetString(rec, 3);
4947 comp = MSI_RecordGetString(rec, 4);
4949 res = env_set_flags(&name, &value, &flags);
4950 if (res != ERROR_SUCCESS)
4953 deformat_string(package, value, &deformatted);
4956 res = ERROR_OUTOFMEMORY;
4960 value = deformatted;
4962 if (flags & ENV_MOD_MACHINE)
4964 environment = machine_env;
4965 root = HKEY_LOCAL_MACHINE;
4969 environment = user_env;
4970 root = HKEY_CURRENT_USER;
4973 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
4974 KEY_ALL_ACCESS, NULL, &env, NULL);
4975 if (res != ERROR_SUCCESS)
4978 if (flags & ENV_ACT_REMOVE)
4979 FIXME("Not removing environment variable on uninstall!\n");
4982 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
4983 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
4984 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
4987 if (res != ERROR_FILE_NOT_FOUND)
4989 if (flags & ENV_ACT_SETABSENT)
4991 res = ERROR_SUCCESS;
4995 data = msi_alloc(size);
4999 return ERROR_OUTOFMEMORY;
5002 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5003 if (res != ERROR_SUCCESS)
5006 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5008 res = RegDeleteKeyW(env, name);
5012 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5013 newval = msi_alloc(size);
5017 res = ERROR_OUTOFMEMORY;
5021 if (!(flags & ENV_MOD_MASK))
5022 lstrcpyW(newval, value);
5025 if (flags & ENV_MOD_PREFIX)
5027 lstrcpyW(newval, value);
5028 lstrcatW(newval, semicolon);
5029 ptr = newval + lstrlenW(value) + 1;
5032 lstrcpyW(ptr, data);
5034 if (flags & ENV_MOD_APPEND)
5036 lstrcatW(newval, semicolon);
5037 lstrcatW(newval, value);
5043 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5044 newval = msi_alloc(size);
5047 res = ERROR_OUTOFMEMORY;
5051 lstrcpyW(newval, value);
5054 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5055 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5058 if (env) RegCloseKey(env);
5059 msi_free(deformatted);
5065 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5069 static const WCHAR ExecSeqQuery[] =
5070 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5071 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5072 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5073 if (rc != ERROR_SUCCESS)
5074 return ERROR_SUCCESS;
5076 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5077 msiobj_release(&view->hdr);
5082 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5093 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5097 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5098 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5100 WARN("Source or dest is directory, not moving\n");
5104 if (options == msidbMoveFileOptionsMove)
5106 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5107 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5110 WARN("MoveFile failed: %d\n", GetLastError());
5116 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5117 ret = CopyFileW(source, dest, FALSE);
5120 WARN("CopyFile failed: %d\n", GetLastError());
5128 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5131 DWORD dirlen, pathlen;
5133 ptr = strrchrW(wildcard, '\\');
5134 dirlen = ptr - wildcard + 1;
5136 pathlen = dirlen + lstrlenW(filename) + 1;
5137 path = msi_alloc(pathlen * sizeof(WCHAR));
5139 lstrcpynW(path, wildcard, dirlen + 1);
5140 lstrcatW(path, filename);
5145 static void free_file_entry(FILE_LIST *file)
5147 msi_free(file->source);
5148 msi_free(file->dest);
5152 static void free_list(FILE_LIST *list)
5154 while (!list_empty(&list->entry))
5156 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5158 list_remove(&file->entry);
5159 free_file_entry(file);
5163 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5165 FILE_LIST *new, *file;
5166 LPWSTR ptr, filename;
5169 new = msi_alloc_zero(sizeof(FILE_LIST));
5173 new->source = strdupW(source);
5174 ptr = strrchrW(dest, '\\') + 1;
5175 filename = strrchrW(new->source, '\\') + 1;
5177 new->sourcename = filename;
5180 new->destname = ptr;
5182 new->destname = new->sourcename;
5184 size = (ptr - dest) + lstrlenW(filename) + 1;
5185 new->dest = msi_alloc(size * sizeof(WCHAR));
5188 free_file_entry(new);
5192 lstrcpynW(new->dest, dest, ptr - dest + 1);
5193 lstrcatW(new->dest, filename);
5195 if (list_empty(&files->entry))
5197 list_add_head(&files->entry, &new->entry);
5201 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5203 if (lstrcmpW(source, file->source) < 0)
5205 list_add_before(&file->entry, &new->entry);
5210 list_add_after(&file->entry, &new->entry);
5214 BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5216 WIN32_FIND_DATAW wfd;
5220 FILE_LIST files, *file;
5223 hfile = FindFirstFileW(source, &wfd);
5224 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5226 list_init(&files.entry);
5228 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5230 if (is_dot_dir(wfd.cFileName)) continue;
5232 path = wildcard_to_file(source, wfd.cFileName);
5239 add_wildcard(&files, path, dest);
5243 /* only the first wildcard match gets renamed to dest */
5244 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5245 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5246 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5253 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5255 while (!list_empty(&files.entry))
5257 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5259 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5261 list_remove(&file->entry);
5262 free_file_entry(file);
5273 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5275 MSIPACKAGE *package = param;
5277 LPCWSTR sourcename, destname;
5278 LPWSTR sourcedir = NULL, destdir = NULL;
5279 LPWSTR source = NULL, dest = NULL;
5282 BOOL ret, wildcards;
5284 static const WCHAR backslash[] = {'\\',0};
5286 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5287 if (!comp || !comp->Enabled ||
5288 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5290 TRACE("Component not set for install, not moving file\n");
5291 return ERROR_SUCCESS;
5294 sourcename = MSI_RecordGetString(rec, 3);
5295 destname = MSI_RecordGetString(rec, 4);
5296 options = MSI_RecordGetInteger(rec, 7);
5298 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5302 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5308 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5311 source = strdupW(sourcedir);
5317 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5318 source = msi_alloc(size * sizeof(WCHAR));
5322 lstrcpyW(source, sourcedir);
5323 if (source[lstrlenW(source) - 1] != '\\')
5324 lstrcatW(source, backslash);
5325 lstrcatW(source, sourcename);
5328 wildcards = strchrW(source, '*') || strchrW(source, '?');
5330 if (!destname && !wildcards)
5332 destname = strdupW(sourcename);
5339 size = lstrlenW(destname);
5341 size += lstrlenW(destdir) + 2;
5342 dest = msi_alloc(size * sizeof(WCHAR));
5346 lstrcpyW(dest, destdir);
5347 if (dest[lstrlenW(dest) - 1] != '\\')
5348 lstrcatW(dest, backslash);
5351 lstrcatW(dest, destname);
5353 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5355 ret = CreateDirectoryW(destdir, NULL);
5358 WARN("CreateDirectory failed: %d\n", GetLastError());
5359 return ERROR_SUCCESS;
5364 msi_move_file(source, dest, options);
5366 move_files_wildcard(source, dest, options);
5369 msi_free(sourcedir);
5374 return ERROR_SUCCESS;
5377 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5382 static const WCHAR ExecSeqQuery[] =
5383 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5384 '`','M','o','v','e','F','i','l','e','`',0};
5386 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5387 if (rc != ERROR_SUCCESS)
5388 return ERROR_SUCCESS;
5390 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5391 msiobj_release(&view->hdr);
5396 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5397 LPCSTR action, LPCWSTR table )
5399 static const WCHAR query[] = {
5400 'S','E','L','E','C','T',' ','*',' ',
5401 'F','R','O','M',' ','`','%','s','`',0 };
5402 MSIQUERY *view = NULL;
5406 r = MSI_OpenQuery( package->db, &view, query, table );
5407 if (r == ERROR_SUCCESS)
5409 r = MSI_IterateRecords(view, &count, NULL, package);
5410 msiobj_release(&view->hdr);
5414 FIXME("%s -> %u ignored %s table values\n",
5415 action, count, debugstr_w(table));
5417 return ERROR_SUCCESS;
5420 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5422 TRACE("%p\n", package);
5423 return ERROR_SUCCESS;
5426 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5428 static const WCHAR table[] =
5429 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5430 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5433 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5435 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5436 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5439 static UINT ACTION_BindImage( MSIPACKAGE *package )
5441 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5442 return msi_unimplemented_action_stub( package, "BindImage", table );
5445 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5447 static const WCHAR table[] = {
5448 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5449 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5452 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5454 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5455 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5458 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5460 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5461 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5464 static UINT ACTION_StopServices( MSIPACKAGE *package )
5466 static const WCHAR table[] = {
5467 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5468 return msi_unimplemented_action_stub( package, "StopServices", table );
5471 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5473 static const WCHAR table[] = {
5474 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5475 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5477 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5479 static const WCHAR table[] = {
5480 'P','r','o','d','u','c','t','I','D',0 };
5481 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5484 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5486 static const WCHAR table[] = {
5487 'E','n','v','i','r','o','n','m','e','n','t',0 };
5488 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5491 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5493 static const WCHAR table[] = {
5494 'M','s','i','A','s','s','e','m','b','l','y',0 };
5495 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5498 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5500 static const WCHAR table[] = {
5501 'M','s','i','A','s','s','e','m','b','l','y',0 };
5502 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5505 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5507 static const WCHAR table[] = { 'F','o','n','t',0 };
5508 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5511 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5513 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5514 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5517 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5519 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5520 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5523 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5525 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5526 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5529 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5531 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5532 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5535 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5537 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5538 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5541 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5543 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5544 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5547 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5549 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5550 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5553 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5555 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5556 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5559 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5561 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5562 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5565 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5567 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5568 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5571 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5573 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5574 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5577 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5579 static const WCHAR table[] = { 'A','p','p','I','d',0 };
5580 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5583 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5585 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5586 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5589 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5591 static const WCHAR table[] = { 'M','I','M','E',0 };
5592 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5595 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5597 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5598 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5601 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5603 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5604 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5607 static const struct _actions StandardActions[] = {
5608 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5609 { szAppSearch, ACTION_AppSearch },
5610 { szBindImage, ACTION_BindImage },
5611 { szCCPSearch, ACTION_CCPSearch },
5612 { szCostFinalize, ACTION_CostFinalize },
5613 { szCostInitialize, ACTION_CostInitialize },
5614 { szCreateFolders, ACTION_CreateFolders },
5615 { szCreateShortcuts, ACTION_CreateShortcuts },
5616 { szDeleteServices, ACTION_DeleteServices },
5617 { szDisableRollback, NULL },
5618 { szDuplicateFiles, ACTION_DuplicateFiles },
5619 { szExecuteAction, ACTION_ExecuteAction },
5620 { szFileCost, ACTION_FileCost },
5621 { szFindRelatedProducts, ACTION_FindRelatedProducts },
5622 { szForceReboot, ACTION_ForceReboot },
5623 { szInstallAdminPackage, NULL },
5624 { szInstallExecute, ACTION_InstallExecute },
5625 { szInstallExecuteAgain, ACTION_InstallExecute },
5626 { szInstallFiles, ACTION_InstallFiles},
5627 { szInstallFinalize, ACTION_InstallFinalize },
5628 { szInstallInitialize, ACTION_InstallInitialize },
5629 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5630 { szInstallValidate, ACTION_InstallValidate },
5631 { szIsolateComponents, ACTION_IsolateComponents },
5632 { szLaunchConditions, ACTION_LaunchConditions },
5633 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5634 { szMoveFiles, ACTION_MoveFiles },
5635 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5636 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5637 { szInstallODBC, ACTION_InstallODBC },
5638 { szInstallServices, ACTION_InstallServices },
5639 { szPatchFiles, ACTION_PatchFiles },
5640 { szProcessComponents, ACTION_ProcessComponents },
5641 { szPublishComponents, ACTION_PublishComponents },
5642 { szPublishFeatures, ACTION_PublishFeatures },
5643 { szPublishProduct, ACTION_PublishProduct },
5644 { szRegisterClassInfo, ACTION_RegisterClassInfo },
5645 { szRegisterComPlus, ACTION_RegisterComPlus},
5646 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5647 { szRegisterFonts, ACTION_RegisterFonts },
5648 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5649 { szRegisterProduct, ACTION_RegisterProduct },
5650 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5651 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5652 { szRegisterUser, ACTION_RegisterUser },
5653 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5654 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5655 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5656 { szRemoveFiles, ACTION_RemoveFiles },
5657 { szRemoveFolders, ACTION_RemoveFolders },
5658 { szRemoveIniValues, ACTION_RemoveIniValues },
5659 { szRemoveODBC, ACTION_RemoveODBC },
5660 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5661 { szRemoveShortcuts, ACTION_RemoveShortcuts },
5662 { szResolveSource, ACTION_ResolveSource },
5663 { szRMCCPSearch, ACTION_RMCCPSearch },
5664 { szScheduleReboot, NULL },
5665 { szSelfRegModules, ACTION_SelfRegModules },
5666 { szSelfUnregModules, ACTION_SelfUnregModules },
5667 { szSetODBCFolders, NULL },
5668 { szStartServices, ACTION_StartServices },
5669 { szStopServices, ACTION_StopServices },
5670 { szUnpublishComponents, ACTION_UnpublishComponents },
5671 { szUnpublishFeatures, ACTION_UnpublishFeatures },
5672 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5673 { szUnregisterComPlus, ACTION_UnregisterComPlus },
5674 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5675 { szUnregisterFonts, ACTION_UnregisterFonts },
5676 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5677 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5678 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5679 { szValidateProductID, ACTION_ValidateProductID },
5680 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5681 { szWriteIniValues, ACTION_WriteIniValues },
5682 { szWriteRegistryValues, ACTION_WriteRegistryValues },