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 /****************************************************
612 * TOP level entry points
613 *****************************************************/
615 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
616 LPCWSTR szCommandLine )
619 BOOL ui = FALSE, ui_exists;
620 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
621 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
622 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
624 MSI_SetPropertyW(package, szAction, szInstall);
626 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
628 package->script->InWhatSequence = SEQUENCE_INSTALL;
632 LPWSTR p, check, dir;
635 dir = strdupW(szPackagePath);
636 p = strrchrW(dir, '\\');
640 file = szPackagePath + (p - dir);
645 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
646 GetCurrentDirectoryW(MAX_PATH, dir);
647 lstrcatW(dir, cszbs);
648 file = szPackagePath;
651 msi_free( package->PackagePath );
652 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
653 if (!package->PackagePath)
656 return ERROR_OUTOFMEMORY;
659 lstrcpyW(package->PackagePath, dir);
660 lstrcatW(package->PackagePath, file);
662 check = msi_dup_property( package, cszSourceDir );
664 MSI_SetPropertyW(package, cszSourceDir, dir);
667 check = msi_dup_property( package, cszSOURCEDIR );
669 MSI_SetPropertyW(package, cszSOURCEDIR, dir);
675 msi_parse_command_line( package, szCommandLine );
677 msi_apply_transforms( package );
678 msi_apply_patches( package );
680 /* properties may have been added by a transform */
681 msi_clone_properties( package );
683 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
685 package->script->InWhatSequence |= SEQUENCE_UI;
686 rc = ACTION_ProcessUISequence(package);
688 ui_exists = ui_sequence_exists(package);
689 if (rc == ERROR_SUCCESS || !ui_exists)
691 package->script->InWhatSequence |= SEQUENCE_EXEC;
692 rc = ACTION_ProcessExecSequence(package,ui_exists);
696 rc = ACTION_ProcessExecSequence(package,FALSE);
698 package->script->CurrentlyScripting= FALSE;
700 /* process the ending type action */
701 if (rc == ERROR_SUCCESS)
702 ACTION_PerformActionSequence(package,-1,ui);
703 else if (rc == ERROR_INSTALL_USEREXIT)
704 ACTION_PerformActionSequence(package,-2,ui);
705 else if (rc == ERROR_INSTALL_SUSPEND)
706 ACTION_PerformActionSequence(package,-4,ui);
708 ACTION_PerformActionSequence(package,-3,ui);
710 /* finish up running custom actions */
711 ACTION_FinishCustomActions(package);
716 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
718 UINT rc = ERROR_SUCCESS;
720 static const WCHAR ExecSeqQuery[] =
721 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
722 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
723 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
724 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
726 static const WCHAR UISeqQuery[] =
727 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
728 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
729 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
730 ' ', '=',' ','%','i',0};
733 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
735 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
739 LPCWSTR action, cond;
741 TRACE("Running the actions\n");
743 /* check conditions */
744 cond = MSI_RecordGetString(row,2);
746 /* this is a hack to skip errors in the condition code */
747 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
750 action = MSI_RecordGetString(row,1);
753 ERR("failed to fetch action\n");
754 rc = ERROR_FUNCTION_FAILED;
759 rc = ACTION_PerformUIAction(package,action,-1);
761 rc = ACTION_PerformAction(package,action,-1,FALSE);
763 msiobj_release(&row->hdr);
774 } iterate_action_param;
776 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
778 iterate_action_param *iap= (iterate_action_param*)param;
780 LPCWSTR cond, action;
782 action = MSI_RecordGetString(row,1);
785 ERR("Error is retrieving action name\n");
786 return ERROR_FUNCTION_FAILED;
789 /* check conditions */
790 cond = MSI_RecordGetString(row,2);
792 /* this is a hack to skip errors in the condition code */
793 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
795 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
796 return ERROR_SUCCESS;
800 rc = ACTION_PerformUIAction(iap->package,action,-1);
802 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
804 msi_dialog_check_messages( NULL );
806 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
807 rc = iap->package->CurrentInstallState;
809 if (rc == ERROR_FUNCTION_NOT_CALLED)
812 if (rc != ERROR_SUCCESS)
813 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
818 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
822 static const WCHAR query[] =
823 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
825 ' ','W','H','E','R','E',' ',
826 '`','S','e','q','u','e','n','c','e','`',' ',
827 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
828 '`','S','e','q','u','e','n','c','e','`',0};
829 iterate_action_param iap;
832 * FIXME: probably should be checking UILevel in the
833 * ACTION_PerformUIAction/ACTION_PerformAction
834 * rather than saving the UI level here. Those
835 * two functions can be merged too.
837 iap.package = package;
840 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
842 r = MSI_OpenQuery( package->db, &view, query, szTable );
843 if (r == ERROR_SUCCESS)
845 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
846 msiobj_release(&view->hdr);
852 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
856 static const WCHAR ExecSeqQuery[] =
857 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
858 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
859 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
860 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
861 'O','R','D','E','R',' ', 'B','Y',' ',
862 '`','S','e','q','u','e','n','c','e','`',0 };
864 static const WCHAR IVQuery[] =
865 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
866 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
867 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
868 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
869 ' ','\'', 'I','n','s','t','a','l','l',
870 'V','a','l','i','d','a','t','e','\'', 0};
872 iterate_action_param iap;
874 iap.package = package;
877 if (package->script->ExecuteSequenceRun)
879 TRACE("Execute Sequence already Run\n");
880 return ERROR_SUCCESS;
883 package->script->ExecuteSequenceRun = TRUE;
885 /* get the sequence number */
888 row = MSI_QueryGetRecord(package->db, IVQuery);
890 return ERROR_FUNCTION_FAILED;
891 seq = MSI_RecordGetInteger(row,1);
892 msiobj_release(&row->hdr);
895 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
896 if (rc == ERROR_SUCCESS)
898 TRACE("Running the actions\n");
900 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
901 msiobj_release(&view->hdr);
907 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
911 static const WCHAR ExecSeqQuery [] =
912 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
913 '`','I','n','s','t','a','l','l',
914 'U','I','S','e','q','u','e','n','c','e','`',
915 ' ','W','H','E','R','E',' ',
916 '`','S','e','q','u','e','n','c','e','`',' ',
917 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
918 '`','S','e','q','u','e','n','c','e','`',0};
919 iterate_action_param iap;
921 iap.package = package;
924 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
926 if (rc == ERROR_SUCCESS)
928 TRACE("Running the actions\n");
930 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
931 msiobj_release(&view->hdr);
937 /********************************************************
938 * ACTION helper functions and functions that perform the actions
939 *******************************************************/
940 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
941 UINT* rc, BOOL force )
947 if (!run && !package->script->CurrentlyScripting)
952 if (strcmpW(action,szInstallFinalize) == 0 ||
953 strcmpW(action,szInstallExecute) == 0 ||
954 strcmpW(action,szInstallExecuteAgain) == 0)
959 while (StandardActions[i].action != NULL)
961 if (strcmpW(StandardActions[i].action, action)==0)
965 ui_actioninfo(package, action, TRUE, 0);
966 *rc = schedule_action(package,INSTALL_SCRIPT,action);
967 ui_actioninfo(package, action, FALSE, *rc);
971 ui_actionstart(package, action);
972 if (StandardActions[i].handler)
974 *rc = StandardActions[i].handler(package);
978 FIXME("unhandled standard action %s\n",debugstr_w(action));
990 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
991 UINT* rc, UINT script, BOOL force )
996 arc = ACTION_CustomAction(package, action, script, force);
998 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1007 * A lot of actions are really important even if they don't do anything
1008 * explicit... Lots of properties are set at the beginning of the installation
1009 * CostFinalize does a bunch of work to translate the directories and such
1011 * But until I get write access to the database that is hard, so I am going to
1012 * hack it to see if I can get something to run.
1014 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1016 UINT rc = ERROR_SUCCESS;
1019 TRACE("Performing action (%s)\n",debugstr_w(action));
1021 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1024 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1028 FIXME("unhandled msi action %s\n",debugstr_w(action));
1029 rc = ERROR_FUNCTION_NOT_CALLED;
1035 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1037 UINT rc = ERROR_SUCCESS;
1038 BOOL handled = FALSE;
1040 TRACE("Performing action (%s)\n",debugstr_w(action));
1042 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1045 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1047 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1052 FIXME("unhandled msi action %s\n",debugstr_w(action));
1053 rc = ERROR_FUNCTION_NOT_CALLED;
1061 * Actual Action Handlers
1064 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1066 MSIPACKAGE *package = (MSIPACKAGE*)param;
1072 dir = MSI_RecordGetString(row,1);
1075 ERR("Unable to get folder id\n");
1076 return ERROR_SUCCESS;
1079 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1082 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1083 return ERROR_SUCCESS;
1086 TRACE("Folder is %s\n",debugstr_w(full_path));
1089 uirow = MSI_CreateRecord(1);
1090 MSI_RecordSetStringW(uirow,1,full_path);
1091 ui_actiondata(package,szCreateFolders,uirow);
1092 msiobj_release( &uirow->hdr );
1094 if (folder->State == 0)
1095 create_full_pathW(full_path);
1099 msi_free(full_path);
1100 return ERROR_SUCCESS;
1103 /* FIXME: probably should merge this with the above function */
1104 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1106 UINT rc = ERROR_SUCCESS;
1108 LPWSTR install_path;
1110 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1112 return ERROR_FUNCTION_FAILED;
1114 /* create the path */
1115 if (folder->State == 0)
1117 create_full_pathW(install_path);
1120 msi_free(install_path);
1125 UINT msi_create_component_directories( MSIPACKAGE *package )
1129 /* create all the folders required by the components are going to install */
1130 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1132 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1134 msi_create_directory( package, comp->Directory );
1137 return ERROR_SUCCESS;
1141 * Also we cannot enable/disable components either, so for now I am just going
1142 * to do all the directories for all the components.
1144 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1146 static const WCHAR ExecSeqQuery[] =
1147 {'S','E','L','E','C','T',' ',
1148 '`','D','i','r','e','c','t','o','r','y','_','`',
1149 ' ','F','R','O','M',' ',
1150 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1154 /* create all the empty folders specified in the CreateFolder table */
1155 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1156 if (rc != ERROR_SUCCESS)
1157 return ERROR_SUCCESS;
1159 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1160 msiobj_release(&view->hdr);
1162 msi_create_component_directories( package );
1167 static UINT load_component( MSIRECORD *row, LPVOID param )
1169 MSIPACKAGE *package = param;
1172 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1174 return ERROR_FUNCTION_FAILED;
1176 list_add_tail( &package->components, &comp->entry );
1178 /* fill in the data */
1179 comp->Component = msi_dup_record_field( row, 1 );
1181 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1183 comp->ComponentId = msi_dup_record_field( row, 2 );
1184 comp->Directory = msi_dup_record_field( row, 3 );
1185 comp->Attributes = MSI_RecordGetInteger(row,4);
1186 comp->Condition = msi_dup_record_field( row, 5 );
1187 comp->KeyPath = msi_dup_record_field( row, 6 );
1189 comp->Installed = INSTALLSTATE_UNKNOWN;
1190 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1192 return ERROR_SUCCESS;
1195 static UINT load_all_components( MSIPACKAGE *package )
1197 static const WCHAR query[] = {
1198 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1199 '`','C','o','m','p','o','n','e','n','t','`',0 };
1203 if (!list_empty(&package->components))
1204 return ERROR_SUCCESS;
1206 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1207 if (r != ERROR_SUCCESS)
1210 r = MSI_IterateRecords(view, NULL, load_component, package);
1211 msiobj_release(&view->hdr);
1216 MSIPACKAGE *package;
1217 MSIFEATURE *feature;
1220 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1224 cl = msi_alloc( sizeof (*cl) );
1226 return ERROR_NOT_ENOUGH_MEMORY;
1227 cl->component = comp;
1228 list_add_tail( &feature->Components, &cl->entry );
1230 return ERROR_SUCCESS;
1233 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1237 fl = msi_alloc( sizeof(*fl) );
1239 return ERROR_NOT_ENOUGH_MEMORY;
1240 fl->feature = child;
1241 list_add_tail( &parent->Children, &fl->entry );
1243 return ERROR_SUCCESS;
1246 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1248 _ilfs* ilfs= (_ilfs*)param;
1252 component = MSI_RecordGetString(row,1);
1254 /* check to see if the component is already loaded */
1255 comp = get_loaded_component( ilfs->package, component );
1258 ERR("unknown component %s\n", debugstr_w(component));
1259 return ERROR_FUNCTION_FAILED;
1262 add_feature_component( ilfs->feature, comp );
1263 comp->Enabled = TRUE;
1265 return ERROR_SUCCESS;
1268 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1270 MSIFEATURE *feature;
1272 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1274 if ( !lstrcmpW( feature->Feature, name ) )
1281 static UINT load_feature(MSIRECORD * row, LPVOID param)
1283 MSIPACKAGE* package = (MSIPACKAGE*)param;
1284 MSIFEATURE* feature;
1285 static const WCHAR Query1[] =
1286 {'S','E','L','E','C','T',' ',
1287 '`','C','o','m','p','o','n','e','n','t','_','`',
1288 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1289 'C','o','m','p','o','n','e','n','t','s','`',' ',
1290 'W','H','E','R','E',' ',
1291 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1296 /* fill in the data */
1298 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1300 return ERROR_NOT_ENOUGH_MEMORY;
1302 list_init( &feature->Children );
1303 list_init( &feature->Components );
1305 feature->Feature = msi_dup_record_field( row, 1 );
1307 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1309 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1310 feature->Title = msi_dup_record_field( row, 3 );
1311 feature->Description = msi_dup_record_field( row, 4 );
1313 if (!MSI_RecordIsNull(row,5))
1314 feature->Display = MSI_RecordGetInteger(row,5);
1316 feature->Level= MSI_RecordGetInteger(row,6);
1317 feature->Directory = msi_dup_record_field( row, 7 );
1318 feature->Attributes = MSI_RecordGetInteger(row,8);
1320 feature->Installed = INSTALLSTATE_UNKNOWN;
1321 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1323 list_add_tail( &package->features, &feature->entry );
1325 /* load feature components */
1327 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1328 if (rc != ERROR_SUCCESS)
1329 return ERROR_SUCCESS;
1331 ilfs.package = package;
1332 ilfs.feature = feature;
1334 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1335 msiobj_release(&view->hdr);
1337 return ERROR_SUCCESS;
1340 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1342 MSIPACKAGE* package = (MSIPACKAGE*)param;
1343 MSIFEATURE *parent, *child;
1345 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1347 return ERROR_FUNCTION_FAILED;
1349 if (!child->Feature_Parent)
1350 return ERROR_SUCCESS;
1352 parent = find_feature_by_name( package, child->Feature_Parent );
1354 return ERROR_FUNCTION_FAILED;
1356 add_feature_child( parent, child );
1357 return ERROR_SUCCESS;
1360 static UINT load_all_features( MSIPACKAGE *package )
1362 static const WCHAR query[] = {
1363 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1364 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1365 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1369 if (!list_empty(&package->features))
1370 return ERROR_SUCCESS;
1372 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1373 if (r != ERROR_SUCCESS)
1376 r = MSI_IterateRecords( view, NULL, load_feature, package );
1377 if (r != ERROR_SUCCESS)
1380 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1381 msiobj_release( &view->hdr );
1386 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1397 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1399 static const WCHAR query[] = {
1400 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1401 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1402 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1403 MSIQUERY *view = NULL;
1404 MSIRECORD *row = NULL;
1407 TRACE("%s\n", debugstr_w(file->File));
1409 r = MSI_OpenQuery(package->db, &view, query, file->File);
1410 if (r != ERROR_SUCCESS)
1413 r = MSI_ViewExecute(view, NULL);
1414 if (r != ERROR_SUCCESS)
1417 r = MSI_ViewFetch(view, &row);
1418 if (r != ERROR_SUCCESS)
1421 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1422 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1423 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1424 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1425 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1428 if (view) msiobj_release(&view->hdr);
1429 if (row) msiobj_release(&row->hdr);
1433 static UINT load_file(MSIRECORD *row, LPVOID param)
1435 MSIPACKAGE* package = (MSIPACKAGE*)param;
1439 /* fill in the data */
1441 file = msi_alloc_zero( sizeof (MSIFILE) );
1443 return ERROR_NOT_ENOUGH_MEMORY;
1445 file->File = msi_dup_record_field( row, 1 );
1447 component = MSI_RecordGetString( row, 2 );
1448 file->Component = get_loaded_component( package, component );
1450 if (!file->Component)
1451 ERR("Unfound Component %s\n",debugstr_w(component));
1453 file->FileName = msi_dup_record_field( row, 3 );
1454 reduce_to_longfilename( file->FileName );
1456 file->ShortName = msi_dup_record_field( row, 3 );
1457 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1459 file->FileSize = MSI_RecordGetInteger( row, 4 );
1460 file->Version = msi_dup_record_field( row, 5 );
1461 file->Language = msi_dup_record_field( row, 6 );
1462 file->Attributes = MSI_RecordGetInteger( row, 7 );
1463 file->Sequence = MSI_RecordGetInteger( row, 8 );
1465 file->state = msifs_invalid;
1467 /* if the compressed bits are not set in the file attributes,
1468 * then read the information from the package word count property
1470 if (file->Attributes & msidbFileAttributesCompressed)
1472 file->IsCompressed = TRUE;
1474 else if (file->Attributes & msidbFileAttributesNoncompressed)
1476 file->IsCompressed = FALSE;
1480 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1483 load_file_hash(package, file);
1485 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1487 list_add_tail( &package->files, &file->entry );
1489 return ERROR_SUCCESS;
1492 static UINT load_all_files(MSIPACKAGE *package)
1496 static const WCHAR Query[] =
1497 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1498 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1499 '`','S','e','q','u','e','n','c','e','`', 0};
1501 if (!list_empty(&package->files))
1502 return ERROR_SUCCESS;
1504 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1505 if (rc != ERROR_SUCCESS)
1506 return ERROR_SUCCESS;
1508 rc = MSI_IterateRecords(view, NULL, load_file, package);
1509 msiobj_release(&view->hdr);
1511 return ERROR_SUCCESS;
1514 static UINT load_folder( MSIRECORD *row, LPVOID param )
1516 MSIPACKAGE *package = param;
1517 static const WCHAR szDot[] = { '.',0 };
1518 static WCHAR szEmpty[] = { 0 };
1519 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1522 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1524 return ERROR_NOT_ENOUGH_MEMORY;
1526 folder->Directory = msi_dup_record_field( row, 1 );
1528 TRACE("%s\n", debugstr_w(folder->Directory));
1530 p = msi_dup_record_field(row, 3);
1532 /* split src and target dir */
1534 src_short = folder_split_path( p, ':' );
1536 /* split the long and short paths */
1537 tgt_long = folder_split_path( tgt_short, '|' );
1538 src_long = folder_split_path( src_short, '|' );
1540 /* check for no-op dirs */
1541 if (!lstrcmpW(szDot, tgt_short))
1542 tgt_short = szEmpty;
1543 if (!lstrcmpW(szDot, src_short))
1544 src_short = szEmpty;
1547 tgt_long = tgt_short;
1550 src_short = tgt_short;
1551 src_long = tgt_long;
1555 src_long = src_short;
1557 /* FIXME: use the target short path too */
1558 folder->TargetDefault = strdupW(tgt_long);
1559 folder->SourceShortPath = strdupW(src_short);
1560 folder->SourceLongPath = strdupW(src_long);
1563 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1564 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1565 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1567 folder->Parent = msi_dup_record_field( row, 2 );
1569 folder->Property = msi_dup_property( package, folder->Directory );
1571 list_add_tail( &package->folders, &folder->entry );
1573 TRACE("returning %p\n", folder);
1575 return ERROR_SUCCESS;
1578 static UINT load_all_folders( MSIPACKAGE *package )
1580 static const WCHAR query[] = {
1581 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1582 '`','D','i','r','e','c','t','o','r','y','`',0 };
1586 if (!list_empty(&package->folders))
1587 return ERROR_SUCCESS;
1589 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1590 if (r != ERROR_SUCCESS)
1593 r = MSI_IterateRecords(view, NULL, load_folder, package);
1594 msiobj_release(&view->hdr);
1599 * I am not doing any of the costing functionality yet.
1600 * Mostly looking at doing the Component and Feature loading
1602 * The native MSI does A LOT of modification to tables here. Mostly adding
1603 * a lot of temporary columns to the Feature and Component tables.
1605 * note: Native msi also tracks the short filename. But I am only going to
1606 * track the long ones. Also looking at this directory table
1607 * it appears that the directory table does not get the parents
1608 * resolved base on property only based on their entries in the
1611 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1613 static const WCHAR szCosting[] =
1614 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1615 static const WCHAR szZero[] = { '0', 0 };
1617 MSI_SetPropertyW(package, szCosting, szZero);
1618 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1620 load_all_components( package );
1621 load_all_features( package );
1622 load_all_files( package );
1623 load_all_folders( package );
1625 return ERROR_SUCCESS;
1628 static UINT execute_script(MSIPACKAGE *package, UINT script )
1631 UINT rc = ERROR_SUCCESS;
1633 TRACE("Executing Script %i\n",script);
1635 if (!package->script)
1637 ERR("no script!\n");
1638 return ERROR_FUNCTION_FAILED;
1641 for (i = 0; i < package->script->ActionCount[script]; i++)
1644 action = package->script->Actions[script][i];
1645 ui_actionstart(package, action);
1646 TRACE("Executing Action (%s)\n",debugstr_w(action));
1647 rc = ACTION_PerformAction(package, action, script, TRUE);
1648 if (rc != ERROR_SUCCESS)
1651 msi_free_action_script(package, script);
1655 static UINT ACTION_FileCost(MSIPACKAGE *package)
1657 return ERROR_SUCCESS;
1660 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1664 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1668 if (!comp->ComponentId)
1671 res = MsiGetComponentPathW( package->ProductCode,
1672 comp->ComponentId, NULL, NULL);
1674 res = INSTALLSTATE_ABSENT;
1675 comp->Installed = res;
1679 /* scan for and update current install states */
1680 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1683 MSIFEATURE *feature;
1685 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1688 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1690 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1692 comp= cl->component;
1694 if (!comp->ComponentId)
1696 res = INSTALLSTATE_ABSENT;
1700 if (res == INSTALLSTATE_ABSENT)
1701 res = comp->Installed;
1704 if (res == comp->Installed)
1707 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1708 res != INSTALLSTATE_SOURCE)
1710 res = INSTALLSTATE_INCOMPLETE;
1714 feature->Installed = res;
1718 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1721 static const WCHAR all[]={'A','L','L',0};
1723 MSIFEATURE *feature;
1725 override = msi_dup_property( package, property );
1729 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1731 if (strcmpiW(override,all)==0)
1732 msi_feature_set_state( feature, state );
1735 LPWSTR ptr = override;
1736 LPWSTR ptr2 = strchrW(override,',');
1740 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1741 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1743 msi_feature_set_state( feature, state );
1749 ptr2 = strchrW(ptr,',');
1761 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1764 static const WCHAR szlevel[] =
1765 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1766 static const WCHAR szAddLocal[] =
1767 {'A','D','D','L','O','C','A','L',0};
1768 static const WCHAR szAddSource[] =
1769 {'A','D','D','S','O','U','R','C','E',0};
1770 static const WCHAR szRemove[] =
1771 {'R','E','M','O','V','E',0};
1772 static const WCHAR szReinstall[] =
1773 {'R','E','I','N','S','T','A','L','L',0};
1774 BOOL override = FALSE;
1775 MSICOMPONENT* component;
1776 MSIFEATURE *feature;
1779 /* I do not know if this is where it should happen.. but */
1781 TRACE("Checking Install Level\n");
1783 install_level = msi_get_property_int( package, szlevel, 1 );
1785 /* ok here is the _real_ rub
1786 * all these activation/deactivation things happen in order and things
1787 * later on the list override things earlier on the list.
1788 * 1) INSTALLLEVEL processing
1798 * 11) FILEADDDEFAULT
1799 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1800 * ignored for all the features. seems strange, especially since it is not
1801 * documented anywhere, but it is how it works.
1803 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1804 * REMOVE are the big ones, since we don't handle administrative installs
1807 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1808 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1809 override |= process_state_property(package,szAddSource,INSTALLSTATE_SOURCE);
1810 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1814 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1816 BOOL feature_state = ((feature->Level > 0) &&
1817 (feature->Level <= install_level));
1819 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1821 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1822 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1823 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1824 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1826 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1830 /* disable child features of unselected parent features */
1831 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1835 if (feature->Level > 0 && feature->Level <= install_level)
1838 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1839 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1844 /* set the Preselected Property */
1845 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1846 static const WCHAR szOne[] = { '1', 0 };
1848 MSI_SetPropertyW(package,szPreselected,szOne);
1852 * now we want to enable or disable components base on feature
1855 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1859 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1860 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1862 /* features with components that have compressed files are made local */
1863 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1865 if (cl->component->Enabled &&
1866 cl->component->ForceLocalState &&
1867 feature->Action == INSTALLSTATE_SOURCE)
1869 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1874 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1876 component = cl->component;
1878 if (!component->Enabled)
1881 switch (feature->Action)
1883 case INSTALLSTATE_ABSENT:
1884 component->anyAbsent = 1;
1886 case INSTALLSTATE_ADVERTISED:
1887 component->hasAdvertiseFeature = 1;
1889 case INSTALLSTATE_SOURCE:
1890 component->hasSourceFeature = 1;
1892 case INSTALLSTATE_LOCAL:
1893 component->hasLocalFeature = 1;
1895 case INSTALLSTATE_DEFAULT:
1896 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1897 component->hasAdvertiseFeature = 1;
1898 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1899 component->hasSourceFeature = 1;
1901 component->hasLocalFeature = 1;
1909 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1911 /* if the component isn't enabled, leave it alone */
1912 if (!component->Enabled)
1915 /* check if it's local or source */
1916 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1917 (component->hasLocalFeature || component->hasSourceFeature))
1919 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1920 !component->ForceLocalState)
1921 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1923 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1927 /* if any feature is local, the component must be local too */
1928 if (component->hasLocalFeature)
1930 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1934 if (component->hasSourceFeature)
1936 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1940 if (component->hasAdvertiseFeature)
1942 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1946 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1947 if (component->anyAbsent)
1948 msi_component_set_state(component, INSTALLSTATE_ABSENT);
1951 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1953 if (component->Action == INSTALLSTATE_DEFAULT)
1955 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1956 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1959 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1960 debugstr_w(component->Component), component->Installed, component->Action);
1964 return ERROR_SUCCESS;
1967 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1969 MSIPACKAGE *package = (MSIPACKAGE*)param;
1974 name = MSI_RecordGetString(row,1);
1976 f = get_loaded_folder(package, name);
1977 if (!f) return ERROR_SUCCESS;
1979 /* reset the ResolvedTarget */
1980 msi_free(f->ResolvedTarget);
1981 f->ResolvedTarget = NULL;
1983 /* This helper function now does ALL the work */
1984 TRACE("Dir %s ...\n",debugstr_w(name));
1985 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1986 TRACE("resolves to %s\n",debugstr_w(path));
1989 return ERROR_SUCCESS;
1992 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1994 MSIPACKAGE *package = (MSIPACKAGE*)param;
1996 MSIFEATURE *feature;
1998 name = MSI_RecordGetString( row, 1 );
2000 feature = get_loaded_feature( package, name );
2002 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2006 Condition = MSI_RecordGetString(row,3);
2008 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2010 int level = MSI_RecordGetInteger(row,2);
2011 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
2012 feature->Level = level;
2015 return ERROR_SUCCESS;
2018 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2020 static const WCHAR name_fmt[] =
2021 {'%','u','.','%','u','.','%','u','.','%','u',0};
2022 static WCHAR name[] = {'\\',0};
2023 VS_FIXEDFILEINFO *lpVer;
2024 WCHAR filever[0x100];
2030 TRACE("%s\n", debugstr_w(filename));
2032 versize = GetFileVersionInfoSizeW( filename, &handle );
2036 version = msi_alloc( versize );
2037 GetFileVersionInfoW( filename, 0, versize, version );
2039 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2041 msi_free( version );
2045 sprintfW( filever, name_fmt,
2046 HIWORD(lpVer->dwFileVersionMS),
2047 LOWORD(lpVer->dwFileVersionMS),
2048 HIWORD(lpVer->dwFileVersionLS),
2049 LOWORD(lpVer->dwFileVersionLS));
2051 msi_free( version );
2053 return strdupW( filever );
2056 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2058 LPWSTR file_version;
2061 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2063 MSICOMPONENT* comp = file->Component;
2069 if (file->IsCompressed)
2070 comp->ForceLocalState = TRUE;
2072 /* calculate target */
2073 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2075 msi_free(file->TargetPath);
2077 TRACE("file %s is named %s\n",
2078 debugstr_w(file->File), debugstr_w(file->FileName));
2080 file->TargetPath = build_directory_name(2, p, file->FileName);
2084 TRACE("file %s resolves to %s\n",
2085 debugstr_w(file->File), debugstr_w(file->TargetPath));
2087 /* don't check files of components that aren't installed */
2088 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2089 comp->Installed == INSTALLSTATE_ABSENT)
2091 file->state = msifs_missing; /* assume files are missing */
2095 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2097 file->state = msifs_missing;
2098 comp->Cost += file->FileSize;
2099 comp->Installed = INSTALLSTATE_INCOMPLETE;
2103 if (file->Version &&
2104 (file_version = msi_get_disk_file_version( file->TargetPath )))
2106 TRACE("new %s old %s\n", debugstr_w(file->Version),
2107 debugstr_w(file_version));
2108 /* FIXME: seems like a bad way to compare version numbers */
2109 if (lstrcmpiW(file_version, file->Version)<0)
2111 file->state = msifs_overwrite;
2112 comp->Cost += file->FileSize;
2113 comp->Installed = INSTALLSTATE_INCOMPLETE;
2116 file->state = msifs_present;
2117 msi_free( file_version );
2120 file->state = msifs_present;
2123 return ERROR_SUCCESS;
2127 * A lot is done in this function aside from just the costing.
2128 * The costing needs to be implemented at some point but for now I am going
2129 * to focus on the directory building
2132 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2134 static const WCHAR ExecSeqQuery[] =
2135 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2136 '`','D','i','r','e','c','t','o','r','y','`',0};
2137 static const WCHAR ConditionQuery[] =
2138 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2139 '`','C','o','n','d','i','t','i','o','n','`',0};
2140 static const WCHAR szCosting[] =
2141 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2142 static const WCHAR szlevel[] =
2143 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2144 static const WCHAR szOne[] = { '1', 0 };
2150 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2151 return ERROR_SUCCESS;
2153 TRACE("Building Directory properties\n");
2155 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2156 if (rc == ERROR_SUCCESS)
2158 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2160 msiobj_release(&view->hdr);
2163 /* read components states from the registry */
2164 ACTION_GetComponentInstallStates(package);
2166 TRACE("File calculations\n");
2167 msi_check_file_install_states( package );
2169 TRACE("Evaluating Condition Table\n");
2171 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2172 if (rc == ERROR_SUCCESS)
2174 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2176 msiobj_release(&view->hdr);
2179 TRACE("Enabling or Disabling Components\n");
2180 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2182 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2184 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2185 comp->Enabled = FALSE;
2189 MSI_SetPropertyW(package,szCosting,szOne);
2190 /* set default run level if not set */
2191 level = msi_dup_property( package, szlevel );
2193 MSI_SetPropertyW(package,szlevel, szOne);
2196 ACTION_UpdateFeatureInstallStates(package);
2198 return MSI_SetFeatureStates(package);
2201 /* OK this value is "interpreted" and then formatted based on the
2202 first few characters */
2203 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2207 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2213 LPWSTR deformated = NULL;
2216 deformat_string(package, &value[2], &deformated);
2218 /* binary value type */
2222 *size = (strlenW(ptr)/2)+1;
2224 *size = strlenW(ptr)/2;
2226 data = msi_alloc(*size);
2232 /* if uneven pad with a zero in front */
2238 data[count] = (BYTE)strtol(byte,NULL,0);
2240 TRACE("Uneven byte count\n");
2248 data[count] = (BYTE)strtol(byte,NULL,0);
2251 msi_free(deformated);
2253 TRACE("Data %i bytes(%i)\n",*size,count);
2260 deformat_string(package, &value[1], &deformated);
2263 *size = sizeof(DWORD);
2264 data = msi_alloc(*size);
2270 if ( (*p < '0') || (*p > '9') )
2276 if (deformated[0] == '-')
2279 TRACE("DWORD %i\n",*(LPDWORD)data);
2281 msi_free(deformated);
2286 static const WCHAR szMulti[] = {'[','~',']',0};
2295 *type=REG_EXPAND_SZ;
2303 if (strstrW(value,szMulti))
2304 *type = REG_MULTI_SZ;
2306 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2311 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2313 MSIPACKAGE *package = (MSIPACKAGE*)param;
2314 static const WCHAR szHCR[] =
2315 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2316 'R','O','O','T','\\',0};
2317 static const WCHAR szHCU[] =
2318 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2319 'U','S','E','R','\\',0};
2320 static const WCHAR szHLM[] =
2321 {'H','K','E','Y','_','L','O','C','A','L','_',
2322 'M','A','C','H','I','N','E','\\',0};
2323 static const WCHAR szHU[] =
2324 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2326 LPSTR value_data = NULL;
2327 HKEY root_key, hkey;
2330 LPCWSTR szRoot, component, name, key, value;
2335 BOOL check_first = FALSE;
2338 ui_progress(package,2,0,0,0);
2345 component = MSI_RecordGetString(row, 6);
2346 comp = get_loaded_component(package,component);
2348 return ERROR_SUCCESS;
2350 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2352 TRACE("Skipping write due to disabled component %s\n",
2353 debugstr_w(component));
2355 comp->Action = comp->Installed;
2357 return ERROR_SUCCESS;
2360 comp->Action = INSTALLSTATE_LOCAL;
2362 name = MSI_RecordGetString(row, 4);
2363 if( MSI_RecordIsNull(row,5) && name )
2365 /* null values can have special meanings */
2366 if (name[0]=='-' && name[1] == 0)
2367 return ERROR_SUCCESS;
2368 else if ((name[0]=='+' && name[1] == 0) ||
2369 (name[0] == '*' && name[1] == 0))
2374 root = MSI_RecordGetInteger(row,2);
2375 key = MSI_RecordGetString(row, 3);
2377 /* get the root key */
2382 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2383 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2384 if (all_users && all_users[0] == '1')
2386 root_key = HKEY_LOCAL_MACHINE;
2391 root_key = HKEY_CURRENT_USER;
2394 msi_free(all_users);
2397 case 0: root_key = HKEY_CLASSES_ROOT;
2400 case 1: root_key = HKEY_CURRENT_USER;
2403 case 2: root_key = HKEY_LOCAL_MACHINE;
2406 case 3: root_key = HKEY_USERS;
2410 ERR("Unknown root %i\n",root);
2416 return ERROR_SUCCESS;
2418 deformat_string(package, key , &deformated);
2419 size = strlenW(deformated) + strlenW(szRoot) + 1;
2420 uikey = msi_alloc(size*sizeof(WCHAR));
2421 strcpyW(uikey,szRoot);
2422 strcatW(uikey,deformated);
2424 if (RegCreateKeyW( root_key, deformated, &hkey))
2426 ERR("Could not create key %s\n",debugstr_w(deformated));
2427 msi_free(deformated);
2429 return ERROR_SUCCESS;
2431 msi_free(deformated);
2433 value = MSI_RecordGetString(row,5);
2435 value_data = parse_value(package, value, &type, &size);
2438 static const WCHAR szEmpty[] = {0};
2439 value_data = (LPSTR)strdupW(szEmpty);
2444 deformat_string(package, name, &deformated);
2446 /* get the double nulls to terminate SZ_MULTI */
2447 if (type == REG_MULTI_SZ)
2448 size +=sizeof(WCHAR);
2452 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2454 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2459 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2460 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2462 TRACE("value %s of %s checked already exists\n",
2463 debugstr_w(deformated), debugstr_w(uikey));
2467 TRACE("Checked and setting value %s of %s\n",
2468 debugstr_w(deformated), debugstr_w(uikey));
2469 if (deformated || size)
2470 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2475 uirow = MSI_CreateRecord(3);
2476 MSI_RecordSetStringW(uirow,2,deformated);
2477 MSI_RecordSetStringW(uirow,1,uikey);
2480 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2482 MSI_RecordSetStringW(uirow,3,value);
2484 ui_actiondata(package,szWriteRegistryValues,uirow);
2485 msiobj_release( &uirow->hdr );
2487 msi_free(value_data);
2488 msi_free(deformated);
2491 return ERROR_SUCCESS;
2494 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2498 static const WCHAR ExecSeqQuery[] =
2499 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2500 '`','R','e','g','i','s','t','r','y','`',0 };
2502 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2503 if (rc != ERROR_SUCCESS)
2504 return ERROR_SUCCESS;
2506 /* increment progress bar each time action data is sent */
2507 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2509 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2511 msiobj_release(&view->hdr);
2515 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2517 package->script->CurrentlyScripting = TRUE;
2519 return ERROR_SUCCESS;
2523 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2528 static const WCHAR q1[]=
2529 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2530 '`','R','e','g','i','s','t','r','y','`',0};
2533 MSIFEATURE *feature;
2536 TRACE("InstallValidate\n");
2538 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2539 if (rc == ERROR_SUCCESS)
2541 MSI_IterateRecords( view, &progress, NULL, package );
2542 msiobj_release( &view->hdr );
2543 total += progress * REG_PROGRESS_VALUE;
2546 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2547 total += COMPONENT_PROGRESS_VALUE;
2549 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2550 total += file->FileSize;
2552 ui_progress(package,0,total,0,0);
2554 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2556 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2557 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2558 feature->ActionRequest);
2561 return ERROR_SUCCESS;
2564 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2566 MSIPACKAGE* package = (MSIPACKAGE*)param;
2567 LPCWSTR cond = NULL;
2568 LPCWSTR message = NULL;
2571 static const WCHAR title[]=
2572 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2574 cond = MSI_RecordGetString(row,1);
2576 r = MSI_EvaluateConditionW(package,cond);
2577 if (r == MSICONDITION_FALSE)
2579 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2582 message = MSI_RecordGetString(row,2);
2583 deformat_string(package,message,&deformated);
2584 MessageBoxW(NULL,deformated,title,MB_OK);
2585 msi_free(deformated);
2588 return ERROR_INSTALL_FAILURE;
2591 return ERROR_SUCCESS;
2594 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2597 MSIQUERY * view = NULL;
2598 static const WCHAR ExecSeqQuery[] =
2599 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2600 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2602 TRACE("Checking launch conditions\n");
2604 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2605 if (rc != ERROR_SUCCESS)
2606 return ERROR_SUCCESS;
2608 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2609 msiobj_release(&view->hdr);
2614 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2618 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2620 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2622 MSIRECORD * row = 0;
2624 LPWSTR deformated,buffer,deformated_name;
2626 static const WCHAR ExecSeqQuery[] =
2627 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2628 '`','R','e','g','i','s','t','r','y','`',' ',
2629 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2630 ' ','=',' ' ,'\'','%','s','\'',0 };
2631 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2632 static const WCHAR fmt2[]=
2633 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2635 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2639 root = MSI_RecordGetInteger(row,2);
2640 key = MSI_RecordGetString(row, 3);
2641 name = MSI_RecordGetString(row, 4);
2642 deformat_string(package, key , &deformated);
2643 deformat_string(package, name, &deformated_name);
2645 len = strlenW(deformated) + 6;
2646 if (deformated_name)
2647 len+=strlenW(deformated_name);
2649 buffer = msi_alloc( len *sizeof(WCHAR));
2651 if (deformated_name)
2652 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2654 sprintfW(buffer,fmt,root,deformated);
2656 msi_free(deformated);
2657 msi_free(deformated_name);
2658 msiobj_release(&row->hdr);
2662 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2664 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2669 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2672 return strdupW( file->TargetPath );
2677 static HKEY openSharedDLLsKey(void)
2680 static const WCHAR path[] =
2681 {'S','o','f','t','w','a','r','e','\\',
2682 'M','i','c','r','o','s','o','f','t','\\',
2683 'W','i','n','d','o','w','s','\\',
2684 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2685 'S','h','a','r','e','d','D','L','L','s',0};
2687 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2691 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2696 DWORD sz = sizeof(count);
2699 hkey = openSharedDLLsKey();
2700 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2701 if (rc != ERROR_SUCCESS)
2707 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2711 hkey = openSharedDLLsKey();
2713 msi_reg_set_val_dword( hkey, path, count );
2715 RegDeleteValueW(hkey,path);
2721 * Return TRUE if the count should be written out and FALSE if not
2723 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2725 MSIFEATURE *feature;
2729 /* only refcount DLLs */
2730 if (comp->KeyPath == NULL ||
2731 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2732 comp->Attributes & msidbComponentAttributesODBCDataSource)
2736 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2737 write = (count > 0);
2739 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2743 /* increment counts */
2744 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2748 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2751 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2753 if ( cl->component == comp )
2758 /* decrement counts */
2759 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2763 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2766 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2768 if ( cl->component == comp )
2773 /* ref count all the files in the component */
2778 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2780 if (file->Component == comp)
2781 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2785 /* add a count for permenent */
2786 if (comp->Attributes & msidbComponentAttributesPermanent)
2789 comp->RefCount = count;
2792 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2796 * Ok further analysis makes me think that this work is
2797 * actually done in the PublishComponents and PublishFeatures
2798 * step, and not here. It appears like the keypath and all that is
2799 * resolved in this step, however actually written in the Publish steps.
2800 * But we will leave it here for now because it is unclear
2802 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2804 WCHAR squished_pc[GUID_SIZE];
2805 WCHAR squished_cc[GUID_SIZE];
2808 HKEY hkey=0,hkey2=0;
2812 /* writes the Component and Features values to the registry */
2814 rc = MSIREG_OpenComponents(&hkey);
2815 if (rc != ERROR_SUCCESS)
2818 squash_guid(package->ProductCode,squished_pc);
2819 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2821 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2825 ui_progress(package,2,0,0,0);
2826 if (!comp->ComponentId)
2829 squash_guid(comp->ComponentId,squished_cc);
2831 msi_free(comp->FullKeypath);
2832 comp->FullKeypath = resolve_keypath( package, comp );
2834 /* do the refcounting */
2835 ACTION_RefCountComponent( package, comp );
2837 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2838 debugstr_w(comp->Component),
2839 debugstr_w(squished_cc),
2840 debugstr_w(comp->FullKeypath),
2843 * Write the keypath out if the component is to be registered
2844 * and delete the key if the component is to be deregistered
2846 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2848 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2849 if (rc != ERROR_SUCCESS)
2852 if (!comp->FullKeypath)
2855 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2857 if (comp->Attributes & msidbComponentAttributesPermanent)
2859 static const WCHAR szPermKey[] =
2860 { '0','0','0','0','0','0','0','0','0','0','0','0',
2861 '0','0','0','0','0','0','0','0','0','0','0','0',
2862 '0','0','0','0','0','0','0','0',0 };
2864 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2869 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey2, TRUE);
2870 if (rc != ERROR_SUCCESS)
2873 msi_reg_set_val_str(hkey2, squished_pc, comp->FullKeypath);
2876 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2880 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2881 if (rc != ERROR_SUCCESS)
2884 RegDeleteValueW(hkey2,squished_pc);
2886 /* if the key is empty delete it */
2887 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2889 if (res == ERROR_NO_MORE_ITEMS)
2890 RegDeleteKeyW(hkey,squished_cc);
2892 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2896 uirow = MSI_CreateRecord(3);
2897 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2898 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2899 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2900 ui_actiondata(package,szProcessComponents,uirow);
2901 msiobj_release( &uirow->hdr );
2915 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2916 LPWSTR lpszName, LONG_PTR lParam)
2919 typelib_struct *tl_struct = (typelib_struct*) lParam;
2920 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2924 if (!IS_INTRESOURCE(lpszName))
2926 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2930 sz = strlenW(tl_struct->source)+4;
2931 sz *= sizeof(WCHAR);
2933 if ((INT_PTR)lpszName == 1)
2934 tl_struct->path = strdupW(tl_struct->source);
2937 tl_struct->path = msi_alloc(sz);
2938 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2941 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2942 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2943 if (!SUCCEEDED(res))
2945 msi_free(tl_struct->path);
2946 tl_struct->path = NULL;
2951 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2952 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2954 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2958 msi_free(tl_struct->path);
2959 tl_struct->path = NULL;
2961 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2962 ITypeLib_Release(tl_struct->ptLib);
2967 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2969 MSIPACKAGE* package = (MSIPACKAGE*)param;
2973 typelib_struct tl_struct;
2975 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2977 component = MSI_RecordGetString(row,3);
2978 comp = get_loaded_component(package,component);
2980 return ERROR_SUCCESS;
2982 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2984 TRACE("Skipping typelib reg due to disabled component\n");
2986 comp->Action = comp->Installed;
2988 return ERROR_SUCCESS;
2991 comp->Action = INSTALLSTATE_LOCAL;
2993 file = get_loaded_file( package, comp->KeyPath );
2995 return ERROR_SUCCESS;
2997 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3001 guid = MSI_RecordGetString(row,1);
3002 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3003 tl_struct.source = strdupW( file->TargetPath );
3004 tl_struct.path = NULL;
3006 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3007 (LONG_PTR)&tl_struct);
3015 helpid = MSI_RecordGetString(row,6);
3018 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3019 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3022 if (!SUCCEEDED(res))
3023 ERR("Failed to register type library %s\n",
3024 debugstr_w(tl_struct.path));
3027 ui_actiondata(package,szRegisterTypeLibraries,row);
3029 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3032 ITypeLib_Release(tl_struct.ptLib);
3033 msi_free(tl_struct.path);
3036 ERR("Failed to load type library %s\n",
3037 debugstr_w(tl_struct.source));
3039 FreeLibrary(module);
3040 msi_free(tl_struct.source);
3043 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3045 return ERROR_SUCCESS;
3048 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3051 * OK this is a bit confusing.. I am given a _Component key and I believe
3052 * that the file that is being registered as a type library is the "key file
3053 * of that component" which I interpret to mean "The file in the KeyPath of
3058 static const WCHAR Query[] =
3059 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3060 '`','T','y','p','e','L','i','b','`',0};
3062 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3063 if (rc != ERROR_SUCCESS)
3064 return ERROR_SUCCESS;
3066 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3067 msiobj_release(&view->hdr);
3071 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3073 MSIPACKAGE *package = (MSIPACKAGE*)param;
3074 LPWSTR target_file, target_folder, filename;
3075 LPCWSTR buffer, extension;
3077 static const WCHAR szlnk[]={'.','l','n','k',0};
3078 IShellLinkW *sl = NULL;
3079 IPersistFile *pf = NULL;
3082 buffer = MSI_RecordGetString(row,4);
3083 comp = get_loaded_component(package,buffer);
3085 return ERROR_SUCCESS;
3087 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3089 TRACE("Skipping shortcut creation due to disabled component\n");
3091 comp->Action = comp->Installed;
3093 return ERROR_SUCCESS;
3096 comp->Action = INSTALLSTATE_LOCAL;
3098 ui_actiondata(package,szCreateShortcuts,row);
3100 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3101 &IID_IShellLinkW, (LPVOID *) &sl );
3105 ERR("CLSID_ShellLink not available\n");
3109 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3112 ERR("QueryInterface(IID_IPersistFile) failed\n");
3116 buffer = MSI_RecordGetString(row,2);
3117 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3119 /* may be needed because of a bug somehwere else */
3120 create_full_pathW(target_folder);
3122 filename = msi_dup_record_field( row, 3 );
3123 reduce_to_longfilename(filename);
3125 extension = strchrW(filename,'.');
3126 if (!extension || strcmpiW(extension,szlnk))
3128 int len = strlenW(filename);
3129 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3130 memcpy(filename + len, szlnk, sizeof(szlnk));
3132 target_file = build_directory_name(2, target_folder, filename);
3133 msi_free(target_folder);
3136 buffer = MSI_RecordGetString(row,5);
3137 if (strchrW(buffer,'['))
3140 deformat_string(package,buffer,&deformated);
3141 IShellLinkW_SetPath(sl,deformated);
3142 msi_free(deformated);
3146 FIXME("poorly handled shortcut format, advertised shortcut\n");
3147 IShellLinkW_SetPath(sl,comp->FullKeypath);
3150 if (!MSI_RecordIsNull(row,6))
3153 buffer = MSI_RecordGetString(row,6);
3154 deformat_string(package,buffer,&deformated);
3155 IShellLinkW_SetArguments(sl,deformated);
3156 msi_free(deformated);
3159 if (!MSI_RecordIsNull(row,7))
3161 buffer = MSI_RecordGetString(row,7);
3162 IShellLinkW_SetDescription(sl,buffer);
3165 if (!MSI_RecordIsNull(row,8))
3166 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3168 if (!MSI_RecordIsNull(row,9))
3173 buffer = MSI_RecordGetString(row,9);
3175 Path = build_icon_path(package,buffer);
3176 index = MSI_RecordGetInteger(row,10);
3178 /* no value means 0 */
3179 if (index == MSI_NULL_INTEGER)
3182 IShellLinkW_SetIconLocation(sl,Path,index);
3186 if (!MSI_RecordIsNull(row,11))
3187 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3189 if (!MSI_RecordIsNull(row,12))
3192 buffer = MSI_RecordGetString(row,12);
3193 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3195 IShellLinkW_SetWorkingDirectory(sl,Path);
3199 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3200 IPersistFile_Save(pf,target_file,FALSE);
3202 msi_free(target_file);
3206 IPersistFile_Release( pf );
3208 IShellLinkW_Release( sl );
3210 return ERROR_SUCCESS;
3213 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3218 static const WCHAR Query[] =
3219 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3220 '`','S','h','o','r','t','c','u','t','`',0};
3222 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3223 if (rc != ERROR_SUCCESS)
3224 return ERROR_SUCCESS;
3226 res = CoInitialize( NULL );
3229 ERR("CoInitialize failed\n");
3230 return ERROR_FUNCTION_FAILED;
3233 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3234 msiobj_release(&view->hdr);
3241 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3243 MSIPACKAGE* package = (MSIPACKAGE*)param;
3252 FileName = MSI_RecordGetString(row,1);
3255 ERR("Unable to get FileName\n");
3256 return ERROR_SUCCESS;
3259 FilePath = build_icon_path(package,FileName);
3261 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3263 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3264 FILE_ATTRIBUTE_NORMAL, NULL);
3266 if (the_file == INVALID_HANDLE_VALUE)
3268 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3270 return ERROR_SUCCESS;
3277 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3278 if (rc != ERROR_SUCCESS)
3280 ERR("Failed to get stream\n");
3281 CloseHandle(the_file);
3282 DeleteFileW(FilePath);
3285 WriteFile(the_file,buffer,sz,&write,NULL);
3286 } while (sz == 1024);
3290 CloseHandle(the_file);
3292 uirow = MSI_CreateRecord(1);
3293 MSI_RecordSetStringW(uirow,1,FileName);
3294 ui_actiondata(package,szPublishProduct,uirow);
3295 msiobj_release( &uirow->hdr );
3297 return ERROR_SUCCESS;
3300 static BOOL msi_check_publish(MSIPACKAGE *package)
3302 MSIFEATURE *feature;
3304 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3306 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3314 * 99% of the work done here is only done for
3315 * advertised installs. However this is where the
3316 * Icon table is processed and written out
3317 * so that is what I am going to do here.
3319 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3323 MSISOURCELISTINFO *info;
3325 static const WCHAR Query[]=
3326 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3327 '`','I','c','o','n','`',0};
3328 /* for registry stuff */
3331 HKEY hudkey=0, props=0;
3332 static const WCHAR szProductLanguage[] =
3333 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3334 static const WCHAR szARPProductIcon[] =
3335 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3336 static const WCHAR szProductVersion[] =
3337 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3341 MSIHANDLE hDb, hSumInfo;
3343 /* FIXME: also need to publish if the product is in advertise mode */
3344 if (!msi_check_publish(package))
3345 return ERROR_SUCCESS;
3347 /* write out icon files */
3349 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3350 if (rc == ERROR_SUCCESS)
3352 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3353 msiobj_release(&view->hdr);
3356 /* ok there is a lot more done here but i need to figure out what */
3358 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3359 if (rc != ERROR_SUCCESS)
3362 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3363 if (rc != ERROR_SUCCESS)
3366 rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3367 if (rc != ERROR_SUCCESS)
3370 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode,&props,TRUE);
3371 if (rc != ERROR_SUCCESS)
3374 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3375 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3378 langid = msi_get_property_int( package, szProductLanguage, 0 );
3379 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3381 buffer = msi_dup_property( package, szARPProductIcon );
3384 LPWSTR path = build_icon_path(package,buffer);
3385 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3390 buffer = msi_dup_property( package, szProductVersion );
3393 DWORD verdword = msi_version_str_to_dword(buffer);
3394 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3398 /* FIXME: Need to write more keys to the user registry */
3400 hDb= alloc_msihandle( &package->db->hdr );
3402 rc = ERROR_NOT_ENOUGH_MEMORY;
3405 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3406 MsiCloseHandle(hDb);
3407 if (rc == ERROR_SUCCESS)
3409 WCHAR guidbuffer[0x200];
3411 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3413 if (rc == ERROR_SUCCESS)
3415 WCHAR squashed[GUID_SIZE];
3416 /* for now we only care about the first guid */
3417 LPWSTR ptr = strchrW(guidbuffer,';');
3419 squash_guid(guidbuffer,squashed);
3420 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3424 ERR("Unable to query Revision_Number...\n");
3427 MsiCloseHandle(hSumInfo);
3431 ERR("Unable to open Summary Information\n");
3435 /* publish the SourceList info */
3436 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3438 MsiSourceListSetInfoW(package->ProductCode, NULL,
3439 info->context, info->options,
3440 info->property, info->value);
3443 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3445 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3446 disk->context, disk->options,
3447 disk->disk_id, disk->volume_label, disk->disk_prompt);
3453 RegCloseKey(hudkey);
3459 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3461 MSIPACKAGE *package = (MSIPACKAGE*)param;
3462 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3463 LPWSTR deformated_section, deformated_key, deformated_value;
3464 LPWSTR folder, fullname = NULL;
3468 static const WCHAR szWindowsFolder[] =
3469 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3471 component = MSI_RecordGetString(row, 8);
3472 comp = get_loaded_component(package,component);
3474 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3476 TRACE("Skipping ini file due to disabled component %s\n",
3477 debugstr_w(component));
3479 comp->Action = comp->Installed;
3481 return ERROR_SUCCESS;
3484 comp->Action = INSTALLSTATE_LOCAL;
3486 identifier = MSI_RecordGetString(row,1);
3487 filename = MSI_RecordGetString(row,2);
3488 dirproperty = MSI_RecordGetString(row,3);
3489 section = MSI_RecordGetString(row,4);
3490 key = MSI_RecordGetString(row,5);
3491 value = MSI_RecordGetString(row,6);
3492 action = MSI_RecordGetInteger(row,7);
3494 deformat_string(package,section,&deformated_section);
3495 deformat_string(package,key,&deformated_key);
3496 deformat_string(package,value,&deformated_value);
3500 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3502 folder = msi_dup_property( package, dirproperty );
3505 folder = msi_dup_property( package, szWindowsFolder );
3509 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3513 fullname = build_directory_name(2, folder, filename);
3517 TRACE("Adding value %s to section %s in %s\n",
3518 debugstr_w(deformated_key), debugstr_w(deformated_section),
3519 debugstr_w(fullname));
3520 WritePrivateProfileStringW(deformated_section, deformated_key,
3521 deformated_value, fullname);
3523 else if (action == 1)
3526 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3527 returned, 10, fullname);
3528 if (returned[0] == 0)
3530 TRACE("Adding value %s to section %s in %s\n",
3531 debugstr_w(deformated_key), debugstr_w(deformated_section),
3532 debugstr_w(fullname));
3534 WritePrivateProfileStringW(deformated_section, deformated_key,
3535 deformated_value, fullname);
3538 else if (action == 3)
3539 FIXME("Append to existing section not yet implemented\n");
3541 uirow = MSI_CreateRecord(4);
3542 MSI_RecordSetStringW(uirow,1,identifier);
3543 MSI_RecordSetStringW(uirow,2,deformated_section);
3544 MSI_RecordSetStringW(uirow,3,deformated_key);
3545 MSI_RecordSetStringW(uirow,4,deformated_value);
3546 ui_actiondata(package,szWriteIniValues,uirow);
3547 msiobj_release( &uirow->hdr );
3551 msi_free(deformated_key);
3552 msi_free(deformated_value);
3553 msi_free(deformated_section);
3554 return ERROR_SUCCESS;
3557 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3561 static const WCHAR ExecSeqQuery[] =
3562 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3563 '`','I','n','i','F','i','l','e','`',0};
3565 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3566 if (rc != ERROR_SUCCESS)
3568 TRACE("no IniFile table\n");
3569 return ERROR_SUCCESS;
3572 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3573 msiobj_release(&view->hdr);
3577 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3579 MSIPACKAGE *package = (MSIPACKAGE*)param;
3584 static const WCHAR ExeStr[] =
3585 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3586 static const WCHAR close[] = {'\"',0};
3588 PROCESS_INFORMATION info;
3593 memset(&si,0,sizeof(STARTUPINFOW));
3595 filename = MSI_RecordGetString(row,1);
3596 file = get_loaded_file( package, filename );
3600 ERR("Unable to find file id %s\n",debugstr_w(filename));
3601 return ERROR_SUCCESS;
3604 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3606 FullName = msi_alloc(len*sizeof(WCHAR));
3607 strcpyW(FullName,ExeStr);
3608 strcatW( FullName, file->TargetPath );
3609 strcatW(FullName,close);
3611 TRACE("Registering %s\n",debugstr_w(FullName));
3612 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3616 msi_dialog_check_messages(info.hProcess);
3621 uirow = MSI_CreateRecord( 2 );
3622 uipath = strdupW( file->TargetPath );
3623 p = strrchrW(uipath,'\\');
3626 MSI_RecordSetStringW( uirow, 1, &p[1] );
3627 MSI_RecordSetStringW( uirow, 2, uipath);
3628 ui_actiondata( package, szSelfRegModules, uirow);
3629 msiobj_release( &uirow->hdr );
3631 /* FIXME: call ui_progress? */
3633 return ERROR_SUCCESS;
3636 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3640 static const WCHAR ExecSeqQuery[] =
3641 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3642 '`','S','e','l','f','R','e','g','`',0};
3644 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3645 if (rc != ERROR_SUCCESS)
3647 TRACE("no SelfReg table\n");
3648 return ERROR_SUCCESS;
3651 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3652 msiobj_release(&view->hdr);
3654 return ERROR_SUCCESS;
3657 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3659 MSIFEATURE *feature;
3665 if (!msi_check_publish(package))
3666 return ERROR_SUCCESS;
3668 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3669 if (rc != ERROR_SUCCESS)
3672 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3673 if (rc != ERROR_SUCCESS)
3676 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &userdata, TRUE);
3677 if (rc != ERROR_SUCCESS)
3680 /* here the guids are base 85 encoded */
3681 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3687 BOOL absent = FALSE;
3690 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3691 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3692 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3696 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3700 if (feature->Feature_Parent)
3701 size += strlenW( feature->Feature_Parent )+2;
3703 data = msi_alloc(size * sizeof(WCHAR));
3706 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3708 MSICOMPONENT* component = cl->component;
3712 if (component->ComponentId)
3714 TRACE("From %s\n",debugstr_w(component->ComponentId));
3715 CLSIDFromString(component->ComponentId, &clsid);
3716 encode_base85_guid(&clsid,buf);
3717 TRACE("to %s\n",debugstr_w(buf));
3722 if (feature->Feature_Parent)
3724 static const WCHAR sep[] = {'\2',0};
3726 strcatW(data,feature->Feature_Parent);
3729 msi_reg_set_val_str( hkey, feature->Feature, data );
3730 msi_reg_set_val_str( userdata, feature->Feature, data );
3734 if (feature->Feature_Parent)
3735 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3738 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3739 (LPBYTE)feature->Feature_Parent,size);
3743 size += 2*sizeof(WCHAR);
3744 data = msi_alloc(size);
3747 if (feature->Feature_Parent)
3748 strcpyW( &data[1], feature->Feature_Parent );
3749 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3755 uirow = MSI_CreateRecord( 1 );
3756 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3757 ui_actiondata( package, szPublishFeatures, uirow);
3758 msiobj_release( &uirow->hdr );
3759 /* FIXME: call ui_progress? */
3768 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3773 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3775 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3776 if (r == ERROR_SUCCESS)
3778 RegDeleteValueW(hkey, feature->Feature);
3782 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3783 if (r == ERROR_SUCCESS)
3785 RegDeleteValueW(hkey, feature->Feature);
3789 return ERROR_SUCCESS;
3792 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3794 MSIFEATURE *feature;
3796 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3798 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3805 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3807 MSIFEATURE *feature;
3809 if (!msi_check_unpublish(package))
3810 return ERROR_SUCCESS;
3812 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3814 msi_unpublish_feature(package, feature);
3817 return ERROR_SUCCESS;
3820 static UINT msi_get_local_package_name( LPWSTR path )
3822 static const WCHAR szInstaller[] = {
3823 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3824 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3828 time = GetTickCount();
3829 GetWindowsDirectoryW( path, MAX_PATH );
3830 lstrcatW( path, szInstaller );
3831 CreateDirectoryW( path, NULL );
3833 len = lstrlenW(path);
3834 for (i=0; i<0x10000; i++)
3836 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3837 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3838 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3839 if (handle != INVALID_HANDLE_VALUE)
3841 CloseHandle(handle);
3844 if (GetLastError() != ERROR_FILE_EXISTS &&
3845 GetLastError() != ERROR_SHARING_VIOLATION)
3846 return ERROR_FUNCTION_FAILED;
3849 return ERROR_SUCCESS;
3852 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3854 static const WCHAR szOriginalDatabase[] =
3855 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3856 WCHAR packagefile[MAX_PATH];
3861 r = msi_get_local_package_name( packagefile );
3862 if (r != ERROR_SUCCESS)
3865 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3867 msiFilePath = msi_dup_property( package, szOriginalDatabase );
3868 r = CopyFileW( msiFilePath, packagefile, FALSE);
3872 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3873 debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3874 msi_free( msiFilePath );
3875 return ERROR_FUNCTION_FAILED;
3877 msi_free( msiFilePath );
3879 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3881 r = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3882 if (r != ERROR_SUCCESS)
3885 msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3887 return ERROR_SUCCESS;
3890 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3892 LPWSTR prop, val, key;
3893 static const LPCSTR propval[] = {
3894 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3895 "ARPCONTACT", "Contact",
3896 "ARPCOMMENTS", "Comments",
3897 "ProductName", "DisplayName",
3898 "ProductVersion", "DisplayVersion",
3899 "ARPHELPLINK", "HelpLink",
3900 "ARPHELPTELEPHONE", "HelpTelephone",
3901 "ARPINSTALLLOCATION", "InstallLocation",
3902 "SourceDir", "InstallSource",
3903 "Manufacturer", "Publisher",
3904 "ARPREADME", "Readme",
3906 "ARPURLINFOABOUT", "URLInfoAbout",
3907 "ARPURLUPDATEINFO", "URLUpdateInfo",
3910 const LPCSTR *p = propval;
3914 prop = strdupAtoW( *p++ );
3915 key = strdupAtoW( *p++ );
3916 val = msi_dup_property( package, prop );
3917 msi_reg_set_val_str( hkey, key, val );
3922 return ERROR_SUCCESS;
3925 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3928 HKEY hudkey=0, props=0;
3929 LPWSTR buffer = NULL;
3932 static const WCHAR szWindowsInstaller[] =
3933 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3934 static const WCHAR szUpgradeCode[] =
3935 {'U','p','g','r','a','d','e','C','o','d','e',0};
3936 static const WCHAR modpath_fmt[] =
3937 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3938 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3939 static const WCHAR szModifyPath[] =
3940 {'M','o','d','i','f','y','P','a','t','h',0};
3941 static const WCHAR szUninstallString[] =
3942 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3943 static const WCHAR szEstimatedSize[] =
3944 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3945 static const WCHAR szProductLanguage[] =
3946 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3947 static const WCHAR szProductVersion[] =
3948 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3951 static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3952 LPWSTR upgrade_code;
3955 /* FIXME: also need to publish if the product is in advertise mode */
3956 if (!msi_check_publish(package))
3957 return ERROR_SUCCESS;
3959 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3960 if (rc != ERROR_SUCCESS)
3963 /* dump all the info i can grab */
3964 /* FIXME: Flesh out more information */
3966 msi_write_uninstall_property_vals( package, hkey );
3968 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3970 msi_make_package_local( package, hkey );
3972 /* do ModifyPath and UninstallString */
3973 size = deformat_string(package,modpath_fmt,&buffer);
3974 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3975 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3978 /* FIXME: Write real Estimated Size when we have it */
3979 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3981 GetLocalTime(&systime);
3982 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3983 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3985 langid = msi_get_property_int( package, szProductLanguage, 0 );
3986 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3988 buffer = msi_dup_property( package, szProductVersion );
3991 DWORD verdword = msi_version_str_to_dword(buffer);
3993 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3994 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3995 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3999 /* Handle Upgrade Codes */
4000 upgrade_code = msi_dup_property( package, szUpgradeCode );
4005 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4006 squash_guid(package->ProductCode,squashed);
4007 msi_reg_set_val_str( hkey2, squashed, NULL );
4009 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4010 squash_guid(package->ProductCode,squashed);
4011 msi_reg_set_val_str( hkey2, squashed, NULL );
4014 msi_free(upgrade_code);
4019 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
4020 if (rc != ERROR_SUCCESS)
4023 RegCloseKey(hudkey);
4025 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
4026 if (rc != ERROR_SUCCESS)
4029 msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
4032 return ERROR_SUCCESS;
4035 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4037 return execute_script(package,INSTALL_SCRIPT);
4040 static UINT msi_unpublish_product(MSIPACKAGE *package)
4042 LPWSTR remove = NULL;
4043 LPWSTR *features = NULL;
4044 BOOL full_uninstall = TRUE;
4045 MSIFEATURE *feature;
4047 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4048 static const WCHAR szAll[] = {'A','L','L',0};
4050 remove = msi_dup_property(package, szRemove);
4052 return ERROR_SUCCESS;
4054 features = msi_split_string(remove, ',');
4058 ERR("REMOVE feature list is empty!\n");
4059 return ERROR_FUNCTION_FAILED;
4062 if (!lstrcmpW(features[0], szAll))
4063 full_uninstall = TRUE;
4066 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4068 if (feature->Action != INSTALLSTATE_ABSENT)
4069 full_uninstall = FALSE;
4073 if (!full_uninstall)
4076 MSIREG_DeleteProductKey(package->ProductCode);
4077 MSIREG_DeleteUserProductKey(package->ProductCode);
4078 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4079 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4080 MSIREG_DeleteUninstallKey(package->ProductCode);
4085 return ERROR_SUCCESS;
4088 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4092 rc = msi_unpublish_product(package);
4093 if (rc != ERROR_SUCCESS)
4096 /* turn off scheduling */
4097 package->script->CurrentlyScripting= FALSE;
4099 /* first do the same as an InstallExecute */
4100 rc = ACTION_InstallExecute(package);
4101 if (rc != ERROR_SUCCESS)
4104 /* then handle Commit Actions */
4105 rc = execute_script(package,COMMIT_SCRIPT);
4110 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4112 static const WCHAR RunOnce[] = {
4113 'S','o','f','t','w','a','r','e','\\',
4114 'M','i','c','r','o','s','o','f','t','\\',
4115 'W','i','n','d','o','w','s','\\',
4116 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4117 'R','u','n','O','n','c','e',0};
4118 static const WCHAR InstallRunOnce[] = {
4119 'S','o','f','t','w','a','r','e','\\',
4120 'M','i','c','r','o','s','o','f','t','\\',
4121 'W','i','n','d','o','w','s','\\',
4122 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4123 'I','n','s','t','a','l','l','e','r','\\',
4124 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4126 static const WCHAR msiexec_fmt[] = {
4128 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4129 '\"','%','s','\"',0};
4130 static const WCHAR install_fmt[] = {
4131 '/','I',' ','\"','%','s','\"',' ',
4132 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4133 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4134 WCHAR buffer[256], sysdir[MAX_PATH];
4136 WCHAR squished_pc[100];
4138 squash_guid(package->ProductCode,squished_pc);
4140 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4141 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4142 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4145 msi_reg_set_val_str( hkey, squished_pc, buffer );
4148 TRACE("Reboot command %s\n",debugstr_w(buffer));
4150 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4151 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4153 msi_reg_set_val_str( hkey, squished_pc, buffer );
4156 return ERROR_INSTALL_SUSPEND;
4159 static UINT msi_set_sourcedir_props(MSIPACKAGE *package)
4164 p = strrchrW( package->PackagePath, '\\' );
4166 return ERROR_SUCCESS;
4168 len = p - package->PackagePath + 2;
4169 source = msi_alloc( len * sizeof(WCHAR) );
4170 lstrcpynW( source, package->PackagePath, len );
4172 MSI_SetPropertyW( package, cszSourceDir, source );
4173 MSI_SetPropertyW( package, cszSOURCEDIR, source );
4177 return ERROR_SUCCESS;
4180 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4186 * We are currently doing what should be done here in the top level Install
4187 * however for Administrative and uninstalls this step will be needed
4189 if (!package->PackagePath)
4190 return ERROR_SUCCESS;
4192 msi_set_sourcedir_props(package);
4194 attrib = GetFileAttributesW(package->PackagePath);
4195 if (attrib == INVALID_FILE_ATTRIBUTES)
4201 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4202 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4203 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4204 if (rc == ERROR_MORE_DATA)
4206 prompt = msi_alloc(size * sizeof(WCHAR));
4207 MsiSourceListGetInfoW(package->ProductCode, NULL,
4208 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4209 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4212 prompt = strdupW(package->PackagePath);
4214 msg = generate_error_string(package,1302,1,prompt);
4215 while(attrib == INVALID_FILE_ATTRIBUTES)
4217 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4220 rc = ERROR_INSTALL_USEREXIT;
4223 attrib = GetFileAttributesW(package->PackagePath);
4229 return ERROR_SUCCESS;
4234 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4241 static const WCHAR szPropKeys[][80] =
4243 {'P','r','o','d','u','c','t','I','D',0},
4244 {'U','S','E','R','N','A','M','E',0},
4245 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4249 static const WCHAR szRegKeys[][80] =
4251 {'P','r','o','d','u','c','t','I','D',0},
4252 {'R','e','g','O','w','n','e','r',0},
4253 {'R','e','g','C','o','m','p','a','n','y',0},
4257 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4259 return ERROR_SUCCESS;
4261 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4262 if (rc != ERROR_SUCCESS)
4265 for( i = 0; szPropKeys[i][0]; i++ )
4267 buffer = msi_dup_property( package, szPropKeys[i] );
4268 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4273 msi_free(productid);
4276 /* FIXME: call ui_actiondata */
4278 return ERROR_SUCCESS;
4282 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4286 package->script->InWhatSequence |= SEQUENCE_EXEC;
4287 rc = ACTION_ProcessExecSequence(package,FALSE);
4292 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4294 MSIPACKAGE *package = (MSIPACKAGE*)param;
4295 LPCWSTR compgroupid=NULL;
4296 LPCWSTR feature=NULL;
4297 LPCWSTR text = NULL;
4298 LPCWSTR qualifier = NULL;
4299 LPCWSTR component = NULL;
4300 LPWSTR advertise = NULL;
4301 LPWSTR output = NULL;
4303 UINT rc = ERROR_SUCCESS;
4308 component = MSI_RecordGetString(rec,3);
4309 comp = get_loaded_component(package,component);
4311 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4312 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4313 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4315 TRACE("Skipping: Component %s not scheduled for install\n",
4316 debugstr_w(component));
4318 return ERROR_SUCCESS;
4321 compgroupid = MSI_RecordGetString(rec,1);
4322 qualifier = MSI_RecordGetString(rec,2);
4324 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4325 if (rc != ERROR_SUCCESS)
4328 text = MSI_RecordGetString(rec,4);
4329 feature = MSI_RecordGetString(rec,5);
4331 advertise = create_component_advertise_string(package, comp, feature);
4333 sz = strlenW(advertise);
4336 sz += lstrlenW(text);
4339 sz *= sizeof(WCHAR);
4341 output = msi_alloc_zero(sz);
4342 strcpyW(output,advertise);
4343 msi_free(advertise);
4346 strcatW(output,text);
4348 msi_reg_set_val_multi_str( hkey, qualifier, output );
4355 uirow = MSI_CreateRecord( 2 );
4356 MSI_RecordSetStringW( uirow, 1, compgroupid );
4357 MSI_RecordSetStringW( uirow, 2, qualifier);
4358 ui_actiondata( package, szPublishComponents, uirow);
4359 msiobj_release( &uirow->hdr );
4360 /* FIXME: call ui_progress? */
4366 * At present I am ignorning the advertised components part of this and only
4367 * focusing on the qualified component sets
4369 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4373 static const WCHAR ExecSeqQuery[] =
4374 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4375 '`','P','u','b','l','i','s','h',
4376 'C','o','m','p','o','n','e','n','t','`',0};
4378 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4379 if (rc != ERROR_SUCCESS)
4380 return ERROR_SUCCESS;
4382 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4383 msiobj_release(&view->hdr);
4388 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4390 MSIPACKAGE *package = (MSIPACKAGE*)param;
4393 SC_HANDLE hscm, service = NULL;
4394 LPCWSTR name, disp, comp, depends, pass;
4395 LPCWSTR load_order, serv_name, key;
4396 DWORD serv_type, start_type;
4399 static const WCHAR query[] =
4400 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4401 '`','C','o','m','p','o','n','e','n','t','`',' ',
4402 'W','H','E','R','E',' ',
4403 '`','C','o','m','p','o','n','e','n','t','`',' ',
4404 '=','\'','%','s','\'',0};
4406 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4409 ERR("Failed to open the SC Manager!\n");
4413 start_type = MSI_RecordGetInteger(rec, 5);
4414 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4417 depends = MSI_RecordGetString(rec, 8);
4418 if (depends && *depends)
4419 FIXME("Dependency list unhandled!\n");
4421 name = MSI_RecordGetString(rec, 2);
4422 disp = MSI_RecordGetString(rec, 3);
4423 serv_type = MSI_RecordGetInteger(rec, 4);
4424 err_control = MSI_RecordGetInteger(rec, 6);
4425 load_order = MSI_RecordGetString(rec, 7);
4426 serv_name = MSI_RecordGetString(rec, 9);
4427 pass = MSI_RecordGetString(rec, 10);
4428 comp = MSI_RecordGetString(rec, 12);
4430 /* fetch the service path */
4431 row = MSI_QueryGetRecord(package->db, query, comp);
4434 ERR("Control query failed!\n");
4438 key = MSI_RecordGetString(row, 6);
4440 file = get_loaded_file(package, key);
4441 msiobj_release(&row->hdr);
4444 ERR("Failed to load the service file\n");
4448 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4449 start_type, err_control, file->TargetPath,
4450 load_order, NULL, NULL, serv_name, pass);
4453 if (GetLastError() != ERROR_SERVICE_EXISTS)
4454 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4458 CloseServiceHandle(service);
4459 CloseServiceHandle(hscm);
4461 return ERROR_SUCCESS;
4464 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4468 static const WCHAR ExecSeqQuery[] =
4469 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4470 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4472 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4473 if (rc != ERROR_SUCCESS)
4474 return ERROR_SUCCESS;
4476 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4477 msiobj_release(&view->hdr);
4482 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4483 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4489 static const WCHAR separator[] = {'[','~',']',0};
4492 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4497 vector = msi_alloc(sizeof(LPWSTR));
4505 vector[*numargs - 1] = p;
4507 if ((q = strstrW(p, separator)))
4511 vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4522 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4524 MSIPACKAGE *package = (MSIPACKAGE *)param;
4526 SC_HANDLE scm, service = NULL;
4527 LPCWSTR name, *vector = NULL;
4529 DWORD event, numargs;
4530 UINT r = ERROR_FUNCTION_FAILED;
4532 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4533 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4534 return ERROR_SUCCESS;
4536 name = MSI_RecordGetString(rec, 2);
4537 event = MSI_RecordGetInteger(rec, 3);
4538 args = strdupW(MSI_RecordGetString(rec, 4));
4540 if (!(event & msidbServiceControlEventStart))
4541 return ERROR_SUCCESS;
4543 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4546 ERR("Failed to open the service control manager\n");
4550 service = OpenServiceW(scm, name, SERVICE_START);
4553 ERR("Failed to open service %s\n", debugstr_w(name));
4557 vector = msi_service_args_to_vector(args, &numargs);
4559 if (!StartServiceW(service, numargs, vector))
4561 ERR("Failed to start service %s\n", debugstr_w(name));
4568 CloseServiceHandle(service);
4569 CloseServiceHandle(scm);
4576 static UINT ACTION_StartServices( MSIPACKAGE *package )
4581 static const WCHAR query[] = {
4582 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4583 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4585 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4586 if (rc != ERROR_SUCCESS)
4587 return ERROR_SUCCESS;
4589 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4590 msiobj_release(&view->hdr);
4595 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4599 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4601 if (!lstrcmpW(file->File, filename))
4608 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4610 MSIPACKAGE *package = (MSIPACKAGE*)param;
4611 LPWSTR driver, driver_path, ptr;
4612 WCHAR outpath[MAX_PATH];
4613 MSIFILE *driver_file, *setup_file;
4616 UINT r = ERROR_SUCCESS;
4618 static const WCHAR driver_fmt[] = {
4619 'D','r','i','v','e','r','=','%','s',0};
4620 static const WCHAR setup_fmt[] = {
4621 'S','e','t','u','p','=','%','s',0};
4622 static const WCHAR usage_fmt[] = {
4623 'F','i','l','e','U','s','a','g','e','=','1',0};
4625 desc = MSI_RecordGetString(rec, 3);
4627 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4628 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4630 if (!driver_file || !setup_file)
4632 ERR("ODBC Driver entry not found!\n");
4633 return ERROR_FUNCTION_FAILED;
4636 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4637 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4638 lstrlenW(usage_fmt) + 1;
4639 driver = msi_alloc(len * sizeof(WCHAR));
4641 return ERROR_OUTOFMEMORY;
4644 lstrcpyW(ptr, desc);
4645 ptr += lstrlenW(ptr) + 1;
4647 sprintfW(ptr, driver_fmt, driver_file->FileName);
4648 ptr += lstrlenW(ptr) + 1;
4650 sprintfW(ptr, setup_fmt, setup_file->FileName);
4651 ptr += lstrlenW(ptr) + 1;
4653 lstrcpyW(ptr, usage_fmt);
4654 ptr += lstrlenW(ptr) + 1;
4657 driver_path = strdupW(driver_file->TargetPath);
4658 ptr = strrchrW(driver_path, '\\');
4659 if (ptr) *ptr = '\0';
4661 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4662 NULL, ODBC_INSTALL_COMPLETE, &usage))
4664 ERR("Failed to install SQL driver!\n");
4665 r = ERROR_FUNCTION_FAILED;
4669 msi_free(driver_path);
4674 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4676 MSIPACKAGE *package = (MSIPACKAGE*)param;
4677 LPWSTR translator, translator_path, ptr;
4678 WCHAR outpath[MAX_PATH];
4679 MSIFILE *translator_file, *setup_file;
4682 UINT r = ERROR_SUCCESS;
4684 static const WCHAR translator_fmt[] = {
4685 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4686 static const WCHAR setup_fmt[] = {
4687 'S','e','t','u','p','=','%','s',0};
4689 desc = MSI_RecordGetString(rec, 3);
4691 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4692 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4694 if (!translator_file || !setup_file)
4696 ERR("ODBC Translator entry not found!\n");
4697 return ERROR_FUNCTION_FAILED;
4700 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4701 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4702 translator = msi_alloc(len * sizeof(WCHAR));
4704 return ERROR_OUTOFMEMORY;
4707 lstrcpyW(ptr, desc);
4708 ptr += lstrlenW(ptr) + 1;
4710 sprintfW(ptr, translator_fmt, translator_file->FileName);
4711 ptr += lstrlenW(ptr) + 1;
4713 sprintfW(ptr, setup_fmt, setup_file->FileName);
4714 ptr += lstrlenW(ptr) + 1;
4717 translator_path = strdupW(translator_file->TargetPath);
4718 ptr = strrchrW(translator_path, '\\');
4719 if (ptr) *ptr = '\0';
4721 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4722 NULL, ODBC_INSTALL_COMPLETE, &usage))
4724 ERR("Failed to install SQL translator!\n");
4725 r = ERROR_FUNCTION_FAILED;
4728 msi_free(translator);
4729 msi_free(translator_path);
4734 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4737 LPCWSTR desc, driver;
4738 WORD request = ODBC_ADD_SYS_DSN;
4741 UINT r = ERROR_SUCCESS;
4743 static const WCHAR attrs_fmt[] = {
4744 'D','S','N','=','%','s',0 };
4746 desc = MSI_RecordGetString(rec, 3);
4747 driver = MSI_RecordGetString(rec, 4);
4748 registration = MSI_RecordGetInteger(rec, 5);
4750 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4751 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4753 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4754 attrs = msi_alloc(len * sizeof(WCHAR));
4756 return ERROR_OUTOFMEMORY;
4758 sprintfW(attrs, attrs_fmt, desc);
4759 attrs[len - 1] = '\0';
4761 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4763 ERR("Failed to install SQL data source!\n");
4764 r = ERROR_FUNCTION_FAILED;
4772 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4777 static const WCHAR driver_query[] = {
4778 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4779 'O','D','B','C','D','r','i','v','e','r',0 };
4781 static const WCHAR translator_query[] = {
4782 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4783 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4785 static const WCHAR source_query[] = {
4786 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4787 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4789 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4790 if (rc != ERROR_SUCCESS)
4791 return ERROR_SUCCESS;
4793 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4794 msiobj_release(&view->hdr);
4796 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4797 if (rc != ERROR_SUCCESS)
4798 return ERROR_SUCCESS;
4800 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4801 msiobj_release(&view->hdr);
4803 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4804 if (rc != ERROR_SUCCESS)
4805 return ERROR_SUCCESS;
4807 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4808 msiobj_release(&view->hdr);
4813 #define ENV_ACT_SETALWAYS 0x1
4814 #define ENV_ACT_SETABSENT 0x2
4815 #define ENV_ACT_REMOVE 0x4
4816 #define ENV_ACT_REMOVEMATCH 0x8
4818 #define ENV_MOD_MACHINE 0x20000000
4819 #define ENV_MOD_APPEND 0x40000000
4820 #define ENV_MOD_PREFIX 0x80000000
4821 #define ENV_MOD_MASK 0xC0000000
4823 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4825 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4827 LPCWSTR cptr = *name;
4828 LPCWSTR ptr = *value;
4830 static const WCHAR prefix[] = {'[','~',']',0};
4831 static const int prefix_len = 3;
4837 *flags |= ENV_ACT_SETALWAYS;
4838 else if (*cptr == '+')
4839 *flags |= ENV_ACT_SETABSENT;
4840 else if (*cptr == '-')
4841 *flags |= ENV_ACT_REMOVE;
4842 else if (*cptr == '!')
4843 *flags |= ENV_ACT_REMOVEMATCH;
4844 else if (*cptr == '*')
4845 *flags |= ENV_MOD_MACHINE;
4855 ERR("Missing environment variable\n");
4856 return ERROR_FUNCTION_FAILED;
4859 if (!strncmpW(ptr, prefix, prefix_len))
4861 *flags |= ENV_MOD_APPEND;
4862 *value += lstrlenW(prefix);
4864 else if (lstrlenW(*value) >= prefix_len)
4866 ptr += lstrlenW(ptr) - prefix_len;
4867 if (!lstrcmpW(ptr, prefix))
4869 *flags |= ENV_MOD_PREFIX;
4870 /* the "[~]" will be removed by deformat_string */;
4875 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4876 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4877 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4878 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4880 ERR("Invalid flags: %08x\n", *flags);
4881 return ERROR_FUNCTION_FAILED;
4884 return ERROR_SUCCESS;
4887 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4889 MSIPACKAGE *package = param;
4890 LPCWSTR name, value, comp;
4891 LPWSTR data = NULL, newval = NULL;
4892 LPWSTR deformatted = NULL, ptr;
4893 DWORD flags, type, size;
4895 HKEY env = NULL, root;
4896 LPCWSTR environment;
4898 static const WCHAR user_env[] =
4899 {'E','n','v','i','r','o','n','m','e','n','t',0};
4900 static const WCHAR machine_env[] =
4901 {'S','y','s','t','e','m','\\',
4902 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4903 'C','o','n','t','r','o','l','\\',
4904 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4905 'E','n','v','i','r','o','n','m','e','n','t',0};
4906 static const WCHAR semicolon[] = {';',0};
4908 name = MSI_RecordGetString(rec, 2);
4909 value = MSI_RecordGetString(rec, 3);
4910 comp = MSI_RecordGetString(rec, 4);
4912 res = env_set_flags(&name, &value, &flags);
4913 if (res != ERROR_SUCCESS)
4916 deformat_string(package, value, &deformatted);
4919 res = ERROR_OUTOFMEMORY;
4923 value = deformatted;
4925 if (flags & ENV_MOD_MACHINE)
4927 environment = machine_env;
4928 root = HKEY_LOCAL_MACHINE;
4932 environment = user_env;
4933 root = HKEY_CURRENT_USER;
4936 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
4937 KEY_ALL_ACCESS, NULL, &env, NULL);
4938 if (res != ERROR_SUCCESS)
4941 if (flags & ENV_ACT_REMOVE)
4942 FIXME("Not removing environment variable on uninstall!\n");
4945 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
4946 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
4947 (res == ERROR_SUCCESS && type != REG_SZ))
4950 if (res != ERROR_FILE_NOT_FOUND)
4952 if (flags & ENV_ACT_SETABSENT)
4954 res = ERROR_SUCCESS;
4958 data = msi_alloc(size);
4962 return ERROR_OUTOFMEMORY;
4965 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
4966 if (res != ERROR_SUCCESS)
4969 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
4971 res = RegDeleteKeyW(env, name);
4975 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
4976 newval = msi_alloc(size);
4980 res = ERROR_OUTOFMEMORY;
4984 if (!(flags & ENV_MOD_MASK))
4985 lstrcpyW(newval, value);
4988 if (flags & ENV_MOD_PREFIX)
4990 lstrcpyW(newval, value);
4991 lstrcatW(newval, semicolon);
4992 ptr = newval + lstrlenW(value) + 1;
4995 lstrcpyW(ptr, data);
4997 if (flags & ENV_MOD_APPEND)
4999 lstrcatW(newval, semicolon);
5000 lstrcatW(newval, value);
5006 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5007 newval = msi_alloc(size);
5010 res = ERROR_OUTOFMEMORY;
5014 lstrcpyW(newval, value);
5017 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5018 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5021 if (env) RegCloseKey(env);
5022 msi_free(deformatted);
5028 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5032 static const WCHAR ExecSeqQuery[] =
5033 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5034 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5035 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5036 if (rc != ERROR_SUCCESS)
5037 return ERROR_SUCCESS;
5039 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5040 msiobj_release(&view->hdr);
5045 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5056 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5060 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5061 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5063 WARN("Source or dest is directory, not moving\n");
5067 if (options == msidbMoveFileOptionsMove)
5069 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5070 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5073 WARN("MoveFile failed: %d\n", GetLastError());
5079 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5080 ret = CopyFileW(source, dest, FALSE);
5083 WARN("CopyFile failed: %d\n", GetLastError());
5091 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5094 DWORD dirlen, pathlen;
5096 ptr = strrchrW(wildcard, '\\');
5097 dirlen = ptr - wildcard + 1;
5099 pathlen = dirlen + lstrlenW(filename) + 1;
5100 path = msi_alloc(pathlen * sizeof(WCHAR));
5102 lstrcpynW(path, wildcard, dirlen + 1);
5103 lstrcatW(path, filename);
5108 static void free_file_entry(FILE_LIST *file)
5110 msi_free(file->source);
5111 msi_free(file->dest);
5115 static void free_list(FILE_LIST *list)
5117 while (!list_empty(&list->entry))
5119 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5121 list_remove(&file->entry);
5122 free_file_entry(file);
5126 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5128 FILE_LIST *new, *file;
5129 LPWSTR ptr, filename;
5132 new = msi_alloc_zero(sizeof(FILE_LIST));
5136 new->source = strdupW(source);
5137 ptr = strrchrW(dest, '\\') + 1;
5138 filename = strrchrW(new->source, '\\') + 1;
5140 new->sourcename = filename;
5143 new->destname = ptr;
5145 new->destname = new->sourcename;
5147 size = (ptr - dest) + lstrlenW(filename) + 1;
5148 new->dest = msi_alloc(size * sizeof(WCHAR));
5151 free_file_entry(new);
5155 lstrcpynW(new->dest, dest, ptr - dest + 1);
5156 lstrcatW(new->dest, filename);
5158 if (list_empty(&files->entry))
5160 list_add_head(&files->entry, &new->entry);
5164 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5166 if (lstrcmpW(source, file->source) < 0)
5168 list_add_before(&file->entry, &new->entry);
5173 list_add_after(&file->entry, &new->entry);
5177 BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5179 WIN32_FIND_DATAW wfd;
5183 FILE_LIST files, *file;
5186 hfile = FindFirstFileW(source, &wfd);
5187 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5189 list_init(&files.entry);
5191 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5193 if (is_dot_dir(wfd.cFileName)) continue;
5195 path = wildcard_to_file(source, wfd.cFileName);
5202 add_wildcard(&files, path, dest);
5206 /* only the first wildcard match gets renamed to dest */
5207 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5208 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5209 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5216 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5218 while (!list_empty(&files.entry))
5220 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5222 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5224 list_remove(&file->entry);
5225 free_file_entry(file);
5236 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5238 MSIPACKAGE *package = param;
5240 LPCWSTR sourcename, destname;
5241 LPWSTR sourcedir = NULL, destdir = NULL;
5242 LPWSTR source = NULL, dest = NULL;
5245 BOOL ret, wildcards;
5247 static const WCHAR backslash[] = {'\\',0};
5249 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5250 if (!comp || !comp->Enabled ||
5251 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5253 TRACE("Component not set for install, not moving file\n");
5254 return ERROR_SUCCESS;
5257 sourcename = MSI_RecordGetString(rec, 3);
5258 destname = MSI_RecordGetString(rec, 4);
5259 options = MSI_RecordGetInteger(rec, 7);
5261 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5265 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5271 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5274 source = strdupW(sourcedir);
5280 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5281 source = msi_alloc(size * sizeof(WCHAR));
5285 lstrcpyW(source, sourcedir);
5286 if (source[lstrlenW(source) - 1] != '\\')
5287 lstrcatW(source, backslash);
5288 lstrcatW(source, sourcename);
5291 wildcards = strchrW(source, '*') || strchrW(source, '?');
5293 if (!destname && !wildcards)
5295 destname = strdupW(sourcename);
5302 size = lstrlenW(destname);
5304 size += lstrlenW(destdir) + 2;
5305 dest = msi_alloc(size * sizeof(WCHAR));
5309 lstrcpyW(dest, destdir);
5310 if (dest[lstrlenW(dest) - 1] != '\\')
5311 lstrcatW(dest, backslash);
5314 lstrcatW(dest, destname);
5316 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5318 ret = CreateDirectoryW(destdir, NULL);
5321 WARN("CreateDirectory failed: %d\n", GetLastError());
5322 return ERROR_SUCCESS;
5327 msi_move_file(source, dest, options);
5329 move_files_wildcard(source, dest, options);
5332 msi_free(sourcedir);
5337 return ERROR_SUCCESS;
5340 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5345 static const WCHAR ExecSeqQuery[] =
5346 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5347 '`','M','o','v','e','F','i','l','e','`',0};
5349 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5350 if (rc != ERROR_SUCCESS)
5351 return ERROR_SUCCESS;
5353 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5354 msiobj_release(&view->hdr);
5359 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5360 LPCSTR action, LPCWSTR table )
5362 static const WCHAR query[] = {
5363 'S','E','L','E','C','T',' ','*',' ',
5364 'F','R','O','M',' ','`','%','s','`',0 };
5365 MSIQUERY *view = NULL;
5369 r = MSI_OpenQuery( package->db, &view, query, table );
5370 if (r == ERROR_SUCCESS)
5372 r = MSI_IterateRecords(view, &count, NULL, package);
5373 msiobj_release(&view->hdr);
5377 FIXME("%s -> %u ignored %s table values\n",
5378 action, count, debugstr_w(table));
5380 return ERROR_SUCCESS;
5383 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5385 TRACE("%p\n", package);
5386 return ERROR_SUCCESS;
5389 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5391 static const WCHAR table[] =
5392 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5393 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5396 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5398 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5399 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5402 static UINT ACTION_BindImage( MSIPACKAGE *package )
5404 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5405 return msi_unimplemented_action_stub( package, "BindImage", table );
5408 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5410 static const WCHAR table[] = {
5411 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5412 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5415 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5417 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5418 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5421 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5423 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5424 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5427 static UINT ACTION_StopServices( MSIPACKAGE *package )
5429 static const WCHAR table[] = {
5430 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5431 return msi_unimplemented_action_stub( package, "StopServices", table );
5434 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5436 static const WCHAR table[] = {
5437 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5438 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5440 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5442 static const WCHAR table[] = {
5443 'P','r','o','d','u','c','t','I','D',0 };
5444 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5447 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5449 static const WCHAR table[] = {
5450 'E','n','v','i','r','o','n','m','e','n','t',0 };
5451 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5454 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5456 static const WCHAR table[] = {
5457 'M','s','i','A','s','s','e','m','b','l','y',0 };
5458 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5461 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5463 static const WCHAR table[] = {
5464 'M','s','i','A','s','s','e','m','b','l','y',0 };
5465 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5468 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5470 static const WCHAR table[] = { 'F','o','n','t',0 };
5471 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5474 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5476 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5477 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5480 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5482 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5483 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5486 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5488 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5489 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5492 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5494 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5495 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5498 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5500 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5501 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5504 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5506 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5507 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5510 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5512 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5513 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5516 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5518 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5519 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5522 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5524 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5525 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5528 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5530 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5531 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5534 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5536 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5537 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5540 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5542 static const WCHAR table[] = { 'A','p','p','I','d',0 };
5543 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5546 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5548 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5549 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5552 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5554 static const WCHAR table[] = { 'M','I','M','E',0 };
5555 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5558 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5560 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5561 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5564 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5566 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5567 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5570 static const struct _actions StandardActions[] = {
5571 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5572 { szAppSearch, ACTION_AppSearch },
5573 { szBindImage, ACTION_BindImage },
5574 { szCCPSearch, ACTION_CCPSearch },
5575 { szCostFinalize, ACTION_CostFinalize },
5576 { szCostInitialize, ACTION_CostInitialize },
5577 { szCreateFolders, ACTION_CreateFolders },
5578 { szCreateShortcuts, ACTION_CreateShortcuts },
5579 { szDeleteServices, ACTION_DeleteServices },
5580 { szDisableRollback, NULL },
5581 { szDuplicateFiles, ACTION_DuplicateFiles },
5582 { szExecuteAction, ACTION_ExecuteAction },
5583 { szFileCost, ACTION_FileCost },
5584 { szFindRelatedProducts, ACTION_FindRelatedProducts },
5585 { szForceReboot, ACTION_ForceReboot },
5586 { szInstallAdminPackage, NULL },
5587 { szInstallExecute, ACTION_InstallExecute },
5588 { szInstallExecuteAgain, ACTION_InstallExecute },
5589 { szInstallFiles, ACTION_InstallFiles},
5590 { szInstallFinalize, ACTION_InstallFinalize },
5591 { szInstallInitialize, ACTION_InstallInitialize },
5592 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5593 { szInstallValidate, ACTION_InstallValidate },
5594 { szIsolateComponents, ACTION_IsolateComponents },
5595 { szLaunchConditions, ACTION_LaunchConditions },
5596 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5597 { szMoveFiles, ACTION_MoveFiles },
5598 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5599 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5600 { szInstallODBC, ACTION_InstallODBC },
5601 { szInstallServices, ACTION_InstallServices },
5602 { szPatchFiles, ACTION_PatchFiles },
5603 { szProcessComponents, ACTION_ProcessComponents },
5604 { szPublishComponents, ACTION_PublishComponents },
5605 { szPublishFeatures, ACTION_PublishFeatures },
5606 { szPublishProduct, ACTION_PublishProduct },
5607 { szRegisterClassInfo, ACTION_RegisterClassInfo },
5608 { szRegisterComPlus, ACTION_RegisterComPlus},
5609 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5610 { szRegisterFonts, ACTION_RegisterFonts },
5611 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5612 { szRegisterProduct, ACTION_RegisterProduct },
5613 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5614 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5615 { szRegisterUser, ACTION_RegisterUser },
5616 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5617 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5618 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5619 { szRemoveFiles, ACTION_RemoveFiles },
5620 { szRemoveFolders, ACTION_RemoveFolders },
5621 { szRemoveIniValues, ACTION_RemoveIniValues },
5622 { szRemoveODBC, ACTION_RemoveODBC },
5623 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5624 { szRemoveShortcuts, ACTION_RemoveShortcuts },
5625 { szResolveSource, ACTION_ResolveSource },
5626 { szRMCCPSearch, ACTION_RMCCPSearch },
5627 { szScheduleReboot, NULL },
5628 { szSelfRegModules, ACTION_SelfRegModules },
5629 { szSelfUnregModules, ACTION_SelfUnregModules },
5630 { szSetODBCFolders, NULL },
5631 { szStartServices, ACTION_StartServices },
5632 { szStopServices, ACTION_StopServices },
5633 { szUnpublishComponents, ACTION_UnpublishComponents },
5634 { szUnpublishFeatures, ACTION_UnpublishFeatures },
5635 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5636 { szUnregisterComPlus, ACTION_UnregisterComPlus },
5637 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5638 { szUnregisterFonts, ACTION_UnregisterFonts },
5639 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5640 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5641 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5642 { szValidateProductID, ACTION_ValidateProductID },
5643 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5644 { szWriteIniValues, ACTION_WriteIniValues },
5645 { szWriteRegistryValues, ACTION_WriteRegistryValues },