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;
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);
1432 static UINT load_file(MSIRECORD *row, LPVOID param)
1434 MSIPACKAGE* package = (MSIPACKAGE*)param;
1438 /* fill in the data */
1440 file = msi_alloc_zero( sizeof (MSIFILE) );
1442 return ERROR_NOT_ENOUGH_MEMORY;
1444 file->File = msi_dup_record_field( row, 1 );
1446 component = MSI_RecordGetString( row, 2 );
1447 file->Component = get_loaded_component( package, component );
1449 if (!file->Component)
1450 ERR("Unfound Component %s\n",debugstr_w(component));
1452 file->FileName = msi_dup_record_field( row, 3 );
1453 reduce_to_longfilename( file->FileName );
1455 file->ShortName = msi_dup_record_field( row, 3 );
1456 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1458 file->FileSize = MSI_RecordGetInteger( row, 4 );
1459 file->Version = msi_dup_record_field( row, 5 );
1460 file->Language = msi_dup_record_field( row, 6 );
1461 file->Attributes = MSI_RecordGetInteger( row, 7 );
1462 file->Sequence = MSI_RecordGetInteger( row, 8 );
1464 file->state = msifs_invalid;
1466 /* if the compressed bits are not set in the file attributes,
1467 * then read the information from the package word count property
1469 if (file->Attributes & msidbFileAttributesCompressed)
1471 file->IsCompressed = TRUE;
1473 else if (file->Attributes & msidbFileAttributesNoncompressed)
1475 file->IsCompressed = FALSE;
1479 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1482 load_file_hash(package, file);
1484 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1486 list_add_tail( &package->files, &file->entry );
1488 return ERROR_SUCCESS;
1491 static UINT load_all_files(MSIPACKAGE *package)
1495 static const WCHAR Query[] =
1496 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1497 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1498 '`','S','e','q','u','e','n','c','e','`', 0};
1500 if (!list_empty(&package->files))
1501 return ERROR_SUCCESS;
1503 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1504 if (rc != ERROR_SUCCESS)
1505 return ERROR_SUCCESS;
1507 rc = MSI_IterateRecords(view, NULL, load_file, package);
1508 msiobj_release(&view->hdr);
1510 return ERROR_SUCCESS;
1513 static UINT load_folder( MSIRECORD *row, LPVOID param )
1515 MSIPACKAGE *package = param;
1516 static const WCHAR szDot[] = { '.',0 };
1517 static WCHAR szEmpty[] = { 0 };
1518 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1521 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1523 return ERROR_NOT_ENOUGH_MEMORY;
1525 folder->Directory = msi_dup_record_field( row, 1 );
1527 TRACE("%s\n", debugstr_w(folder->Directory));
1529 p = msi_dup_record_field(row, 3);
1531 /* split src and target dir */
1533 src_short = folder_split_path( p, ':' );
1535 /* split the long and short paths */
1536 tgt_long = folder_split_path( tgt_short, '|' );
1537 src_long = folder_split_path( src_short, '|' );
1539 /* check for no-op dirs */
1540 if (!lstrcmpW(szDot, tgt_short))
1541 tgt_short = szEmpty;
1542 if (!lstrcmpW(szDot, src_short))
1543 src_short = szEmpty;
1546 tgt_long = tgt_short;
1549 src_short = tgt_short;
1550 src_long = tgt_long;
1554 src_long = src_short;
1556 /* FIXME: use the target short path too */
1557 folder->TargetDefault = strdupW(tgt_long);
1558 folder->SourceShortPath = strdupW(src_short);
1559 folder->SourceLongPath = strdupW(src_long);
1562 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1563 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1564 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1566 folder->Parent = msi_dup_record_field( row, 2 );
1568 folder->Property = msi_dup_property( package, folder->Directory );
1570 list_add_tail( &package->folders, &folder->entry );
1572 TRACE("returning %p\n", folder);
1574 return ERROR_SUCCESS;
1577 static UINT load_all_folders( MSIPACKAGE *package )
1579 static const WCHAR query[] = {
1580 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1581 '`','D','i','r','e','c','t','o','r','y','`',0 };
1585 if (!list_empty(&package->folders))
1586 return ERROR_SUCCESS;
1588 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1589 if (r != ERROR_SUCCESS)
1592 r = MSI_IterateRecords(view, NULL, load_folder, package);
1593 msiobj_release(&view->hdr);
1598 * I am not doing any of the costing functionality yet.
1599 * Mostly looking at doing the Component and Feature loading
1601 * The native MSI does A LOT of modification to tables here. Mostly adding
1602 * a lot of temporary columns to the Feature and Component tables.
1604 * note: Native msi also tracks the short filename. But I am only going to
1605 * track the long ones. Also looking at this directory table
1606 * it appears that the directory table does not get the parents
1607 * resolved base on property only based on their entries in the
1610 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1612 static const WCHAR szCosting[] =
1613 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1614 static const WCHAR szZero[] = { '0', 0 };
1616 MSI_SetPropertyW(package, szCosting, szZero);
1617 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1619 load_all_components( package );
1620 load_all_features( package );
1621 load_all_files( package );
1622 load_all_folders( package );
1624 return ERROR_SUCCESS;
1627 static UINT execute_script(MSIPACKAGE *package, UINT script )
1630 UINT rc = ERROR_SUCCESS;
1632 TRACE("Executing Script %i\n",script);
1634 if (!package->script)
1636 ERR("no script!\n");
1637 return ERROR_FUNCTION_FAILED;
1640 for (i = 0; i < package->script->ActionCount[script]; i++)
1643 action = package->script->Actions[script][i];
1644 ui_actionstart(package, action);
1645 TRACE("Executing Action (%s)\n",debugstr_w(action));
1646 rc = ACTION_PerformAction(package, action, script, TRUE);
1647 if (rc != ERROR_SUCCESS)
1650 msi_free_action_script(package, script);
1654 static UINT ACTION_FileCost(MSIPACKAGE *package)
1656 return ERROR_SUCCESS;
1659 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1663 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1667 if (!comp->ComponentId)
1670 res = MsiGetComponentPathW( package->ProductCode,
1671 comp->ComponentId, NULL, NULL);
1673 res = INSTALLSTATE_ABSENT;
1674 comp->Installed = res;
1678 /* scan for and update current install states */
1679 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1682 MSIFEATURE *feature;
1684 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1687 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1689 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1691 comp= cl->component;
1693 if (!comp->ComponentId)
1695 res = INSTALLSTATE_ABSENT;
1699 if (res == INSTALLSTATE_ABSENT)
1700 res = comp->Installed;
1703 if (res == comp->Installed)
1706 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1707 res != INSTALLSTATE_SOURCE)
1709 res = INSTALLSTATE_INCOMPLETE;
1713 feature->Installed = res;
1717 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1720 static const WCHAR all[]={'A','L','L',0};
1722 MSIFEATURE *feature;
1724 override = msi_dup_property( package, property );
1728 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1730 if (strcmpiW(override,all)==0)
1731 msi_feature_set_state( feature, state );
1734 LPWSTR ptr = override;
1735 LPWSTR ptr2 = strchrW(override,',');
1739 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1740 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1742 msi_feature_set_state( feature, state );
1748 ptr2 = strchrW(ptr,',');
1760 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1763 static const WCHAR szlevel[] =
1764 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1765 static const WCHAR szAddLocal[] =
1766 {'A','D','D','L','O','C','A','L',0};
1767 static const WCHAR szAddSource[] =
1768 {'A','D','D','S','O','U','R','C','E',0};
1769 static const WCHAR szRemove[] =
1770 {'R','E','M','O','V','E',0};
1771 static const WCHAR szReinstall[] =
1772 {'R','E','I','N','S','T','A','L','L',0};
1773 BOOL override = FALSE;
1774 MSICOMPONENT* component;
1775 MSIFEATURE *feature;
1778 /* I do not know if this is where it should happen.. but */
1780 TRACE("Checking Install Level\n");
1782 install_level = msi_get_property_int( package, szlevel, 1 );
1784 /* ok here is the _real_ rub
1785 * all these activation/deactivation things happen in order and things
1786 * later on the list override things earlier on the list.
1787 * 1) INSTALLLEVEL processing
1797 * 11) FILEADDDEFAULT
1798 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1799 * ignored for all the features. seems strange, especially since it is not
1800 * documented anywhere, but it is how it works.
1802 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1803 * REMOVE are the big ones, since we don't handle administrative installs
1806 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1807 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1808 override |= process_state_property(package,szAddSource,INSTALLSTATE_SOURCE);
1809 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1813 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1815 BOOL feature_state = ((feature->Level > 0) &&
1816 (feature->Level <= install_level));
1818 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1820 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1821 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1822 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1823 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1825 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1829 /* disable child features of unselected parent features */
1830 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1834 if (feature->Level > 0 && feature->Level <= install_level)
1837 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1838 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1843 /* set the Preselected Property */
1844 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1845 static const WCHAR szOne[] = { '1', 0 };
1847 MSI_SetPropertyW(package,szPreselected,szOne);
1851 * now we want to enable or disable components base on feature
1854 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1858 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1859 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1861 /* features with components that have compressed files are made local */
1862 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1864 if (cl->component->Enabled &&
1865 cl->component->ForceLocalState &&
1866 feature->Action == INSTALLSTATE_SOURCE)
1868 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1873 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1875 component = cl->component;
1877 if (!component->Enabled)
1880 switch (feature->Action)
1882 case INSTALLSTATE_ABSENT:
1883 component->anyAbsent = 1;
1885 case INSTALLSTATE_ADVERTISED:
1886 component->hasAdvertiseFeature = 1;
1888 case INSTALLSTATE_SOURCE:
1889 component->hasSourceFeature = 1;
1891 case INSTALLSTATE_LOCAL:
1892 component->hasLocalFeature = 1;
1894 case INSTALLSTATE_DEFAULT:
1895 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1896 component->hasAdvertiseFeature = 1;
1897 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1898 component->hasSourceFeature = 1;
1900 component->hasLocalFeature = 1;
1908 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1910 /* if the component isn't enabled, leave it alone */
1911 if (!component->Enabled)
1914 /* check if it's local or source */
1915 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1916 (component->hasLocalFeature || component->hasSourceFeature))
1918 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1919 !component->ForceLocalState)
1920 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1922 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1926 /* if any feature is local, the component must be local too */
1927 if (component->hasLocalFeature)
1929 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1933 if (component->hasSourceFeature)
1935 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1939 if (component->hasAdvertiseFeature)
1941 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1945 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1946 if (component->anyAbsent)
1947 msi_component_set_state(component, INSTALLSTATE_ABSENT);
1950 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1952 if (component->Action == INSTALLSTATE_DEFAULT)
1954 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1955 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1958 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1959 debugstr_w(component->Component), component->Installed, component->Action);
1963 return ERROR_SUCCESS;
1966 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1968 MSIPACKAGE *package = (MSIPACKAGE*)param;
1973 name = MSI_RecordGetString(row,1);
1975 f = get_loaded_folder(package, name);
1976 if (!f) return ERROR_SUCCESS;
1978 /* reset the ResolvedTarget */
1979 msi_free(f->ResolvedTarget);
1980 f->ResolvedTarget = NULL;
1982 /* This helper function now does ALL the work */
1983 TRACE("Dir %s ...\n",debugstr_w(name));
1984 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1985 TRACE("resolves to %s\n",debugstr_w(path));
1988 return ERROR_SUCCESS;
1991 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1993 MSIPACKAGE *package = (MSIPACKAGE*)param;
1995 MSIFEATURE *feature;
1997 name = MSI_RecordGetString( row, 1 );
1999 feature = get_loaded_feature( package, name );
2001 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2005 Condition = MSI_RecordGetString(row,3);
2007 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2009 int level = MSI_RecordGetInteger(row,2);
2010 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
2011 feature->Level = level;
2014 return ERROR_SUCCESS;
2017 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2019 static const WCHAR name_fmt[] =
2020 {'%','u','.','%','u','.','%','u','.','%','u',0};
2021 static WCHAR name[] = {'\\',0};
2022 VS_FIXEDFILEINFO *lpVer;
2023 WCHAR filever[0x100];
2029 TRACE("%s\n", debugstr_w(filename));
2031 versize = GetFileVersionInfoSizeW( filename, &handle );
2035 version = msi_alloc( versize );
2036 GetFileVersionInfoW( filename, 0, versize, version );
2038 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2040 msi_free( version );
2044 sprintfW( filever, name_fmt,
2045 HIWORD(lpVer->dwFileVersionMS),
2046 LOWORD(lpVer->dwFileVersionMS),
2047 HIWORD(lpVer->dwFileVersionLS),
2048 LOWORD(lpVer->dwFileVersionLS));
2050 msi_free( version );
2052 return strdupW( filever );
2055 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2057 LPWSTR file_version;
2060 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2062 MSICOMPONENT* comp = file->Component;
2068 if (file->IsCompressed)
2069 comp->ForceLocalState = TRUE;
2071 /* calculate target */
2072 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2074 msi_free(file->TargetPath);
2076 TRACE("file %s is named %s\n",
2077 debugstr_w(file->File), debugstr_w(file->FileName));
2079 file->TargetPath = build_directory_name(2, p, file->FileName);
2083 TRACE("file %s resolves to %s\n",
2084 debugstr_w(file->File), debugstr_w(file->TargetPath));
2086 /* don't check files of components that aren't installed */
2087 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2088 comp->Installed == INSTALLSTATE_ABSENT)
2090 file->state = msifs_missing; /* assume files are missing */
2094 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2096 file->state = msifs_missing;
2097 comp->Cost += file->FileSize;
2098 comp->Installed = INSTALLSTATE_INCOMPLETE;
2102 if (file->Version &&
2103 (file_version = msi_get_disk_file_version( file->TargetPath )))
2105 TRACE("new %s old %s\n", debugstr_w(file->Version),
2106 debugstr_w(file_version));
2107 /* FIXME: seems like a bad way to compare version numbers */
2108 if (lstrcmpiW(file_version, file->Version)<0)
2110 file->state = msifs_overwrite;
2111 comp->Cost += file->FileSize;
2112 comp->Installed = INSTALLSTATE_INCOMPLETE;
2115 file->state = msifs_present;
2116 msi_free( file_version );
2119 file->state = msifs_present;
2122 return ERROR_SUCCESS;
2126 * A lot is done in this function aside from just the costing.
2127 * The costing needs to be implemented at some point but for now I am going
2128 * to focus on the directory building
2131 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2133 static const WCHAR ExecSeqQuery[] =
2134 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2135 '`','D','i','r','e','c','t','o','r','y','`',0};
2136 static const WCHAR ConditionQuery[] =
2137 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2138 '`','C','o','n','d','i','t','i','o','n','`',0};
2139 static const WCHAR szCosting[] =
2140 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2141 static const WCHAR szlevel[] =
2142 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2143 static const WCHAR szOne[] = { '1', 0 };
2149 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2150 return ERROR_SUCCESS;
2152 TRACE("Building Directory properties\n");
2154 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2155 if (rc == ERROR_SUCCESS)
2157 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2159 msiobj_release(&view->hdr);
2162 /* read components states from the registry */
2163 ACTION_GetComponentInstallStates(package);
2165 TRACE("File calculations\n");
2166 msi_check_file_install_states( package );
2168 TRACE("Evaluating Condition Table\n");
2170 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2171 if (rc == ERROR_SUCCESS)
2173 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2175 msiobj_release(&view->hdr);
2178 TRACE("Enabling or Disabling Components\n");
2179 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2181 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2183 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2184 comp->Enabled = FALSE;
2188 MSI_SetPropertyW(package,szCosting,szOne);
2189 /* set default run level if not set */
2190 level = msi_dup_property( package, szlevel );
2192 MSI_SetPropertyW(package,szlevel, szOne);
2195 ACTION_UpdateFeatureInstallStates(package);
2197 return MSI_SetFeatureStates(package);
2200 /* OK this value is "interpreted" and then formatted based on the
2201 first few characters */
2202 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2206 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2212 LPWSTR deformated = NULL;
2215 deformat_string(package, &value[2], &deformated);
2217 /* binary value type */
2221 *size = (strlenW(ptr)/2)+1;
2223 *size = strlenW(ptr)/2;
2225 data = msi_alloc(*size);
2231 /* if uneven pad with a zero in front */
2237 data[count] = (BYTE)strtol(byte,NULL,0);
2239 TRACE("Uneven byte count\n");
2247 data[count] = (BYTE)strtol(byte,NULL,0);
2250 msi_free(deformated);
2252 TRACE("Data %i bytes(%i)\n",*size,count);
2259 deformat_string(package, &value[1], &deformated);
2262 *size = sizeof(DWORD);
2263 data = msi_alloc(*size);
2269 if ( (*p < '0') || (*p > '9') )
2275 if (deformated[0] == '-')
2278 TRACE("DWORD %i\n",*(LPDWORD)data);
2280 msi_free(deformated);
2285 static const WCHAR szMulti[] = {'[','~',']',0};
2294 *type=REG_EXPAND_SZ;
2302 if (strstrW(value,szMulti))
2303 *type = REG_MULTI_SZ;
2305 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2310 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2312 MSIPACKAGE *package = (MSIPACKAGE*)param;
2313 static const WCHAR szHCR[] =
2314 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2315 'R','O','O','T','\\',0};
2316 static const WCHAR szHCU[] =
2317 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2318 'U','S','E','R','\\',0};
2319 static const WCHAR szHLM[] =
2320 {'H','K','E','Y','_','L','O','C','A','L','_',
2321 'M','A','C','H','I','N','E','\\',0};
2322 static const WCHAR szHU[] =
2323 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2325 LPSTR value_data = NULL;
2326 HKEY root_key, hkey;
2329 LPCWSTR szRoot, component, name, key, value;
2334 BOOL check_first = FALSE;
2337 ui_progress(package,2,0,0,0);
2344 component = MSI_RecordGetString(row, 6);
2345 comp = get_loaded_component(package,component);
2347 return ERROR_SUCCESS;
2349 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2351 TRACE("Skipping write due to disabled component %s\n",
2352 debugstr_w(component));
2354 comp->Action = comp->Installed;
2356 return ERROR_SUCCESS;
2359 comp->Action = INSTALLSTATE_LOCAL;
2361 name = MSI_RecordGetString(row, 4);
2362 if( MSI_RecordIsNull(row,5) && name )
2364 /* null values can have special meanings */
2365 if (name[0]=='-' && name[1] == 0)
2366 return ERROR_SUCCESS;
2367 else if ((name[0]=='+' && name[1] == 0) ||
2368 (name[0] == '*' && name[1] == 0))
2373 root = MSI_RecordGetInteger(row,2);
2374 key = MSI_RecordGetString(row, 3);
2376 /* get the root key */
2381 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2382 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2383 if (all_users && all_users[0] == '1')
2385 root_key = HKEY_LOCAL_MACHINE;
2390 root_key = HKEY_CURRENT_USER;
2393 msi_free(all_users);
2396 case 0: root_key = HKEY_CLASSES_ROOT;
2399 case 1: root_key = HKEY_CURRENT_USER;
2402 case 2: root_key = HKEY_LOCAL_MACHINE;
2405 case 3: root_key = HKEY_USERS;
2409 ERR("Unknown root %i\n",root);
2415 return ERROR_SUCCESS;
2417 deformat_string(package, key , &deformated);
2418 size = strlenW(deformated) + strlenW(szRoot) + 1;
2419 uikey = msi_alloc(size*sizeof(WCHAR));
2420 strcpyW(uikey,szRoot);
2421 strcatW(uikey,deformated);
2423 if (RegCreateKeyW( root_key, deformated, &hkey))
2425 ERR("Could not create key %s\n",debugstr_w(deformated));
2426 msi_free(deformated);
2428 return ERROR_SUCCESS;
2430 msi_free(deformated);
2432 value = MSI_RecordGetString(row,5);
2434 value_data = parse_value(package, value, &type, &size);
2437 static const WCHAR szEmpty[] = {0};
2438 value_data = (LPSTR)strdupW(szEmpty);
2443 deformat_string(package, name, &deformated);
2445 /* get the double nulls to terminate SZ_MULTI */
2446 if (type == REG_MULTI_SZ)
2447 size +=sizeof(WCHAR);
2451 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2453 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2458 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2459 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2461 TRACE("value %s of %s checked already exists\n",
2462 debugstr_w(deformated), debugstr_w(uikey));
2466 TRACE("Checked and setting value %s of %s\n",
2467 debugstr_w(deformated), debugstr_w(uikey));
2468 if (deformated || size)
2469 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2474 uirow = MSI_CreateRecord(3);
2475 MSI_RecordSetStringW(uirow,2,deformated);
2476 MSI_RecordSetStringW(uirow,1,uikey);
2479 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2481 MSI_RecordSetStringW(uirow,3,value);
2483 ui_actiondata(package,szWriteRegistryValues,uirow);
2484 msiobj_release( &uirow->hdr );
2486 msi_free(value_data);
2487 msi_free(deformated);
2490 return ERROR_SUCCESS;
2493 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2497 static const WCHAR ExecSeqQuery[] =
2498 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2499 '`','R','e','g','i','s','t','r','y','`',0 };
2501 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2502 if (rc != ERROR_SUCCESS)
2503 return ERROR_SUCCESS;
2505 /* increment progress bar each time action data is sent */
2506 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2508 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2510 msiobj_release(&view->hdr);
2514 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2516 package->script->CurrentlyScripting = TRUE;
2518 return ERROR_SUCCESS;
2522 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2527 static const WCHAR q1[]=
2528 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2529 '`','R','e','g','i','s','t','r','y','`',0};
2532 MSIFEATURE *feature;
2535 TRACE("InstallValidate\n");
2537 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2538 if (rc == ERROR_SUCCESS)
2540 MSI_IterateRecords( view, &progress, NULL, package );
2541 msiobj_release( &view->hdr );
2542 total += progress * REG_PROGRESS_VALUE;
2545 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2546 total += COMPONENT_PROGRESS_VALUE;
2548 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2549 total += file->FileSize;
2551 ui_progress(package,0,total,0,0);
2553 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2555 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2556 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2557 feature->ActionRequest);
2560 return ERROR_SUCCESS;
2563 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2565 MSIPACKAGE* package = (MSIPACKAGE*)param;
2566 LPCWSTR cond = NULL;
2567 LPCWSTR message = NULL;
2570 static const WCHAR title[]=
2571 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2573 cond = MSI_RecordGetString(row,1);
2575 r = MSI_EvaluateConditionW(package,cond);
2576 if (r == MSICONDITION_FALSE)
2578 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2581 message = MSI_RecordGetString(row,2);
2582 deformat_string(package,message,&deformated);
2583 MessageBoxW(NULL,deformated,title,MB_OK);
2584 msi_free(deformated);
2587 return ERROR_INSTALL_FAILURE;
2590 return ERROR_SUCCESS;
2593 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2596 MSIQUERY * view = NULL;
2597 static const WCHAR ExecSeqQuery[] =
2598 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2599 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2601 TRACE("Checking launch conditions\n");
2603 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2604 if (rc != ERROR_SUCCESS)
2605 return ERROR_SUCCESS;
2607 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2608 msiobj_release(&view->hdr);
2613 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2617 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2619 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2621 MSIRECORD * row = 0;
2623 LPWSTR deformated,buffer,deformated_name;
2625 static const WCHAR ExecSeqQuery[] =
2626 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2627 '`','R','e','g','i','s','t','r','y','`',' ',
2628 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2629 ' ','=',' ' ,'\'','%','s','\'',0 };
2630 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2631 static const WCHAR fmt2[]=
2632 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2634 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2638 root = MSI_RecordGetInteger(row,2);
2639 key = MSI_RecordGetString(row, 3);
2640 name = MSI_RecordGetString(row, 4);
2641 deformat_string(package, key , &deformated);
2642 deformat_string(package, name, &deformated_name);
2644 len = strlenW(deformated) + 6;
2645 if (deformated_name)
2646 len+=strlenW(deformated_name);
2648 buffer = msi_alloc( len *sizeof(WCHAR));
2650 if (deformated_name)
2651 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2653 sprintfW(buffer,fmt,root,deformated);
2655 msi_free(deformated);
2656 msi_free(deformated_name);
2657 msiobj_release(&row->hdr);
2661 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2663 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2668 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2671 return strdupW( file->TargetPath );
2676 static HKEY openSharedDLLsKey(void)
2679 static const WCHAR path[] =
2680 {'S','o','f','t','w','a','r','e','\\',
2681 'M','i','c','r','o','s','o','f','t','\\',
2682 'W','i','n','d','o','w','s','\\',
2683 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2684 'S','h','a','r','e','d','D','L','L','s',0};
2686 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2690 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2695 DWORD sz = sizeof(count);
2698 hkey = openSharedDLLsKey();
2699 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2700 if (rc != ERROR_SUCCESS)
2706 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2710 hkey = openSharedDLLsKey();
2712 msi_reg_set_val_dword( hkey, path, count );
2714 RegDeleteValueW(hkey,path);
2720 * Return TRUE if the count should be written out and FALSE if not
2722 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2724 MSIFEATURE *feature;
2728 /* only refcount DLLs */
2729 if (comp->KeyPath == NULL ||
2730 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2731 comp->Attributes & msidbComponentAttributesODBCDataSource)
2735 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2736 write = (count > 0);
2738 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2742 /* increment counts */
2743 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2747 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2750 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2752 if ( cl->component == comp )
2757 /* decrement counts */
2758 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2762 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2765 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2767 if ( cl->component == comp )
2772 /* ref count all the files in the component */
2777 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2779 if (file->Component == comp)
2780 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2784 /* add a count for permenent */
2785 if (comp->Attributes & msidbComponentAttributesPermanent)
2788 comp->RefCount = count;
2791 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2795 * Ok further analysis makes me think that this work is
2796 * actually done in the PublishComponents and PublishFeatures
2797 * step, and not here. It appears like the keypath and all that is
2798 * resolved in this step, however actually written in the Publish steps.
2799 * But we will leave it here for now because it is unclear
2801 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2803 WCHAR squished_pc[GUID_SIZE];
2804 WCHAR squished_cc[GUID_SIZE];
2807 HKEY hkey=0,hkey2=0;
2811 /* writes the Component and Features values to the registry */
2813 rc = MSIREG_OpenComponents(&hkey);
2814 if (rc != ERROR_SUCCESS)
2817 squash_guid(package->ProductCode,squished_pc);
2818 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2820 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2824 ui_progress(package,2,0,0,0);
2825 if (!comp->ComponentId)
2828 squash_guid(comp->ComponentId,squished_cc);
2830 msi_free(comp->FullKeypath);
2831 comp->FullKeypath = resolve_keypath( package, comp );
2833 /* do the refcounting */
2834 ACTION_RefCountComponent( package, comp );
2836 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2837 debugstr_w(comp->Component),
2838 debugstr_w(squished_cc),
2839 debugstr_w(comp->FullKeypath),
2842 * Write the keypath out if the component is to be registered
2843 * and delete the key if the component is to be deregistered
2845 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2847 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2848 if (rc != ERROR_SUCCESS)
2851 if (!comp->FullKeypath)
2854 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2856 if (comp->Attributes & msidbComponentAttributesPermanent)
2858 static const WCHAR szPermKey[] =
2859 { '0','0','0','0','0','0','0','0','0','0','0','0',
2860 '0','0','0','0','0','0','0','0','0','0','0','0',
2861 '0','0','0','0','0','0','0','0',0 };
2863 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2868 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey2, TRUE);
2869 if (rc != ERROR_SUCCESS)
2872 msi_reg_set_val_str(hkey2, squished_pc, comp->FullKeypath);
2875 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2879 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2880 if (rc != ERROR_SUCCESS)
2883 RegDeleteValueW(hkey2,squished_pc);
2885 /* if the key is empty delete it */
2886 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2888 if (res == ERROR_NO_MORE_ITEMS)
2889 RegDeleteKeyW(hkey,squished_cc);
2891 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2895 uirow = MSI_CreateRecord(3);
2896 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2897 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2898 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2899 ui_actiondata(package,szProcessComponents,uirow);
2900 msiobj_release( &uirow->hdr );
2914 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2915 LPWSTR lpszName, LONG_PTR lParam)
2918 typelib_struct *tl_struct = (typelib_struct*) lParam;
2919 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2923 if (!IS_INTRESOURCE(lpszName))
2925 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2929 sz = strlenW(tl_struct->source)+4;
2930 sz *= sizeof(WCHAR);
2932 if ((INT_PTR)lpszName == 1)
2933 tl_struct->path = strdupW(tl_struct->source);
2936 tl_struct->path = msi_alloc(sz);
2937 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2940 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2941 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2942 if (!SUCCEEDED(res))
2944 msi_free(tl_struct->path);
2945 tl_struct->path = NULL;
2950 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2951 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2953 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2957 msi_free(tl_struct->path);
2958 tl_struct->path = NULL;
2960 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2961 ITypeLib_Release(tl_struct->ptLib);
2966 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2968 MSIPACKAGE* package = (MSIPACKAGE*)param;
2972 typelib_struct tl_struct;
2974 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2976 component = MSI_RecordGetString(row,3);
2977 comp = get_loaded_component(package,component);
2979 return ERROR_SUCCESS;
2981 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2983 TRACE("Skipping typelib reg due to disabled component\n");
2985 comp->Action = comp->Installed;
2987 return ERROR_SUCCESS;
2990 comp->Action = INSTALLSTATE_LOCAL;
2992 file = get_loaded_file( package, comp->KeyPath );
2994 return ERROR_SUCCESS;
2996 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3000 guid = MSI_RecordGetString(row,1);
3001 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3002 tl_struct.source = strdupW( file->TargetPath );
3003 tl_struct.path = NULL;
3005 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3006 (LONG_PTR)&tl_struct);
3014 helpid = MSI_RecordGetString(row,6);
3017 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3018 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3021 if (!SUCCEEDED(res))
3022 ERR("Failed to register type library %s\n",
3023 debugstr_w(tl_struct.path));
3026 ui_actiondata(package,szRegisterTypeLibraries,row);
3028 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3031 ITypeLib_Release(tl_struct.ptLib);
3032 msi_free(tl_struct.path);
3035 ERR("Failed to load type library %s\n",
3036 debugstr_w(tl_struct.source));
3038 FreeLibrary(module);
3039 msi_free(tl_struct.source);
3042 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3044 return ERROR_SUCCESS;
3047 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3050 * OK this is a bit confusing.. I am given a _Component key and I believe
3051 * that the file that is being registered as a type library is the "key file
3052 * of that component" which I interpret to mean "The file in the KeyPath of
3057 static const WCHAR Query[] =
3058 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3059 '`','T','y','p','e','L','i','b','`',0};
3061 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3062 if (rc != ERROR_SUCCESS)
3063 return ERROR_SUCCESS;
3065 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3066 msiobj_release(&view->hdr);
3070 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3072 MSIPACKAGE *package = (MSIPACKAGE*)param;
3073 LPWSTR target_file, target_folder, filename;
3074 LPCWSTR buffer, extension;
3076 static const WCHAR szlnk[]={'.','l','n','k',0};
3077 IShellLinkW *sl = NULL;
3078 IPersistFile *pf = NULL;
3081 buffer = MSI_RecordGetString(row,4);
3082 comp = get_loaded_component(package,buffer);
3084 return ERROR_SUCCESS;
3086 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3088 TRACE("Skipping shortcut creation due to disabled component\n");
3090 comp->Action = comp->Installed;
3092 return ERROR_SUCCESS;
3095 comp->Action = INSTALLSTATE_LOCAL;
3097 ui_actiondata(package,szCreateShortcuts,row);
3099 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3100 &IID_IShellLinkW, (LPVOID *) &sl );
3104 ERR("CLSID_ShellLink not available\n");
3108 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3111 ERR("QueryInterface(IID_IPersistFile) failed\n");
3115 buffer = MSI_RecordGetString(row,2);
3116 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3118 /* may be needed because of a bug somehwere else */
3119 create_full_pathW(target_folder);
3121 filename = msi_dup_record_field( row, 3 );
3122 reduce_to_longfilename(filename);
3124 extension = strchrW(filename,'.');
3125 if (!extension || strcmpiW(extension,szlnk))
3127 int len = strlenW(filename);
3128 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3129 memcpy(filename + len, szlnk, sizeof(szlnk));
3131 target_file = build_directory_name(2, target_folder, filename);
3132 msi_free(target_folder);
3135 buffer = MSI_RecordGetString(row,5);
3136 if (strchrW(buffer,'['))
3139 deformat_string(package,buffer,&deformated);
3140 IShellLinkW_SetPath(sl,deformated);
3141 msi_free(deformated);
3145 FIXME("poorly handled shortcut format, advertised shortcut\n");
3146 IShellLinkW_SetPath(sl,comp->FullKeypath);
3149 if (!MSI_RecordIsNull(row,6))
3152 buffer = MSI_RecordGetString(row,6);
3153 deformat_string(package,buffer,&deformated);
3154 IShellLinkW_SetArguments(sl,deformated);
3155 msi_free(deformated);
3158 if (!MSI_RecordIsNull(row,7))
3160 buffer = MSI_RecordGetString(row,7);
3161 IShellLinkW_SetDescription(sl,buffer);
3164 if (!MSI_RecordIsNull(row,8))
3165 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3167 if (!MSI_RecordIsNull(row,9))
3172 buffer = MSI_RecordGetString(row,9);
3174 Path = build_icon_path(package,buffer);
3175 index = MSI_RecordGetInteger(row,10);
3177 /* no value means 0 */
3178 if (index == MSI_NULL_INTEGER)
3181 IShellLinkW_SetIconLocation(sl,Path,index);
3185 if (!MSI_RecordIsNull(row,11))
3186 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3188 if (!MSI_RecordIsNull(row,12))
3191 buffer = MSI_RecordGetString(row,12);
3192 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3194 IShellLinkW_SetWorkingDirectory(sl,Path);
3198 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3199 IPersistFile_Save(pf,target_file,FALSE);
3201 msi_free(target_file);
3205 IPersistFile_Release( pf );
3207 IShellLinkW_Release( sl );
3209 return ERROR_SUCCESS;
3212 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3217 static const WCHAR Query[] =
3218 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3219 '`','S','h','o','r','t','c','u','t','`',0};
3221 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3222 if (rc != ERROR_SUCCESS)
3223 return ERROR_SUCCESS;
3225 res = CoInitialize( NULL );
3228 ERR("CoInitialize failed\n");
3229 return ERROR_FUNCTION_FAILED;
3232 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3233 msiobj_release(&view->hdr);
3240 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3242 MSIPACKAGE* package = (MSIPACKAGE*)param;
3251 FileName = MSI_RecordGetString(row,1);
3254 ERR("Unable to get FileName\n");
3255 return ERROR_SUCCESS;
3258 FilePath = build_icon_path(package,FileName);
3260 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3262 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3263 FILE_ATTRIBUTE_NORMAL, NULL);
3265 if (the_file == INVALID_HANDLE_VALUE)
3267 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3269 return ERROR_SUCCESS;
3276 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3277 if (rc != ERROR_SUCCESS)
3279 ERR("Failed to get stream\n");
3280 CloseHandle(the_file);
3281 DeleteFileW(FilePath);
3284 WriteFile(the_file,buffer,sz,&write,NULL);
3285 } while (sz == 1024);
3289 CloseHandle(the_file);
3291 uirow = MSI_CreateRecord(1);
3292 MSI_RecordSetStringW(uirow,1,FileName);
3293 ui_actiondata(package,szPublishProduct,uirow);
3294 msiobj_release( &uirow->hdr );
3296 return ERROR_SUCCESS;
3299 static BOOL msi_check_publish(MSIPACKAGE *package)
3301 MSIFEATURE *feature;
3303 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3305 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3313 * 99% of the work done here is only done for
3314 * advertised installs. However this is where the
3315 * Icon table is processed and written out
3316 * so that is what I am going to do here.
3318 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3322 MSISOURCELISTINFO *info;
3324 static const WCHAR Query[]=
3325 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3326 '`','I','c','o','n','`',0};
3327 /* for registry stuff */
3330 HKEY hudkey=0, props=0;
3331 static const WCHAR szProductLanguage[] =
3332 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3333 static const WCHAR szARPProductIcon[] =
3334 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3335 static const WCHAR szProductVersion[] =
3336 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3340 MSIHANDLE hDb, hSumInfo;
3342 /* FIXME: also need to publish if the product is in advertise mode */
3343 if (!msi_check_publish(package))
3344 return ERROR_SUCCESS;
3346 /* write out icon files */
3348 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3349 if (rc == ERROR_SUCCESS)
3351 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3352 msiobj_release(&view->hdr);
3355 /* ok there is a lot more done here but i need to figure out what */
3357 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3358 if (rc != ERROR_SUCCESS)
3361 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3362 if (rc != ERROR_SUCCESS)
3365 rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3366 if (rc != ERROR_SUCCESS)
3369 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode,&props,TRUE);
3370 if (rc != ERROR_SUCCESS)
3373 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3374 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3377 langid = msi_get_property_int( package, szProductLanguage, 0 );
3378 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3380 buffer = msi_dup_property( package, szARPProductIcon );
3383 LPWSTR path = build_icon_path(package,buffer);
3384 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3389 buffer = msi_dup_property( package, szProductVersion );
3392 DWORD verdword = msi_version_str_to_dword(buffer);
3393 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3397 /* FIXME: Need to write more keys to the user registry */
3399 hDb= alloc_msihandle( &package->db->hdr );
3401 rc = ERROR_NOT_ENOUGH_MEMORY;
3404 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3405 MsiCloseHandle(hDb);
3406 if (rc == ERROR_SUCCESS)
3408 WCHAR guidbuffer[0x200];
3410 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3412 if (rc == ERROR_SUCCESS)
3414 WCHAR squashed[GUID_SIZE];
3415 /* for now we only care about the first guid */
3416 LPWSTR ptr = strchrW(guidbuffer,';');
3418 squash_guid(guidbuffer,squashed);
3419 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3423 ERR("Unable to query Revision_Number...\n");
3426 MsiCloseHandle(hSumInfo);
3430 ERR("Unable to open Summary Information\n");
3434 /* publish the SourceList info */
3435 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3437 MsiSourceListSetInfoW(package->ProductCode, NULL,
3438 info->context, info->options,
3439 info->property, info->value);
3442 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3444 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3445 disk->context, disk->options,
3446 disk->disk_id, disk->volume_label, disk->disk_prompt);
3452 RegCloseKey(hudkey);
3458 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3460 MSIPACKAGE *package = (MSIPACKAGE*)param;
3461 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3462 LPWSTR deformated_section, deformated_key, deformated_value;
3463 LPWSTR folder, fullname = NULL;
3467 static const WCHAR szWindowsFolder[] =
3468 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3470 component = MSI_RecordGetString(row, 8);
3471 comp = get_loaded_component(package,component);
3473 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3475 TRACE("Skipping ini file due to disabled component %s\n",
3476 debugstr_w(component));
3478 comp->Action = comp->Installed;
3480 return ERROR_SUCCESS;
3483 comp->Action = INSTALLSTATE_LOCAL;
3485 identifier = MSI_RecordGetString(row,1);
3486 filename = MSI_RecordGetString(row,2);
3487 dirproperty = MSI_RecordGetString(row,3);
3488 section = MSI_RecordGetString(row,4);
3489 key = MSI_RecordGetString(row,5);
3490 value = MSI_RecordGetString(row,6);
3491 action = MSI_RecordGetInteger(row,7);
3493 deformat_string(package,section,&deformated_section);
3494 deformat_string(package,key,&deformated_key);
3495 deformat_string(package,value,&deformated_value);
3499 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3501 folder = msi_dup_property( package, dirproperty );
3504 folder = msi_dup_property( package, szWindowsFolder );
3508 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3512 fullname = build_directory_name(2, folder, filename);
3516 TRACE("Adding value %s to section %s in %s\n",
3517 debugstr_w(deformated_key), debugstr_w(deformated_section),
3518 debugstr_w(fullname));
3519 WritePrivateProfileStringW(deformated_section, deformated_key,
3520 deformated_value, fullname);
3522 else if (action == 1)
3525 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3526 returned, 10, fullname);
3527 if (returned[0] == 0)
3529 TRACE("Adding value %s to section %s in %s\n",
3530 debugstr_w(deformated_key), debugstr_w(deformated_section),
3531 debugstr_w(fullname));
3533 WritePrivateProfileStringW(deformated_section, deformated_key,
3534 deformated_value, fullname);
3537 else if (action == 3)
3538 FIXME("Append to existing section not yet implemented\n");
3540 uirow = MSI_CreateRecord(4);
3541 MSI_RecordSetStringW(uirow,1,identifier);
3542 MSI_RecordSetStringW(uirow,2,deformated_section);
3543 MSI_RecordSetStringW(uirow,3,deformated_key);
3544 MSI_RecordSetStringW(uirow,4,deformated_value);
3545 ui_actiondata(package,szWriteIniValues,uirow);
3546 msiobj_release( &uirow->hdr );
3550 msi_free(deformated_key);
3551 msi_free(deformated_value);
3552 msi_free(deformated_section);
3553 return ERROR_SUCCESS;
3556 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3560 static const WCHAR ExecSeqQuery[] =
3561 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3562 '`','I','n','i','F','i','l','e','`',0};
3564 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3565 if (rc != ERROR_SUCCESS)
3567 TRACE("no IniFile table\n");
3568 return ERROR_SUCCESS;
3571 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3572 msiobj_release(&view->hdr);
3576 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3578 MSIPACKAGE *package = (MSIPACKAGE*)param;
3583 static const WCHAR ExeStr[] =
3584 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3585 static const WCHAR close[] = {'\"',0};
3587 PROCESS_INFORMATION info;
3592 memset(&si,0,sizeof(STARTUPINFOW));
3594 filename = MSI_RecordGetString(row,1);
3595 file = get_loaded_file( package, filename );
3599 ERR("Unable to find file id %s\n",debugstr_w(filename));
3600 return ERROR_SUCCESS;
3603 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3605 FullName = msi_alloc(len*sizeof(WCHAR));
3606 strcpyW(FullName,ExeStr);
3607 strcatW( FullName, file->TargetPath );
3608 strcatW(FullName,close);
3610 TRACE("Registering %s\n",debugstr_w(FullName));
3611 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3615 msi_dialog_check_messages(info.hProcess);
3620 uirow = MSI_CreateRecord( 2 );
3621 uipath = strdupW( file->TargetPath );
3622 p = strrchrW(uipath,'\\');
3625 MSI_RecordSetStringW( uirow, 1, &p[1] );
3626 MSI_RecordSetStringW( uirow, 2, uipath);
3627 ui_actiondata( package, szSelfRegModules, uirow);
3628 msiobj_release( &uirow->hdr );
3630 /* FIXME: call ui_progress? */
3632 return ERROR_SUCCESS;
3635 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3639 static const WCHAR ExecSeqQuery[] =
3640 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3641 '`','S','e','l','f','R','e','g','`',0};
3643 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3644 if (rc != ERROR_SUCCESS)
3646 TRACE("no SelfReg table\n");
3647 return ERROR_SUCCESS;
3650 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3651 msiobj_release(&view->hdr);
3653 return ERROR_SUCCESS;
3656 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3658 MSIFEATURE *feature;
3664 if (!msi_check_publish(package))
3665 return ERROR_SUCCESS;
3667 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3668 if (rc != ERROR_SUCCESS)
3671 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3672 if (rc != ERROR_SUCCESS)
3675 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &userdata, TRUE);
3676 if (rc != ERROR_SUCCESS)
3679 /* here the guids are base 85 encoded */
3680 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3686 BOOL absent = FALSE;
3689 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3690 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3691 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3695 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3699 if (feature->Feature_Parent)
3700 size += strlenW( feature->Feature_Parent )+2;
3702 data = msi_alloc(size * sizeof(WCHAR));
3705 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3707 MSICOMPONENT* component = cl->component;
3711 if (component->ComponentId)
3713 TRACE("From %s\n",debugstr_w(component->ComponentId));
3714 CLSIDFromString(component->ComponentId, &clsid);
3715 encode_base85_guid(&clsid,buf);
3716 TRACE("to %s\n",debugstr_w(buf));
3721 if (feature->Feature_Parent)
3723 static const WCHAR sep[] = {'\2',0};
3725 strcatW(data,feature->Feature_Parent);
3728 msi_reg_set_val_str( hkey, feature->Feature, data );
3729 msi_reg_set_val_str( userdata, feature->Feature, data );
3733 if (feature->Feature_Parent)
3734 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3737 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3738 (LPBYTE)feature->Feature_Parent,size);
3742 size += 2*sizeof(WCHAR);
3743 data = msi_alloc(size);
3746 if (feature->Feature_Parent)
3747 strcpyW( &data[1], feature->Feature_Parent );
3748 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3754 uirow = MSI_CreateRecord( 1 );
3755 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3756 ui_actiondata( package, szPublishFeatures, uirow);
3757 msiobj_release( &uirow->hdr );
3758 /* FIXME: call ui_progress? */
3767 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3772 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3774 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3775 if (r == ERROR_SUCCESS)
3777 RegDeleteValueW(hkey, feature->Feature);
3781 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3782 if (r == ERROR_SUCCESS)
3784 RegDeleteValueW(hkey, feature->Feature);
3788 return ERROR_SUCCESS;
3791 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3793 MSIFEATURE *feature;
3795 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3797 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3804 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3806 MSIFEATURE *feature;
3808 if (!msi_check_unpublish(package))
3809 return ERROR_SUCCESS;
3811 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3813 msi_unpublish_feature(package, feature);
3816 return ERROR_SUCCESS;
3819 static UINT msi_get_local_package_name( LPWSTR path )
3821 static const WCHAR szInstaller[] = {
3822 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3823 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3827 time = GetTickCount();
3828 GetWindowsDirectoryW( path, MAX_PATH );
3829 lstrcatW( path, szInstaller );
3830 CreateDirectoryW( path, NULL );
3832 len = lstrlenW(path);
3833 for (i=0; i<0x10000; i++)
3835 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3836 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3837 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3838 if (handle != INVALID_HANDLE_VALUE)
3840 CloseHandle(handle);
3843 if (GetLastError() != ERROR_FILE_EXISTS &&
3844 GetLastError() != ERROR_SHARING_VIOLATION)
3845 return ERROR_FUNCTION_FAILED;
3848 return ERROR_SUCCESS;
3851 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3853 static const WCHAR szOriginalDatabase[] =
3854 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3855 WCHAR packagefile[MAX_PATH];
3860 r = msi_get_local_package_name( packagefile );
3861 if (r != ERROR_SUCCESS)
3864 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3866 msiFilePath = msi_dup_property( package, szOriginalDatabase );
3867 r = CopyFileW( msiFilePath, packagefile, FALSE);
3871 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3872 debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3873 msi_free( msiFilePath );
3874 return ERROR_FUNCTION_FAILED;
3876 msi_free( msiFilePath );
3878 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3880 r = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3881 if (r != ERROR_SUCCESS)
3884 msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3886 return ERROR_SUCCESS;
3889 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3891 LPWSTR prop, val, key;
3892 static const LPCSTR propval[] = {
3893 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3894 "ARPCONTACT", "Contact",
3895 "ARPCOMMENTS", "Comments",
3896 "ProductName", "DisplayName",
3897 "ProductVersion", "DisplayVersion",
3898 "ARPHELPLINK", "HelpLink",
3899 "ARPHELPTELEPHONE", "HelpTelephone",
3900 "ARPINSTALLLOCATION", "InstallLocation",
3901 "SourceDir", "InstallSource",
3902 "Manufacturer", "Publisher",
3903 "ARPREADME", "Readme",
3905 "ARPURLINFOABOUT", "URLInfoAbout",
3906 "ARPURLUPDATEINFO", "URLUpdateInfo",
3909 const LPCSTR *p = propval;
3913 prop = strdupAtoW( *p++ );
3914 key = strdupAtoW( *p++ );
3915 val = msi_dup_property( package, prop );
3916 msi_reg_set_val_str( hkey, key, val );
3921 return ERROR_SUCCESS;
3924 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3927 HKEY hudkey=0, props=0;
3928 LPWSTR buffer = NULL;
3931 static const WCHAR szWindowsInstaller[] =
3932 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3933 static const WCHAR szUpgradeCode[] =
3934 {'U','p','g','r','a','d','e','C','o','d','e',0};
3935 static const WCHAR modpath_fmt[] =
3936 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3937 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3938 static const WCHAR szModifyPath[] =
3939 {'M','o','d','i','f','y','P','a','t','h',0};
3940 static const WCHAR szUninstallString[] =
3941 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3942 static const WCHAR szEstimatedSize[] =
3943 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3944 static const WCHAR szProductLanguage[] =
3945 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3946 static const WCHAR szProductVersion[] =
3947 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3950 static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3951 LPWSTR upgrade_code;
3954 /* FIXME: also need to publish if the product is in advertise mode */
3955 if (!msi_check_publish(package))
3956 return ERROR_SUCCESS;
3958 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3959 if (rc != ERROR_SUCCESS)
3962 /* dump all the info i can grab */
3963 /* FIXME: Flesh out more information */
3965 msi_write_uninstall_property_vals( package, hkey );
3967 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3969 msi_make_package_local( package, hkey );
3971 /* do ModifyPath and UninstallString */
3972 size = deformat_string(package,modpath_fmt,&buffer);
3973 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3974 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3977 /* FIXME: Write real Estimated Size when we have it */
3978 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3980 GetLocalTime(&systime);
3981 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3982 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3984 langid = msi_get_property_int( package, szProductLanguage, 0 );
3985 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3987 buffer = msi_dup_property( package, szProductVersion );
3990 DWORD verdword = msi_version_str_to_dword(buffer);
3992 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3993 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3994 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3998 /* Handle Upgrade Codes */
3999 upgrade_code = msi_dup_property( package, szUpgradeCode );
4004 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4005 squash_guid(package->ProductCode,squashed);
4006 msi_reg_set_val_str( hkey2, squashed, NULL );
4008 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4009 squash_guid(package->ProductCode,squashed);
4010 msi_reg_set_val_str( hkey2, squashed, NULL );
4013 msi_free(upgrade_code);
4018 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
4019 if (rc != ERROR_SUCCESS)
4022 RegCloseKey(hudkey);
4024 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
4025 if (rc != ERROR_SUCCESS)
4028 msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
4031 return ERROR_SUCCESS;
4034 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4036 return execute_script(package,INSTALL_SCRIPT);
4039 static UINT msi_unpublish_product(MSIPACKAGE *package)
4041 LPWSTR remove = NULL;
4042 LPWSTR *features = NULL;
4043 BOOL full_uninstall = TRUE;
4044 MSIFEATURE *feature;
4046 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4047 static const WCHAR szAll[] = {'A','L','L',0};
4049 remove = msi_dup_property(package, szRemove);
4051 return ERROR_SUCCESS;
4053 features = msi_split_string(remove, ',');
4057 ERR("REMOVE feature list is empty!\n");
4058 return ERROR_FUNCTION_FAILED;
4061 if (!lstrcmpW(features[0], szAll))
4062 full_uninstall = TRUE;
4065 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4067 if (feature->Action != INSTALLSTATE_ABSENT)
4068 full_uninstall = FALSE;
4072 if (!full_uninstall)
4075 MSIREG_DeleteProductKey(package->ProductCode);
4076 MSIREG_DeleteUserProductKey(package->ProductCode);
4077 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4078 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4079 MSIREG_DeleteUninstallKey(package->ProductCode);
4084 return ERROR_SUCCESS;
4087 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4091 rc = msi_unpublish_product(package);
4092 if (rc != ERROR_SUCCESS)
4095 /* turn off scheduling */
4096 package->script->CurrentlyScripting= FALSE;
4098 /* first do the same as an InstallExecute */
4099 rc = ACTION_InstallExecute(package);
4100 if (rc != ERROR_SUCCESS)
4103 /* then handle Commit Actions */
4104 rc = execute_script(package,COMMIT_SCRIPT);
4109 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4111 static const WCHAR RunOnce[] = {
4112 'S','o','f','t','w','a','r','e','\\',
4113 'M','i','c','r','o','s','o','f','t','\\',
4114 'W','i','n','d','o','w','s','\\',
4115 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4116 'R','u','n','O','n','c','e',0};
4117 static const WCHAR InstallRunOnce[] = {
4118 'S','o','f','t','w','a','r','e','\\',
4119 'M','i','c','r','o','s','o','f','t','\\',
4120 'W','i','n','d','o','w','s','\\',
4121 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4122 'I','n','s','t','a','l','l','e','r','\\',
4123 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4125 static const WCHAR msiexec_fmt[] = {
4127 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4128 '\"','%','s','\"',0};
4129 static const WCHAR install_fmt[] = {
4130 '/','I',' ','\"','%','s','\"',' ',
4131 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4132 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4133 WCHAR buffer[256], sysdir[MAX_PATH];
4135 WCHAR squished_pc[100];
4137 squash_guid(package->ProductCode,squished_pc);
4139 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4140 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4141 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4144 msi_reg_set_val_str( hkey, squished_pc, buffer );
4147 TRACE("Reboot command %s\n",debugstr_w(buffer));
4149 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4150 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4152 msi_reg_set_val_str( hkey, squished_pc, buffer );
4155 return ERROR_INSTALL_SUSPEND;
4158 static UINT msi_set_sourcedir_props(MSIPACKAGE *package)
4163 p = strrchrW( package->PackagePath, '\\' );
4165 return ERROR_SUCCESS;
4167 len = p - package->PackagePath + 2;
4168 source = msi_alloc( len * sizeof(WCHAR) );
4169 lstrcpynW( source, package->PackagePath, len );
4171 MSI_SetPropertyW( package, cszSourceDir, source );
4172 MSI_SetPropertyW( package, cszSOURCEDIR, source );
4176 return ERROR_SUCCESS;
4179 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4185 * We are currently doing what should be done here in the top level Install
4186 * however for Administrative and uninstalls this step will be needed
4188 if (!package->PackagePath)
4189 return ERROR_SUCCESS;
4191 msi_set_sourcedir_props(package);
4193 attrib = GetFileAttributesW(package->PackagePath);
4194 if (attrib == INVALID_FILE_ATTRIBUTES)
4200 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4201 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4202 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4203 if (rc == ERROR_MORE_DATA)
4205 prompt = msi_alloc(size * sizeof(WCHAR));
4206 MsiSourceListGetInfoW(package->ProductCode, NULL,
4207 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4208 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4211 prompt = strdupW(package->PackagePath);
4213 msg = generate_error_string(package,1302,1,prompt);
4214 while(attrib == INVALID_FILE_ATTRIBUTES)
4216 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4219 rc = ERROR_INSTALL_USEREXIT;
4222 attrib = GetFileAttributesW(package->PackagePath);
4228 return ERROR_SUCCESS;
4233 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4240 static const WCHAR szPropKeys[][80] =
4242 {'P','r','o','d','u','c','t','I','D',0},
4243 {'U','S','E','R','N','A','M','E',0},
4244 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4248 static const WCHAR szRegKeys[][80] =
4250 {'P','r','o','d','u','c','t','I','D',0},
4251 {'R','e','g','O','w','n','e','r',0},
4252 {'R','e','g','C','o','m','p','a','n','y',0},
4256 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4258 return ERROR_SUCCESS;
4260 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4261 if (rc != ERROR_SUCCESS)
4264 for( i = 0; szPropKeys[i][0]; i++ )
4266 buffer = msi_dup_property( package, szPropKeys[i] );
4267 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4272 msi_free(productid);
4275 /* FIXME: call ui_actiondata */
4277 return ERROR_SUCCESS;
4281 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4285 package->script->InWhatSequence |= SEQUENCE_EXEC;
4286 rc = ACTION_ProcessExecSequence(package,FALSE);
4291 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4293 MSIPACKAGE *package = (MSIPACKAGE*)param;
4294 LPCWSTR compgroupid=NULL;
4295 LPCWSTR feature=NULL;
4296 LPCWSTR text = NULL;
4297 LPCWSTR qualifier = NULL;
4298 LPCWSTR component = NULL;
4299 LPWSTR advertise = NULL;
4300 LPWSTR output = NULL;
4302 UINT rc = ERROR_SUCCESS;
4307 component = MSI_RecordGetString(rec,3);
4308 comp = get_loaded_component(package,component);
4310 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4311 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4312 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4314 TRACE("Skipping: Component %s not scheduled for install\n",
4315 debugstr_w(component));
4317 return ERROR_SUCCESS;
4320 compgroupid = MSI_RecordGetString(rec,1);
4321 qualifier = MSI_RecordGetString(rec,2);
4323 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4324 if (rc != ERROR_SUCCESS)
4327 text = MSI_RecordGetString(rec,4);
4328 feature = MSI_RecordGetString(rec,5);
4330 advertise = create_component_advertise_string(package, comp, feature);
4332 sz = strlenW(advertise);
4335 sz += lstrlenW(text);
4338 sz *= sizeof(WCHAR);
4340 output = msi_alloc_zero(sz);
4341 strcpyW(output,advertise);
4342 msi_free(advertise);
4345 strcatW(output,text);
4347 msi_reg_set_val_multi_str( hkey, qualifier, output );
4354 uirow = MSI_CreateRecord( 2 );
4355 MSI_RecordSetStringW( uirow, 1, compgroupid );
4356 MSI_RecordSetStringW( uirow, 2, qualifier);
4357 ui_actiondata( package, szPublishComponents, uirow);
4358 msiobj_release( &uirow->hdr );
4359 /* FIXME: call ui_progress? */
4365 * At present I am ignorning the advertised components part of this and only
4366 * focusing on the qualified component sets
4368 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4372 static const WCHAR ExecSeqQuery[] =
4373 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4374 '`','P','u','b','l','i','s','h',
4375 'C','o','m','p','o','n','e','n','t','`',0};
4377 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4378 if (rc != ERROR_SUCCESS)
4379 return ERROR_SUCCESS;
4381 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4382 msiobj_release(&view->hdr);
4387 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4389 MSIPACKAGE *package = (MSIPACKAGE*)param;
4392 SC_HANDLE hscm, service = NULL;
4393 LPCWSTR name, disp, comp, depends, pass;
4394 LPCWSTR load_order, serv_name, key;
4395 DWORD serv_type, start_type;
4398 static const WCHAR query[] =
4399 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4400 '`','C','o','m','p','o','n','e','n','t','`',' ',
4401 'W','H','E','R','E',' ',
4402 '`','C','o','m','p','o','n','e','n','t','`',' ',
4403 '=','\'','%','s','\'',0};
4405 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4408 ERR("Failed to open the SC Manager!\n");
4412 start_type = MSI_RecordGetInteger(rec, 5);
4413 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4416 depends = MSI_RecordGetString(rec, 8);
4417 if (depends && *depends)
4418 FIXME("Dependency list unhandled!\n");
4420 name = MSI_RecordGetString(rec, 2);
4421 disp = MSI_RecordGetString(rec, 3);
4422 serv_type = MSI_RecordGetInteger(rec, 4);
4423 err_control = MSI_RecordGetInteger(rec, 6);
4424 load_order = MSI_RecordGetString(rec, 7);
4425 serv_name = MSI_RecordGetString(rec, 9);
4426 pass = MSI_RecordGetString(rec, 10);
4427 comp = MSI_RecordGetString(rec, 12);
4429 /* fetch the service path */
4430 row = MSI_QueryGetRecord(package->db, query, comp);
4433 ERR("Control query failed!\n");
4437 key = MSI_RecordGetString(row, 6);
4439 file = get_loaded_file(package, key);
4440 msiobj_release(&row->hdr);
4443 ERR("Failed to load the service file\n");
4447 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4448 start_type, err_control, file->TargetPath,
4449 load_order, NULL, NULL, serv_name, pass);
4452 if (GetLastError() != ERROR_SERVICE_EXISTS)
4453 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4457 CloseServiceHandle(service);
4458 CloseServiceHandle(hscm);
4460 return ERROR_SUCCESS;
4463 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4467 static const WCHAR ExecSeqQuery[] =
4468 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4469 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4471 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4472 if (rc != ERROR_SUCCESS)
4473 return ERROR_SUCCESS;
4475 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4476 msiobj_release(&view->hdr);
4481 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4482 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4488 static const WCHAR separator[] = {'[','~',']',0};
4491 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4496 vector = msi_alloc(sizeof(LPWSTR));
4504 vector[*numargs - 1] = p;
4506 if ((q = strstrW(p, separator)))
4510 vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4521 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4523 MSIPACKAGE *package = (MSIPACKAGE *)param;
4525 SC_HANDLE scm, service = NULL;
4526 LPCWSTR name, *vector = NULL;
4528 DWORD event, numargs;
4529 UINT r = ERROR_FUNCTION_FAILED;
4531 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4532 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4533 return ERROR_SUCCESS;
4535 name = MSI_RecordGetString(rec, 2);
4536 event = MSI_RecordGetInteger(rec, 3);
4537 args = strdupW(MSI_RecordGetString(rec, 4));
4539 if (!(event & msidbServiceControlEventStart))
4540 return ERROR_SUCCESS;
4542 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4545 ERR("Failed to open the service control manager\n");
4549 service = OpenServiceW(scm, name, SERVICE_START);
4552 ERR("Failed to open service %s\n", debugstr_w(name));
4556 vector = msi_service_args_to_vector(args, &numargs);
4558 if (!StartServiceW(service, numargs, vector))
4560 ERR("Failed to start service %s\n", debugstr_w(name));
4567 CloseServiceHandle(service);
4568 CloseServiceHandle(scm);
4575 static UINT ACTION_StartServices( MSIPACKAGE *package )
4580 static const WCHAR query[] = {
4581 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4582 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4584 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4585 if (rc != ERROR_SUCCESS)
4586 return ERROR_SUCCESS;
4588 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4589 msiobj_release(&view->hdr);
4594 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4598 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4600 if (!lstrcmpW(file->File, filename))
4607 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4609 MSIPACKAGE *package = (MSIPACKAGE*)param;
4610 LPWSTR driver, driver_path, ptr;
4611 WCHAR outpath[MAX_PATH];
4612 MSIFILE *driver_file, *setup_file;
4615 UINT r = ERROR_SUCCESS;
4617 static const WCHAR driver_fmt[] = {
4618 'D','r','i','v','e','r','=','%','s',0};
4619 static const WCHAR setup_fmt[] = {
4620 'S','e','t','u','p','=','%','s',0};
4621 static const WCHAR usage_fmt[] = {
4622 'F','i','l','e','U','s','a','g','e','=','1',0};
4624 desc = MSI_RecordGetString(rec, 3);
4626 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4627 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4629 if (!driver_file || !setup_file)
4631 ERR("ODBC Driver entry not found!\n");
4632 return ERROR_FUNCTION_FAILED;
4635 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4636 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4637 lstrlenW(usage_fmt) + 1;
4638 driver = msi_alloc(len * sizeof(WCHAR));
4640 return ERROR_OUTOFMEMORY;
4643 lstrcpyW(ptr, desc);
4644 ptr += lstrlenW(ptr) + 1;
4646 sprintfW(ptr, driver_fmt, driver_file->FileName);
4647 ptr += lstrlenW(ptr) + 1;
4649 sprintfW(ptr, setup_fmt, setup_file->FileName);
4650 ptr += lstrlenW(ptr) + 1;
4652 lstrcpyW(ptr, usage_fmt);
4653 ptr += lstrlenW(ptr) + 1;
4656 driver_path = strdupW(driver_file->TargetPath);
4657 ptr = strrchrW(driver_path, '\\');
4658 if (ptr) *ptr = '\0';
4660 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4661 NULL, ODBC_INSTALL_COMPLETE, &usage))
4663 ERR("Failed to install SQL driver!\n");
4664 r = ERROR_FUNCTION_FAILED;
4668 msi_free(driver_path);
4673 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4675 MSIPACKAGE *package = (MSIPACKAGE*)param;
4676 LPWSTR translator, translator_path, ptr;
4677 WCHAR outpath[MAX_PATH];
4678 MSIFILE *translator_file, *setup_file;
4681 UINT r = ERROR_SUCCESS;
4683 static const WCHAR translator_fmt[] = {
4684 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4685 static const WCHAR setup_fmt[] = {
4686 'S','e','t','u','p','=','%','s',0};
4688 desc = MSI_RecordGetString(rec, 3);
4690 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4691 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4693 if (!translator_file || !setup_file)
4695 ERR("ODBC Translator entry not found!\n");
4696 return ERROR_FUNCTION_FAILED;
4699 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4700 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4701 translator = msi_alloc(len * sizeof(WCHAR));
4703 return ERROR_OUTOFMEMORY;
4706 lstrcpyW(ptr, desc);
4707 ptr += lstrlenW(ptr) + 1;
4709 sprintfW(ptr, translator_fmt, translator_file->FileName);
4710 ptr += lstrlenW(ptr) + 1;
4712 sprintfW(ptr, setup_fmt, setup_file->FileName);
4713 ptr += lstrlenW(ptr) + 1;
4716 translator_path = strdupW(translator_file->TargetPath);
4717 ptr = strrchrW(translator_path, '\\');
4718 if (ptr) *ptr = '\0';
4720 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4721 NULL, ODBC_INSTALL_COMPLETE, &usage))
4723 ERR("Failed to install SQL translator!\n");
4724 r = ERROR_FUNCTION_FAILED;
4727 msi_free(translator);
4728 msi_free(translator_path);
4733 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4736 LPCWSTR desc, driver;
4737 WORD request = ODBC_ADD_SYS_DSN;
4740 UINT r = ERROR_SUCCESS;
4742 static const WCHAR attrs_fmt[] = {
4743 'D','S','N','=','%','s',0 };
4745 desc = MSI_RecordGetString(rec, 3);
4746 driver = MSI_RecordGetString(rec, 4);
4747 registration = MSI_RecordGetInteger(rec, 5);
4749 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4750 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4752 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4753 attrs = msi_alloc(len * sizeof(WCHAR));
4755 return ERROR_OUTOFMEMORY;
4757 sprintfW(attrs, attrs_fmt, desc);
4758 attrs[len - 1] = '\0';
4760 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4762 ERR("Failed to install SQL data source!\n");
4763 r = ERROR_FUNCTION_FAILED;
4771 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4776 static const WCHAR driver_query[] = {
4777 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4778 'O','D','B','C','D','r','i','v','e','r',0 };
4780 static const WCHAR translator_query[] = {
4781 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4782 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4784 static const WCHAR source_query[] = {
4785 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4786 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4788 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4789 if (rc != ERROR_SUCCESS)
4790 return ERROR_SUCCESS;
4792 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4793 msiobj_release(&view->hdr);
4795 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4796 if (rc != ERROR_SUCCESS)
4797 return ERROR_SUCCESS;
4799 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4800 msiobj_release(&view->hdr);
4802 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4803 if (rc != ERROR_SUCCESS)
4804 return ERROR_SUCCESS;
4806 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4807 msiobj_release(&view->hdr);
4812 #define ENV_ACT_SETALWAYS 0x1
4813 #define ENV_ACT_SETABSENT 0x2
4814 #define ENV_ACT_REMOVE 0x4
4815 #define ENV_ACT_REMOVEMATCH 0x8
4817 #define ENV_MOD_MACHINE 0x20000000
4818 #define ENV_MOD_APPEND 0x40000000
4819 #define ENV_MOD_PREFIX 0x80000000
4820 #define ENV_MOD_MASK 0xC0000000
4822 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4824 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4826 LPCWSTR cptr = *name;
4827 LPCWSTR ptr = *value;
4829 static const WCHAR prefix[] = {'[','~',']',0};
4830 static const int prefix_len = 3;
4836 *flags |= ENV_ACT_SETALWAYS;
4837 else if (*cptr == '+')
4838 *flags |= ENV_ACT_SETABSENT;
4839 else if (*cptr == '-')
4840 *flags |= ENV_ACT_REMOVE;
4841 else if (*cptr == '!')
4842 *flags |= ENV_ACT_REMOVEMATCH;
4843 else if (*cptr == '*')
4844 *flags |= ENV_MOD_MACHINE;
4854 ERR("Missing environment variable\n");
4855 return ERROR_FUNCTION_FAILED;
4858 if (!strncmpW(ptr, prefix, prefix_len))
4860 *flags |= ENV_MOD_APPEND;
4861 *value += lstrlenW(prefix);
4863 else if (lstrlenW(*value) >= prefix_len)
4865 ptr += lstrlenW(ptr) - prefix_len;
4866 if (!lstrcmpW(ptr, prefix))
4868 *flags |= ENV_MOD_PREFIX;
4869 /* the "[~]" will be removed by deformat_string */;
4874 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4875 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4876 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4877 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4879 ERR("Invalid flags: %08x\n", *flags);
4880 return ERROR_FUNCTION_FAILED;
4883 return ERROR_SUCCESS;
4886 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4888 MSIPACKAGE *package = param;
4889 LPCWSTR name, value, comp;
4890 LPWSTR data = NULL, newval = NULL;
4891 LPWSTR deformatted = NULL, ptr;
4892 DWORD flags, type, size;
4894 HKEY env = NULL, root;
4895 LPCWSTR environment;
4897 static const WCHAR user_env[] =
4898 {'E','n','v','i','r','o','n','m','e','n','t',0};
4899 static const WCHAR machine_env[] =
4900 {'S','y','s','t','e','m','\\',
4901 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4902 'C','o','n','t','r','o','l','\\',
4903 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4904 'E','n','v','i','r','o','n','m','e','n','t',0};
4905 static const WCHAR semicolon[] = {';',0};
4907 name = MSI_RecordGetString(rec, 2);
4908 value = MSI_RecordGetString(rec, 3);
4909 comp = MSI_RecordGetString(rec, 4);
4911 res = env_set_flags(&name, &value, &flags);
4912 if (res != ERROR_SUCCESS)
4915 deformat_string(package, value, &deformatted);
4918 res = ERROR_OUTOFMEMORY;
4922 value = deformatted;
4924 if (flags & ENV_MOD_MACHINE)
4926 environment = machine_env;
4927 root = HKEY_LOCAL_MACHINE;
4931 environment = user_env;
4932 root = HKEY_CURRENT_USER;
4935 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
4936 KEY_ALL_ACCESS, NULL, &env, NULL);
4937 if (res != ERROR_SUCCESS)
4940 if (flags & ENV_ACT_REMOVE)
4941 FIXME("Not removing environment variable on uninstall!\n");
4944 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
4945 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
4946 (res == ERROR_SUCCESS && type != REG_SZ))
4949 if (res != ERROR_FILE_NOT_FOUND)
4951 if (flags & ENV_ACT_SETABSENT)
4953 res = ERROR_SUCCESS;
4957 data = msi_alloc(size);
4961 return ERROR_OUTOFMEMORY;
4964 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
4965 if (res != ERROR_SUCCESS)
4968 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
4970 res = RegDeleteKeyW(env, name);
4974 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
4975 newval = msi_alloc(size);
4979 res = ERROR_OUTOFMEMORY;
4983 if (!(flags & ENV_MOD_MASK))
4984 lstrcpyW(newval, value);
4987 if (flags & ENV_MOD_PREFIX)
4989 lstrcpyW(newval, value);
4990 lstrcatW(newval, semicolon);
4991 ptr = newval + lstrlenW(value) + 1;
4994 lstrcpyW(ptr, data);
4996 if (flags & ENV_MOD_APPEND)
4998 lstrcatW(newval, semicolon);
4999 lstrcatW(newval, value);
5005 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5006 newval = msi_alloc(size);
5009 res = ERROR_OUTOFMEMORY;
5013 lstrcpyW(newval, value);
5016 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5017 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5020 if (env) RegCloseKey(env);
5021 msi_free(deformatted);
5027 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5031 static const WCHAR ExecSeqQuery[] =
5032 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5033 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5034 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5035 if (rc != ERROR_SUCCESS)
5036 return ERROR_SUCCESS;
5038 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5039 msiobj_release(&view->hdr);
5044 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5055 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5059 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5060 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5062 WARN("Source or dest is directory, not moving\n");
5066 if (options == msidbMoveFileOptionsMove)
5068 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5069 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5072 WARN("MoveFile failed: %d\n", GetLastError());
5078 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5079 ret = CopyFileW(source, dest, FALSE);
5082 WARN("CopyFile failed: %d\n", GetLastError());
5090 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5093 DWORD dirlen, pathlen;
5095 ptr = strrchrW(wildcard, '\\');
5096 dirlen = ptr - wildcard + 1;
5098 pathlen = dirlen + lstrlenW(filename) + 1;
5099 path = msi_alloc(pathlen * sizeof(WCHAR));
5101 lstrcpynW(path, wildcard, dirlen + 1);
5102 lstrcatW(path, filename);
5107 static void free_file_entry(FILE_LIST *file)
5109 msi_free(file->source);
5110 msi_free(file->dest);
5114 static void free_list(FILE_LIST *list)
5116 while (!list_empty(&list->entry))
5118 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5120 list_remove(&file->entry);
5121 free_file_entry(file);
5125 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5127 FILE_LIST *new, *file;
5128 LPWSTR ptr, filename;
5131 new = msi_alloc_zero(sizeof(FILE_LIST));
5135 new->source = strdupW(source);
5136 ptr = strrchrW(dest, '\\') + 1;
5137 filename = strrchrW(new->source, '\\') + 1;
5139 new->sourcename = filename;
5142 new->destname = ptr;
5144 new->destname = new->sourcename;
5146 size = (ptr - dest) + lstrlenW(filename) + 1;
5147 new->dest = msi_alloc(size * sizeof(WCHAR));
5150 free_file_entry(new);
5154 lstrcpynW(new->dest, dest, ptr - dest + 1);
5155 lstrcatW(new->dest, filename);
5157 if (list_empty(&files->entry))
5159 list_add_head(&files->entry, &new->entry);
5163 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5165 if (lstrcmpW(source, file->source) < 0)
5167 list_add_before(&file->entry, &new->entry);
5172 list_add_after(&file->entry, &new->entry);
5176 BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5178 WIN32_FIND_DATAW wfd;
5182 FILE_LIST files, *file;
5185 hfile = FindFirstFileW(source, &wfd);
5186 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5188 list_init(&files.entry);
5190 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5192 if (is_dot_dir(wfd.cFileName)) continue;
5194 path = wildcard_to_file(source, wfd.cFileName);
5201 add_wildcard(&files, path, dest);
5205 /* only the first wildcard match gets renamed to dest */
5206 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5207 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5208 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5215 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5217 while (!list_empty(&files.entry))
5219 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5221 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5223 list_remove(&file->entry);
5224 free_file_entry(file);
5230 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5232 MSIPACKAGE *package = param;
5234 LPCWSTR sourcename, destname;
5235 LPWSTR sourcedir = NULL, destdir = NULL;
5236 LPWSTR source = NULL, dest = NULL;
5239 BOOL ret, wildcards;
5241 static const WCHAR backslash[] = {'\\',0};
5243 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5244 if (!comp || !comp->Enabled ||
5245 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5247 TRACE("Component not set for install, not moving file\n");
5248 return ERROR_SUCCESS;
5251 sourcename = MSI_RecordGetString(rec, 3);
5252 destname = MSI_RecordGetString(rec, 4);
5253 options = MSI_RecordGetInteger(rec, 7);
5255 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5259 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5265 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5268 source = strdupW(sourcedir);
5274 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5275 source = msi_alloc(size * sizeof(WCHAR));
5279 lstrcpyW(source, sourcedir);
5280 if (source[lstrlenW(source) - 1] != '\\')
5281 lstrcatW(source, backslash);
5282 lstrcatW(source, sourcename);
5285 wildcards = strchrW(source, '*') || strchrW(source, '?');
5287 if (!destname && !wildcards)
5289 destname = strdupW(sourcename);
5296 size = lstrlenW(destname);
5298 size += lstrlenW(destdir) + 2;
5299 dest = msi_alloc(size * sizeof(WCHAR));
5303 lstrcpyW(dest, destdir);
5304 if (dest[lstrlenW(dest) - 1] != '\\')
5305 lstrcatW(dest, backslash);
5308 lstrcatW(dest, destname);
5310 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5312 ret = CreateDirectoryW(destdir, NULL);
5315 WARN("CreateDirectory failed: %d\n", GetLastError());
5316 return ERROR_SUCCESS;
5321 msi_move_file(source, dest, options);
5323 move_files_wildcard(source, dest, options);
5326 msi_free(sourcedir);
5331 return ERROR_SUCCESS;
5334 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5339 static const WCHAR ExecSeqQuery[] =
5340 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5341 '`','M','o','v','e','F','i','l','e','`',0};
5343 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5344 if (rc != ERROR_SUCCESS)
5345 return ERROR_SUCCESS;
5347 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5348 msiobj_release(&view->hdr);
5353 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5354 LPCSTR action, LPCWSTR table )
5356 static const WCHAR query[] = {
5357 'S','E','L','E','C','T',' ','*',' ',
5358 'F','R','O','M',' ','`','%','s','`',0 };
5359 MSIQUERY *view = NULL;
5363 r = MSI_OpenQuery( package->db, &view, query, table );
5364 if (r == ERROR_SUCCESS)
5366 r = MSI_IterateRecords(view, &count, NULL, package);
5367 msiobj_release(&view->hdr);
5371 FIXME("%s -> %u ignored %s table values\n",
5372 action, count, debugstr_w(table));
5374 return ERROR_SUCCESS;
5377 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5379 TRACE("%p\n", package);
5380 return ERROR_SUCCESS;
5383 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5385 static const WCHAR table[] =
5386 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5387 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5390 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5392 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5393 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5396 static UINT ACTION_BindImage( MSIPACKAGE *package )
5398 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5399 return msi_unimplemented_action_stub( package, "BindImage", table );
5402 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5404 static const WCHAR table[] = {
5405 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5406 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5409 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5411 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5412 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5415 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5417 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5418 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5421 static UINT ACTION_StopServices( MSIPACKAGE *package )
5423 static const WCHAR table[] = {
5424 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5425 return msi_unimplemented_action_stub( package, "StopServices", table );
5428 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5430 static const WCHAR table[] = {
5431 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5432 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5434 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5436 static const WCHAR table[] = {
5437 'P','r','o','d','u','c','t','I','D',0 };
5438 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5441 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5443 static const WCHAR table[] = {
5444 'E','n','v','i','r','o','n','m','e','n','t',0 };
5445 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5448 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5450 static const WCHAR table[] = {
5451 'M','s','i','A','s','s','e','m','b','l','y',0 };
5452 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5455 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5457 static const WCHAR table[] = {
5458 'M','s','i','A','s','s','e','m','b','l','y',0 };
5459 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5462 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5464 static const WCHAR table[] = { 'F','o','n','t',0 };
5465 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5468 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5470 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5471 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5474 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5476 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5477 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5480 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5482 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5483 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5486 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5488 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5489 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5492 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5494 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5495 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5498 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5500 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5501 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5504 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5506 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5507 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5510 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5512 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5513 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5516 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5518 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5519 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5522 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5524 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5525 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5528 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5530 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5531 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5534 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5536 static const WCHAR table[] = { 'A','p','p','I','d',0 };
5537 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5540 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5542 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5543 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5546 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5548 static const WCHAR table[] = { 'M','I','M','E',0 };
5549 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5552 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5554 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5555 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5558 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5560 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5561 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5564 static const struct _actions StandardActions[] = {
5565 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5566 { szAppSearch, ACTION_AppSearch },
5567 { szBindImage, ACTION_BindImage },
5568 { szCCPSearch, ACTION_CCPSearch },
5569 { szCostFinalize, ACTION_CostFinalize },
5570 { szCostInitialize, ACTION_CostInitialize },
5571 { szCreateFolders, ACTION_CreateFolders },
5572 { szCreateShortcuts, ACTION_CreateShortcuts },
5573 { szDeleteServices, ACTION_DeleteServices },
5574 { szDisableRollback, NULL },
5575 { szDuplicateFiles, ACTION_DuplicateFiles },
5576 { szExecuteAction, ACTION_ExecuteAction },
5577 { szFileCost, ACTION_FileCost },
5578 { szFindRelatedProducts, ACTION_FindRelatedProducts },
5579 { szForceReboot, ACTION_ForceReboot },
5580 { szInstallAdminPackage, NULL },
5581 { szInstallExecute, ACTION_InstallExecute },
5582 { szInstallExecuteAgain, ACTION_InstallExecute },
5583 { szInstallFiles, ACTION_InstallFiles},
5584 { szInstallFinalize, ACTION_InstallFinalize },
5585 { szInstallInitialize, ACTION_InstallInitialize },
5586 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5587 { szInstallValidate, ACTION_InstallValidate },
5588 { szIsolateComponents, ACTION_IsolateComponents },
5589 { szLaunchConditions, ACTION_LaunchConditions },
5590 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5591 { szMoveFiles, ACTION_MoveFiles },
5592 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5593 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5594 { szInstallODBC, ACTION_InstallODBC },
5595 { szInstallServices, ACTION_InstallServices },
5596 { szPatchFiles, ACTION_PatchFiles },
5597 { szProcessComponents, ACTION_ProcessComponents },
5598 { szPublishComponents, ACTION_PublishComponents },
5599 { szPublishFeatures, ACTION_PublishFeatures },
5600 { szPublishProduct, ACTION_PublishProduct },
5601 { szRegisterClassInfo, ACTION_RegisterClassInfo },
5602 { szRegisterComPlus, ACTION_RegisterComPlus},
5603 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5604 { szRegisterFonts, ACTION_RegisterFonts },
5605 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5606 { szRegisterProduct, ACTION_RegisterProduct },
5607 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5608 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5609 { szRegisterUser, ACTION_RegisterUser },
5610 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5611 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5612 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5613 { szRemoveFiles, ACTION_RemoveFiles },
5614 { szRemoveFolders, ACTION_RemoveFolders },
5615 { szRemoveIniValues, ACTION_RemoveIniValues },
5616 { szRemoveODBC, ACTION_RemoveODBC },
5617 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5618 { szRemoveShortcuts, ACTION_RemoveShortcuts },
5619 { szResolveSource, ACTION_ResolveSource },
5620 { szRMCCPSearch, ACTION_RMCCPSearch },
5621 { szScheduleReboot, NULL },
5622 { szSelfRegModules, ACTION_SelfRegModules },
5623 { szSelfUnregModules, ACTION_SelfUnregModules },
5624 { szSetODBCFolders, NULL },
5625 { szStartServices, ACTION_StartServices },
5626 { szStopServices, ACTION_StopServices },
5627 { szUnpublishComponents, ACTION_UnpublishComponents },
5628 { szUnpublishFeatures, ACTION_UnpublishFeatures },
5629 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5630 { szUnregisterComPlus, ACTION_UnregisterComPlus },
5631 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5632 { szUnregisterFonts, ACTION_UnregisterFonts },
5633 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5634 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5635 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5636 { szValidateProductID, ACTION_ValidateProductID },
5637 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5638 { szWriteIniValues, ACTION_WriteIniValues },
5639 { szWriteRegistryValues, ACTION_WriteRegistryValues },