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 static 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 szProdID[] = { 'P','r','o','d','u','c','t','I','D',0 };
458 LPWSTR guid_list, *guids, product_id;
459 UINT i, ret = ERROR_FUNCTION_FAILED;
461 product_id = msi_dup_property( package, szProdID );
464 /* FIXME: the property ProductID should be written into the DB somewhere */
465 ERR("no product ID 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_id ))
477 msi_free( guid_list );
478 msi_free( product_id );
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 /****************************************************
588 * TOP level entry points
589 *****************************************************/
591 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
592 LPCWSTR szCommandLine )
596 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
597 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
598 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
600 MSI_SetPropertyW(package, szAction, szInstall);
602 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
604 package->script->InWhatSequence = SEQUENCE_INSTALL;
608 LPWSTR p, check, path;
610 path = strdupW(szPackagePath);
611 p = strrchrW(path,'\\');
620 path = msi_alloc(MAX_PATH*sizeof(WCHAR));
621 GetCurrentDirectoryW(MAX_PATH,path);
625 check = msi_dup_property( package, cszSourceDir );
627 MSI_SetPropertyW(package, cszSourceDir, path);
630 check = msi_dup_property( package, cszSOURCEDIR );
632 MSI_SetPropertyW(package, cszSOURCEDIR, path);
634 msi_free( package->PackagePath );
635 package->PackagePath = path;
640 msi_parse_command_line( package, szCommandLine );
642 msi_apply_transforms( package );
643 msi_apply_patches( package );
645 if ( msi_get_property_int(package, szUILevel, 0) >= INSTALLUILEVEL_REDUCED )
647 package->script->InWhatSequence |= SEQUENCE_UI;
648 rc = ACTION_ProcessUISequence(package);
650 if (rc == ERROR_SUCCESS)
652 package->script->InWhatSequence |= SEQUENCE_EXEC;
653 rc = ACTION_ProcessExecSequence(package,TRUE);
657 rc = ACTION_ProcessExecSequence(package,FALSE);
661 /* install was halted but should be considered a success */
665 package->script->CurrentlyScripting= FALSE;
667 /* process the ending type action */
668 if (rc == ERROR_SUCCESS)
669 ACTION_PerformActionSequence(package,-1,ui);
670 else if (rc == ERROR_INSTALL_USEREXIT)
671 ACTION_PerformActionSequence(package,-2,ui);
672 else if (rc == ERROR_INSTALL_SUSPEND)
673 ACTION_PerformActionSequence(package,-4,ui);
675 ACTION_PerformActionSequence(package,-3,ui);
677 /* finish up running custom actions */
678 ACTION_FinishCustomActions(package);
683 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
685 UINT rc = ERROR_SUCCESS;
687 static const WCHAR ExecSeqQuery[] =
688 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
689 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
690 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
691 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
693 static const WCHAR UISeqQuery[] =
694 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
695 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
696 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
697 ' ', '=',' ','%','i',0};
700 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
702 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
706 LPCWSTR action, cond;
708 TRACE("Running the actions\n");
710 /* check conditions */
711 cond = MSI_RecordGetString(row,2);
713 /* this is a hack to skip errors in the condition code */
714 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
717 action = MSI_RecordGetString(row,1);
720 ERR("failed to fetch action\n");
721 rc = ERROR_FUNCTION_FAILED;
726 rc = ACTION_PerformUIAction(package,action);
728 rc = ACTION_PerformAction(package,action,FALSE);
730 msiobj_release(&row->hdr);
741 } iterate_action_param;
743 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
745 iterate_action_param *iap= (iterate_action_param*)param;
747 LPCWSTR cond, action;
749 action = MSI_RecordGetString(row,1);
752 ERR("Error is retrieving action name\n");
753 return ERROR_FUNCTION_FAILED;
756 /* check conditions */
757 cond = MSI_RecordGetString(row,2);
759 /* this is a hack to skip errors in the condition code */
760 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
762 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
763 return ERROR_SUCCESS;
767 rc = ACTION_PerformUIAction(iap->package,action);
769 rc = ACTION_PerformAction(iap->package,action,FALSE);
771 msi_dialog_check_messages( NULL );
773 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
774 rc = iap->package->CurrentInstallState;
776 if (rc == ERROR_FUNCTION_NOT_CALLED)
779 if (rc != ERROR_SUCCESS)
780 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
785 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
789 static const WCHAR query[] =
790 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
792 ' ','W','H','E','R','E',' ',
793 '`','S','e','q','u','e','n','c','e','`',' ',
794 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
795 '`','S','e','q','u','e','n','c','e','`',0};
796 iterate_action_param iap;
799 * FIXME: probably should be checking UILevel in the
800 * ACTION_PerformUIAction/ACTION_PerformAction
801 * rather than saving the UI level here. Those
802 * two functions can be merged too.
804 iap.package = package;
807 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
809 r = MSI_OpenQuery( package->db, &view, query, szTable );
810 if (r == ERROR_SUCCESS)
812 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
813 msiobj_release(&view->hdr);
819 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
823 static const WCHAR ExecSeqQuery[] =
824 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
825 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
826 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
827 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
828 'O','R','D','E','R',' ', 'B','Y',' ',
829 '`','S','e','q','u','e','n','c','e','`',0 };
831 static const WCHAR IVQuery[] =
832 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
833 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
834 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
835 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
836 ' ','\'', 'I','n','s','t','a','l','l',
837 'V','a','l','i','d','a','t','e','\'', 0};
839 iterate_action_param iap;
841 iap.package = package;
844 if (package->script->ExecuteSequenceRun)
846 TRACE("Execute Sequence already Run\n");
847 return ERROR_SUCCESS;
850 package->script->ExecuteSequenceRun = TRUE;
852 /* get the sequence number */
855 row = MSI_QueryGetRecord(package->db, IVQuery);
857 return ERROR_FUNCTION_FAILED;
858 seq = MSI_RecordGetInteger(row,1);
859 msiobj_release(&row->hdr);
862 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
863 if (rc == ERROR_SUCCESS)
865 TRACE("Running the actions\n");
867 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
868 msiobj_release(&view->hdr);
874 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
878 static const WCHAR ExecSeqQuery [] =
879 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
880 '`','I','n','s','t','a','l','l',
881 'U','I','S','e','q','u','e','n','c','e','`',
882 ' ','W','H','E','R','E',' ',
883 '`','S','e','q','u','e','n','c','e','`',' ',
884 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
885 '`','S','e','q','u','e','n','c','e','`',0};
886 iterate_action_param iap;
888 iap.package = package;
891 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
893 if (rc == ERROR_SUCCESS)
895 TRACE("Running the actions\n");
897 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
898 msiobj_release(&view->hdr);
904 /********************************************************
905 * ACTION helper functions and functions that perform the actions
906 *******************************************************/
907 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
908 UINT* rc, BOOL force )
914 if (!run && !package->script->CurrentlyScripting)
919 if (strcmpW(action,szInstallFinalize) == 0 ||
920 strcmpW(action,szInstallExecute) == 0 ||
921 strcmpW(action,szInstallExecuteAgain) == 0)
926 while (StandardActions[i].action != NULL)
928 if (strcmpW(StandardActions[i].action, action)==0)
932 ui_actioninfo(package, action, TRUE, 0);
933 *rc = schedule_action(package,INSTALL_SCRIPT,action);
934 ui_actioninfo(package, action, FALSE, *rc);
938 ui_actionstart(package, action);
939 if (StandardActions[i].handler)
941 *rc = StandardActions[i].handler(package);
945 FIXME("unhandled standard action %s\n",debugstr_w(action));
957 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
958 UINT* rc, BOOL force )
963 arc = ACTION_CustomAction(package,action, force);
965 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
974 * A lot of actions are really important even if they don't do anything
975 * explicit... Lots of properties are set at the beginning of the installation
976 * CostFinalize does a bunch of work to translate the directories and such
978 * But until I get write access to the database that is hard, so I am going to
979 * hack it to see if I can get something to run.
981 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, BOOL force)
983 UINT rc = ERROR_SUCCESS;
986 TRACE("Performing action (%s)\n",debugstr_w(action));
988 handled = ACTION_HandleStandardAction(package, action, &rc, force);
991 handled = ACTION_HandleCustomAction(package, action, &rc, force);
995 FIXME("unhandled msi action %s\n",debugstr_w(action));
996 rc = ERROR_FUNCTION_NOT_CALLED;
1002 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action)
1004 UINT rc = ERROR_SUCCESS;
1005 BOOL handled = FALSE;
1007 TRACE("Performing action (%s)\n",debugstr_w(action));
1009 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1012 handled = ACTION_HandleCustomAction(package, action, &rc, FALSE);
1014 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1019 FIXME("unhandled msi action %s\n",debugstr_w(action));
1020 rc = ERROR_FUNCTION_NOT_CALLED;
1028 * Actual Action Handlers
1031 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1033 MSIPACKAGE *package = (MSIPACKAGE*)param;
1039 dir = MSI_RecordGetString(row,1);
1042 ERR("Unable to get folder id\n");
1043 return ERROR_SUCCESS;
1046 full_path = resolve_folder(package,dir,FALSE,FALSE,&folder);
1049 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1050 return ERROR_SUCCESS;
1053 TRACE("Folder is %s\n",debugstr_w(full_path));
1056 uirow = MSI_CreateRecord(1);
1057 MSI_RecordSetStringW(uirow,1,full_path);
1058 ui_actiondata(package,szCreateFolders,uirow);
1059 msiobj_release( &uirow->hdr );
1061 if (folder->State == 0)
1062 create_full_pathW(full_path);
1066 msi_free(full_path);
1067 return ERROR_SUCCESS;
1070 /* FIXME: probably should merge this with the above function */
1071 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1073 UINT rc = ERROR_SUCCESS;
1075 LPWSTR install_path;
1077 install_path = resolve_folder(package, dir, FALSE, FALSE, &folder);
1079 return ERROR_FUNCTION_FAILED;
1081 /* create the path */
1082 if (folder->State == 0)
1084 create_full_pathW(install_path);
1087 msi_free(install_path);
1092 UINT msi_create_component_directories( MSIPACKAGE *package )
1096 /* create all the folders required by the components are going to install */
1097 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1099 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1101 msi_create_directory( package, comp->Directory );
1104 return ERROR_SUCCESS;
1108 * Also we cannot enable/disable components either, so for now I am just going
1109 * to do all the directories for all the components.
1111 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1113 static const WCHAR ExecSeqQuery[] =
1114 {'S','E','L','E','C','T',' ',
1115 '`','D','i','r','e','c','t','o','r','y','_','`',
1116 ' ','F','R','O','M',' ',
1117 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1121 /* create all the empty folders specified in the CreateFolder table */
1122 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1123 if (rc != ERROR_SUCCESS)
1124 return ERROR_SUCCESS;
1126 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1127 msiobj_release(&view->hdr);
1129 msi_create_component_directories( package );
1134 static UINT load_component( MSIRECORD *row, LPVOID param )
1136 MSIPACKAGE *package = param;
1139 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1141 return ERROR_FUNCTION_FAILED;
1143 list_add_tail( &package->components, &comp->entry );
1145 /* fill in the data */
1146 comp->Component = msi_dup_record_field( row, 1 );
1148 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1150 comp->ComponentId = msi_dup_record_field( row, 2 );
1151 comp->Directory = msi_dup_record_field( row, 3 );
1152 comp->Attributes = MSI_RecordGetInteger(row,4);
1153 comp->Condition = msi_dup_record_field( row, 5 );
1154 comp->KeyPath = msi_dup_record_field( row, 6 );
1156 comp->Installed = INSTALLSTATE_UNKNOWN;
1157 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1159 return ERROR_SUCCESS;
1162 static UINT load_all_components( MSIPACKAGE *package )
1164 static const WCHAR query[] = {
1165 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1166 '`','C','o','m','p','o','n','e','n','t','`',0 };
1170 if (!list_empty(&package->components))
1171 return ERROR_SUCCESS;
1173 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1174 if (r != ERROR_SUCCESS)
1177 r = MSI_IterateRecords(view, NULL, load_component, package);
1178 msiobj_release(&view->hdr);
1183 MSIPACKAGE *package;
1184 MSIFEATURE *feature;
1187 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1191 cl = msi_alloc( sizeof (*cl) );
1193 return ERROR_NOT_ENOUGH_MEMORY;
1194 cl->component = comp;
1195 list_add_tail( &feature->Components, &cl->entry );
1197 return ERROR_SUCCESS;
1200 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1204 fl = msi_alloc( sizeof(*fl) );
1206 return ERROR_NOT_ENOUGH_MEMORY;
1207 fl->feature = child;
1208 list_add_tail( &parent->Children, &fl->entry );
1210 return ERROR_SUCCESS;
1213 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1215 _ilfs* ilfs= (_ilfs*)param;
1219 component = MSI_RecordGetString(row,1);
1221 /* check to see if the component is already loaded */
1222 comp = get_loaded_component( ilfs->package, component );
1225 ERR("unknown component %s\n", debugstr_w(component));
1226 return ERROR_FUNCTION_FAILED;
1229 add_feature_component( ilfs->feature, comp );
1230 comp->Enabled = TRUE;
1232 return ERROR_SUCCESS;
1235 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1237 MSIFEATURE *feature;
1239 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1241 if ( !lstrcmpW( feature->Feature, name ) )
1248 static UINT load_feature(MSIRECORD * row, LPVOID param)
1250 MSIPACKAGE* package = (MSIPACKAGE*)param;
1251 MSIFEATURE* feature;
1252 static const WCHAR Query1[] =
1253 {'S','E','L','E','C','T',' ',
1254 '`','C','o','m','p','o','n','e','n','t','_','`',
1255 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1256 'C','o','m','p','o','n','e','n','t','s','`',' ',
1257 'W','H','E','R','E',' ',
1258 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1263 /* fill in the data */
1265 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1267 return ERROR_NOT_ENOUGH_MEMORY;
1269 list_init( &feature->Children );
1270 list_init( &feature->Components );
1272 feature->Feature = msi_dup_record_field( row, 1 );
1274 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1276 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1277 feature->Title = msi_dup_record_field( row, 3 );
1278 feature->Description = msi_dup_record_field( row, 4 );
1280 if (!MSI_RecordIsNull(row,5))
1281 feature->Display = MSI_RecordGetInteger(row,5);
1283 feature->Level= MSI_RecordGetInteger(row,6);
1284 feature->Directory = msi_dup_record_field( row, 7 );
1285 feature->Attributes = MSI_RecordGetInteger(row,8);
1287 feature->Installed = INSTALLSTATE_UNKNOWN;
1288 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1290 list_add_tail( &package->features, &feature->entry );
1292 /* load feature components */
1294 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1295 if (rc != ERROR_SUCCESS)
1296 return ERROR_SUCCESS;
1298 ilfs.package = package;
1299 ilfs.feature = feature;
1301 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1302 msiobj_release(&view->hdr);
1304 return ERROR_SUCCESS;
1307 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1309 MSIPACKAGE* package = (MSIPACKAGE*)param;
1310 MSIFEATURE *parent, *child;
1312 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1314 return ERROR_FUNCTION_FAILED;
1316 if (!child->Feature_Parent)
1317 return ERROR_SUCCESS;
1319 parent = find_feature_by_name( package, child->Feature_Parent );
1321 return ERROR_FUNCTION_FAILED;
1323 add_feature_child( parent, child );
1324 return ERROR_SUCCESS;
1327 static UINT load_all_features( MSIPACKAGE *package )
1329 static const WCHAR query[] = {
1330 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1331 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1332 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1336 if (!list_empty(&package->features))
1337 return ERROR_SUCCESS;
1339 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1340 if (r != ERROR_SUCCESS)
1343 r = MSI_IterateRecords( view, NULL, load_feature, package );
1344 if (r != ERROR_SUCCESS)
1347 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1348 msiobj_release( &view->hdr );
1353 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1364 static UINT load_file(MSIRECORD *row, LPVOID param)
1366 MSIPACKAGE* package = (MSIPACKAGE*)param;
1370 /* fill in the data */
1372 file = msi_alloc_zero( sizeof (MSIFILE) );
1374 return ERROR_NOT_ENOUGH_MEMORY;
1376 file->File = msi_dup_record_field( row, 1 );
1378 component = MSI_RecordGetString( row, 2 );
1379 file->Component = get_loaded_component( package, component );
1381 if (!file->Component)
1382 ERR("Unfound Component %s\n",debugstr_w(component));
1384 file->FileName = msi_dup_record_field( row, 3 );
1385 reduce_to_longfilename( file->FileName );
1387 file->ShortName = msi_dup_record_field( row, 3 );
1388 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1390 file->FileSize = MSI_RecordGetInteger( row, 4 );
1391 file->Version = msi_dup_record_field( row, 5 );
1392 file->Language = msi_dup_record_field( row, 6 );
1393 file->Attributes = MSI_RecordGetInteger( row, 7 );
1394 file->Sequence = MSI_RecordGetInteger( row, 8 );
1396 file->state = msifs_invalid;
1398 /* if the compressed bits are not set in the file attributes,
1399 * then read the information from the package word count property
1401 if (file->Attributes & msidbFileAttributesCompressed)
1403 file->IsCompressed = TRUE;
1405 else if (file->Attributes & msidbFileAttributesNoncompressed)
1407 file->IsCompressed = FALSE;
1411 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1414 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1416 list_add_tail( &package->files, &file->entry );
1418 return ERROR_SUCCESS;
1421 static UINT load_all_files(MSIPACKAGE *package)
1425 static const WCHAR Query[] =
1426 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1427 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1428 '`','S','e','q','u','e','n','c','e','`', 0};
1430 if (!list_empty(&package->files))
1431 return ERROR_SUCCESS;
1433 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1434 if (rc != ERROR_SUCCESS)
1435 return ERROR_SUCCESS;
1437 rc = MSI_IterateRecords(view, NULL, load_file, package);
1438 msiobj_release(&view->hdr);
1440 return ERROR_SUCCESS;
1443 static UINT load_folder( MSIRECORD *row, LPVOID param )
1445 MSIPACKAGE *package = param;
1446 static const WCHAR szDot[] = { '.',0 };
1447 static WCHAR szEmpty[] = { 0 };
1448 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1451 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1453 return ERROR_NOT_ENOUGH_MEMORY;
1455 folder->Directory = msi_dup_record_field( row, 1 );
1457 TRACE("%s\n", debugstr_w(folder->Directory));
1459 p = msi_dup_record_field(row, 3);
1461 /* split src and target dir */
1463 src_short = folder_split_path( p, ':' );
1465 /* split the long and short paths */
1466 tgt_long = folder_split_path( tgt_short, '|' );
1467 src_long = folder_split_path( src_short, '|' );
1469 /* check for no-op dirs */
1470 if (!lstrcmpW(szDot, tgt_short))
1471 tgt_short = szEmpty;
1472 if (!lstrcmpW(szDot, src_short))
1473 src_short = szEmpty;
1476 tgt_long = tgt_short;
1479 src_short = tgt_short;
1480 src_long = tgt_long;
1484 src_long = src_short;
1486 /* FIXME: use the target short path too */
1487 folder->TargetDefault = strdupW(tgt_long);
1488 folder->SourceShortPath = strdupW(src_short);
1489 folder->SourceLongPath = strdupW(src_long);
1492 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1493 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1494 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1496 folder->Parent = msi_dup_record_field( row, 2 );
1498 folder->Property = msi_dup_property( package, folder->Directory );
1500 list_add_tail( &package->folders, &folder->entry );
1502 TRACE("returning %p\n", folder);
1504 return ERROR_SUCCESS;
1507 static UINT load_all_folders( MSIPACKAGE *package )
1509 static const WCHAR query[] = {
1510 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1511 '`','D','i','r','e','c','t','o','r','y','`',0 };
1515 if (!list_empty(&package->folders))
1516 return ERROR_SUCCESS;
1518 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1519 if (r != ERROR_SUCCESS)
1522 r = MSI_IterateRecords(view, NULL, load_folder, package);
1523 msiobj_release(&view->hdr);
1528 * I am not doing any of the costing functionality yet.
1529 * Mostly looking at doing the Component and Feature loading
1531 * The native MSI does A LOT of modification to tables here. Mostly adding
1532 * a lot of temporary columns to the Feature and Component tables.
1534 * note: Native msi also tracks the short filename. But I am only going to
1535 * track the long ones. Also looking at this directory table
1536 * it appears that the directory table does not get the parents
1537 * resolved base on property only based on their entries in the
1540 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1542 static const WCHAR szCosting[] =
1543 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1544 static const WCHAR szZero[] = { '0', 0 };
1546 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
1547 return ERROR_SUCCESS;
1549 MSI_SetPropertyW(package, szCosting, szZero);
1550 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1552 load_all_components( package );
1553 load_all_features( package );
1554 load_all_files( package );
1555 load_all_folders( package );
1557 return ERROR_SUCCESS;
1560 static UINT execute_script(MSIPACKAGE *package, UINT script )
1563 UINT rc = ERROR_SUCCESS;
1565 TRACE("Executing Script %i\n",script);
1567 if (!package->script)
1569 ERR("no script!\n");
1570 return ERROR_FUNCTION_FAILED;
1573 for (i = 0; i < package->script->ActionCount[script]; i++)
1576 action = package->script->Actions[script][i];
1577 ui_actionstart(package, action);
1578 TRACE("Executing Action (%s)\n",debugstr_w(action));
1579 rc = ACTION_PerformAction(package, action, TRUE);
1580 if (rc != ERROR_SUCCESS)
1583 msi_free_action_script(package, script);
1587 static UINT ACTION_FileCost(MSIPACKAGE *package)
1589 return ERROR_SUCCESS;
1592 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1596 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1600 if (!comp->ComponentId)
1603 res = MsiGetComponentPathW( package->ProductCode,
1604 comp->ComponentId, NULL, NULL);
1606 res = INSTALLSTATE_ABSENT;
1607 comp->Installed = res;
1611 /* scan for and update current install states */
1612 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1615 MSIFEATURE *feature;
1617 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1620 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1622 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1624 comp= cl->component;
1626 if (!comp->ComponentId)
1628 res = INSTALLSTATE_ABSENT;
1632 if (res == INSTALLSTATE_ABSENT)
1633 res = comp->Installed;
1636 if (res == comp->Installed)
1639 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1640 res != INSTALLSTATE_SOURCE)
1642 res = INSTALLSTATE_INCOMPLETE;
1646 feature->Installed = res;
1650 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1653 static const WCHAR all[]={'A','L','L',0};
1655 MSIFEATURE *feature;
1657 override = msi_dup_property( package, property );
1661 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1663 if (strcmpiW(override,all)==0)
1664 msi_feature_set_state( feature, state );
1667 LPWSTR ptr = override;
1668 LPWSTR ptr2 = strchrW(override,',');
1672 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1673 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1675 msi_feature_set_state( feature, state );
1681 ptr2 = strchrW(ptr,',');
1693 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1696 static const WCHAR szlevel[] =
1697 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1698 static const WCHAR szAddLocal[] =
1699 {'A','D','D','L','O','C','A','L',0};
1700 static const WCHAR szRemove[] =
1701 {'R','E','M','O','V','E',0};
1702 static const WCHAR szReinstall[] =
1703 {'R','E','I','N','S','T','A','L','L',0};
1704 BOOL override = FALSE;
1705 MSICOMPONENT* component;
1706 MSIFEATURE *feature;
1709 /* I do not know if this is where it should happen.. but */
1711 TRACE("Checking Install Level\n");
1713 install_level = msi_get_property_int( package, szlevel, 1 );
1715 /* ok here is the _real_ rub
1716 * all these activation/deactivation things happen in order and things
1717 * later on the list override things earlier on the list.
1718 * 1) INSTALLLEVEL processing
1728 * 11) FILEADDDEFAULT
1729 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1730 * ignored for all the features. seems strange, especially since it is not
1731 * documented anywhere, but it is how it works.
1733 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1734 * REMOVE are the big ones, since we don't handle administrative installs
1737 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1738 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1739 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1743 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1745 BOOL feature_state = ((feature->Level > 0) &&
1746 (feature->Level <= install_level));
1748 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1750 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1751 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1752 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1753 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1755 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1759 /* disable child features of unselected parent features */
1760 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1764 if (feature->Level > 0 && feature->Level <= install_level)
1767 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1768 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1773 /* set the Preselected Property */
1774 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1775 static const WCHAR szOne[] = { '1', 0 };
1777 MSI_SetPropertyW(package,szPreselected,szOne);
1781 * now we want to enable or disable components base on feature
1784 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1788 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1789 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1791 /* features with components that have compressed files are made local */
1792 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1794 if (cl->component->Enabled &&
1795 cl->component->ForceLocalState &&
1796 feature->Action == INSTALLSTATE_SOURCE)
1798 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1803 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1805 component = cl->component;
1807 if (!component->Enabled)
1810 switch (feature->Action)
1812 case INSTALLSTATE_ADVERTISED:
1813 component->hasAdvertiseFeature = 1;
1815 case INSTALLSTATE_SOURCE:
1816 component->hasSourceFeature = 1;
1818 case INSTALLSTATE_LOCAL:
1819 component->hasLocalFeature = 1;
1821 case INSTALLSTATE_DEFAULT:
1822 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1823 component->hasAdvertiseFeature = 1;
1824 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1825 component->hasSourceFeature = 1;
1827 component->hasLocalFeature = 1;
1835 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1837 /* if the component isn't enabled, leave it alone */
1838 if (!component->Enabled)
1841 /* check if it's local or source */
1842 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1843 (component->hasLocalFeature || component->hasSourceFeature))
1845 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1846 !component->ForceLocalState)
1847 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1849 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1853 /* if any feature is local, the component must be local too */
1854 if (component->hasLocalFeature)
1856 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1860 if (component->hasSourceFeature)
1862 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1866 if (component->hasAdvertiseFeature)
1868 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1872 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1875 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1877 if (component->Action == INSTALLSTATE_DEFAULT)
1879 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1880 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1883 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1884 debugstr_w(component->Component), component->Installed, component->Action);
1888 return ERROR_SUCCESS;
1891 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1893 MSIPACKAGE *package = (MSIPACKAGE*)param;
1897 name = MSI_RecordGetString(row,1);
1899 /* This helper function now does ALL the work */
1900 TRACE("Dir %s ...\n",debugstr_w(name));
1901 path = resolve_folder(package,name,FALSE,TRUE,NULL);
1902 TRACE("resolves to %s\n",debugstr_w(path));
1905 return ERROR_SUCCESS;
1908 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1910 MSIPACKAGE *package = (MSIPACKAGE*)param;
1912 MSIFEATURE *feature;
1914 name = MSI_RecordGetString( row, 1 );
1916 feature = get_loaded_feature( package, name );
1918 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1922 Condition = MSI_RecordGetString(row,3);
1924 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1926 int level = MSI_RecordGetInteger(row,2);
1927 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
1928 feature->Level = level;
1931 return ERROR_SUCCESS;
1934 LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1936 static const WCHAR name_fmt[] =
1937 {'%','u','.','%','u','.','%','u','.','%','u',0};
1938 static WCHAR name[] = {'\\',0};
1939 VS_FIXEDFILEINFO *lpVer;
1940 WCHAR filever[0x100];
1946 TRACE("%s\n", debugstr_w(filename));
1948 versize = GetFileVersionInfoSizeW( filename, &handle );
1952 version = msi_alloc( versize );
1953 GetFileVersionInfoW( filename, 0, versize, version );
1955 VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz );
1956 msi_free( version );
1958 sprintfW( filever, name_fmt,
1959 HIWORD(lpVer->dwFileVersionMS),
1960 LOWORD(lpVer->dwFileVersionMS),
1961 HIWORD(lpVer->dwFileVersionLS),
1962 LOWORD(lpVer->dwFileVersionLS));
1964 return strdupW( filever );
1967 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1969 LPWSTR file_version;
1972 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1974 MSICOMPONENT* comp = file->Component;
1980 if (file->IsCompressed)
1981 comp->ForceLocalState = TRUE;
1983 /* calculate target */
1984 p = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL);
1986 msi_free(file->TargetPath);
1988 TRACE("file %s is named %s\n",
1989 debugstr_w(file->File), debugstr_w(file->FileName));
1991 file->TargetPath = build_directory_name(2, p, file->FileName);
1995 TRACE("file %s resolves to %s\n",
1996 debugstr_w(file->File), debugstr_w(file->TargetPath));
1998 /* don't check files of components that aren't installed */
1999 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2000 comp->Installed == INSTALLSTATE_ABSENT)
2002 file->state = msifs_missing; /* assume files are missing */
2006 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2008 file->state = msifs_missing;
2009 comp->Cost += file->FileSize;
2010 comp->Installed = INSTALLSTATE_INCOMPLETE;
2014 if (file->Version &&
2015 (file_version = msi_get_disk_file_version( file->TargetPath )))
2017 TRACE("new %s old %s\n", debugstr_w(file->Version),
2018 debugstr_w(file_version));
2019 /* FIXME: seems like a bad way to compare version numbers */
2020 if (lstrcmpiW(file_version, file->Version)<0)
2022 file->state = msifs_overwrite;
2023 comp->Cost += file->FileSize;
2024 comp->Installed = INSTALLSTATE_INCOMPLETE;
2027 file->state = msifs_present;
2028 msi_free( file_version );
2031 file->state = msifs_present;
2034 return ERROR_SUCCESS;
2038 * A lot is done in this function aside from just the costing.
2039 * The costing needs to be implemented at some point but for now I am going
2040 * to focus on the directory building
2043 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2045 static const WCHAR ExecSeqQuery[] =
2046 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2047 '`','D','i','r','e','c','t','o','r','y','`',0};
2048 static const WCHAR ConditionQuery[] =
2049 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2050 '`','C','o','n','d','i','t','i','o','n','`',0};
2051 static const WCHAR szCosting[] =
2052 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2053 static const WCHAR szlevel[] =
2054 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2055 static const WCHAR szOne[] = { '1', 0 };
2061 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2062 return ERROR_SUCCESS;
2064 TRACE("Building Directory properties\n");
2066 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2067 if (rc == ERROR_SUCCESS)
2069 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2071 msiobj_release(&view->hdr);
2074 /* read components states from the registry */
2075 ACTION_GetComponentInstallStates(package);
2077 TRACE("File calculations\n");
2078 msi_check_file_install_states( package );
2080 TRACE("Evaluating Condition Table\n");
2082 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2083 if (rc == ERROR_SUCCESS)
2085 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2087 msiobj_release(&view->hdr);
2090 TRACE("Enabling or Disabling Components\n");
2091 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2093 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2095 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2096 comp->Enabled = FALSE;
2100 MSI_SetPropertyW(package,szCosting,szOne);
2101 /* set default run level if not set */
2102 level = msi_dup_property( package, szlevel );
2104 MSI_SetPropertyW(package,szlevel, szOne);
2107 ACTION_UpdateFeatureInstallStates(package);
2109 return MSI_SetFeatureStates(package);
2112 /* OK this value is "interpreted" and then formatted based on the
2113 first few characters */
2114 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2118 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2124 LPWSTR deformated = NULL;
2127 deformat_string(package, &value[2], &deformated);
2129 /* binary value type */
2133 *size = (strlenW(ptr)/2)+1;
2135 *size = strlenW(ptr)/2;
2137 data = msi_alloc(*size);
2143 /* if uneven pad with a zero in front */
2149 data[count] = (BYTE)strtol(byte,NULL,0);
2151 TRACE("Uneven byte count\n");
2159 data[count] = (BYTE)strtol(byte,NULL,0);
2162 msi_free(deformated);
2164 TRACE("Data %i bytes(%i)\n",*size,count);
2171 deformat_string(package, &value[1], &deformated);
2174 *size = sizeof(DWORD);
2175 data = msi_alloc(*size);
2181 if ( (*p < '0') || (*p > '9') )
2187 if (deformated[0] == '-')
2190 TRACE("DWORD %i\n",*(LPDWORD)data);
2192 msi_free(deformated);
2197 static const WCHAR szMulti[] = {'[','~',']',0};
2206 *type=REG_EXPAND_SZ;
2214 if (strstrW(value,szMulti))
2215 *type = REG_MULTI_SZ;
2217 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2222 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2224 MSIPACKAGE *package = (MSIPACKAGE*)param;
2225 static const WCHAR szHCR[] =
2226 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2227 'R','O','O','T','\\',0};
2228 static const WCHAR szHCU[] =
2229 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2230 'U','S','E','R','\\',0};
2231 static const WCHAR szHLM[] =
2232 {'H','K','E','Y','_','L','O','C','A','L','_',
2233 'M','A','C','H','I','N','E','\\',0};
2234 static const WCHAR szHU[] =
2235 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2237 LPSTR value_data = NULL;
2238 HKEY root_key, hkey;
2241 LPCWSTR szRoot, component, name, key, value;
2246 BOOL check_first = FALSE;
2249 ui_progress(package,2,0,0,0);
2256 component = MSI_RecordGetString(row, 6);
2257 comp = get_loaded_component(package,component);
2259 return ERROR_SUCCESS;
2261 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2263 TRACE("Skipping write due to disabled component %s\n",
2264 debugstr_w(component));
2266 comp->Action = comp->Installed;
2268 return ERROR_SUCCESS;
2271 comp->Action = INSTALLSTATE_LOCAL;
2273 name = MSI_RecordGetString(row, 4);
2274 if( MSI_RecordIsNull(row,5) && name )
2276 /* null values can have special meanings */
2277 if (name[0]=='-' && name[1] == 0)
2278 return ERROR_SUCCESS;
2279 else if ((name[0]=='+' && name[1] == 0) ||
2280 (name[0] == '*' && name[1] == 0))
2285 root = MSI_RecordGetInteger(row,2);
2286 key = MSI_RecordGetString(row, 3);
2288 /* get the root key */
2293 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2294 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2295 if (all_users && all_users[0] == '1')
2297 root_key = HKEY_LOCAL_MACHINE;
2302 root_key = HKEY_CURRENT_USER;
2305 msi_free(all_users);
2308 case 0: root_key = HKEY_CLASSES_ROOT;
2311 case 1: root_key = HKEY_CURRENT_USER;
2314 case 2: root_key = HKEY_LOCAL_MACHINE;
2317 case 3: root_key = HKEY_USERS;
2321 ERR("Unknown root %i\n",root);
2327 return ERROR_SUCCESS;
2329 deformat_string(package, key , &deformated);
2330 size = strlenW(deformated) + strlenW(szRoot) + 1;
2331 uikey = msi_alloc(size*sizeof(WCHAR));
2332 strcpyW(uikey,szRoot);
2333 strcatW(uikey,deformated);
2335 if (RegCreateKeyW( root_key, deformated, &hkey))
2337 ERR("Could not create key %s\n",debugstr_w(deformated));
2338 msi_free(deformated);
2340 return ERROR_SUCCESS;
2342 msi_free(deformated);
2344 value = MSI_RecordGetString(row,5);
2346 value_data = parse_value(package, value, &type, &size);
2349 static const WCHAR szEmpty[] = {0};
2350 value_data = (LPSTR)strdupW(szEmpty);
2355 deformat_string(package, name, &deformated);
2357 /* get the double nulls to terminate SZ_MULTI */
2358 if (type == REG_MULTI_SZ)
2359 size +=sizeof(WCHAR);
2363 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2365 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2370 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2371 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2373 TRACE("value %s of %s checked already exists\n",
2374 debugstr_w(deformated), debugstr_w(uikey));
2378 TRACE("Checked and setting value %s of %s\n",
2379 debugstr_w(deformated), debugstr_w(uikey));
2380 if (deformated || size)
2381 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2386 uirow = MSI_CreateRecord(3);
2387 MSI_RecordSetStringW(uirow,2,deformated);
2388 MSI_RecordSetStringW(uirow,1,uikey);
2391 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2393 MSI_RecordSetStringW(uirow,3,value);
2395 ui_actiondata(package,szWriteRegistryValues,uirow);
2396 msiobj_release( &uirow->hdr );
2398 msi_free(value_data);
2399 msi_free(deformated);
2402 return ERROR_SUCCESS;
2405 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2409 static const WCHAR ExecSeqQuery[] =
2410 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2411 '`','R','e','g','i','s','t','r','y','`',0 };
2413 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2414 if (rc != ERROR_SUCCESS)
2415 return ERROR_SUCCESS;
2417 /* increment progress bar each time action data is sent */
2418 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2420 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2422 msiobj_release(&view->hdr);
2426 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2428 package->script->CurrentlyScripting = TRUE;
2430 return ERROR_SUCCESS;
2434 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2439 static const WCHAR q1[]=
2440 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2441 '`','R','e','g','i','s','t','r','y','`',0};
2444 MSIFEATURE *feature;
2447 TRACE("InstallValidate\n");
2449 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2450 if (rc == ERROR_SUCCESS)
2452 MSI_IterateRecords( view, &progress, NULL, package );
2453 msiobj_release( &view->hdr );
2454 total += progress * REG_PROGRESS_VALUE;
2457 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2458 total += COMPONENT_PROGRESS_VALUE;
2460 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2461 total += file->FileSize;
2463 ui_progress(package,0,total,0,0);
2465 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2467 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2468 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2469 feature->ActionRequest);
2472 return ERROR_SUCCESS;
2475 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2477 MSIPACKAGE* package = (MSIPACKAGE*)param;
2478 LPCWSTR cond = NULL;
2479 LPCWSTR message = NULL;
2480 static const WCHAR title[]=
2481 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2483 cond = MSI_RecordGetString(row,1);
2485 if (MSI_EvaluateConditionW(package,cond) != MSICONDITION_TRUE)
2488 message = MSI_RecordGetString(row,2);
2489 deformat_string(package,message,&deformated);
2490 MessageBoxW(NULL,deformated,title,MB_OK);
2491 msi_free(deformated);
2492 return ERROR_FUNCTION_FAILED;
2495 return ERROR_SUCCESS;
2498 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2501 MSIQUERY * view = NULL;
2502 static const WCHAR ExecSeqQuery[] =
2503 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2504 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2506 TRACE("Checking launch conditions\n");
2508 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2509 if (rc != ERROR_SUCCESS)
2510 return ERROR_SUCCESS;
2512 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2513 msiobj_release(&view->hdr);
2518 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2522 return resolve_folder(package,cmp->Directory,FALSE,FALSE,NULL);
2524 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2526 MSIRECORD * row = 0;
2528 LPWSTR deformated,buffer,deformated_name;
2530 static const WCHAR ExecSeqQuery[] =
2531 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2532 '`','R','e','g','i','s','t','r','y','`',' ',
2533 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2534 ' ','=',' ' ,'\'','%','s','\'',0 };
2535 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2536 static const WCHAR fmt2[]=
2537 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2539 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2543 root = MSI_RecordGetInteger(row,2);
2544 key = MSI_RecordGetString(row, 3);
2545 name = MSI_RecordGetString(row, 4);
2546 deformat_string(package, key , &deformated);
2547 deformat_string(package, name, &deformated_name);
2549 len = strlenW(deformated) + 6;
2550 if (deformated_name)
2551 len+=strlenW(deformated_name);
2553 buffer = msi_alloc( len *sizeof(WCHAR));
2555 if (deformated_name)
2556 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2558 sprintfW(buffer,fmt,root,deformated);
2560 msi_free(deformated);
2561 msi_free(deformated_name);
2562 msiobj_release(&row->hdr);
2566 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2568 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2573 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2576 return strdupW( file->TargetPath );
2581 static HKEY openSharedDLLsKey(void)
2584 static const WCHAR path[] =
2585 {'S','o','f','t','w','a','r','e','\\',
2586 'M','i','c','r','o','s','o','f','t','\\',
2587 'W','i','n','d','o','w','s','\\',
2588 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2589 'S','h','a','r','e','d','D','L','L','s',0};
2591 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2595 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2600 DWORD sz = sizeof(count);
2603 hkey = openSharedDLLsKey();
2604 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2605 if (rc != ERROR_SUCCESS)
2611 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2615 hkey = openSharedDLLsKey();
2617 msi_reg_set_val_dword( hkey, path, count );
2619 RegDeleteValueW(hkey,path);
2625 * Return TRUE if the count should be written out and FALSE if not
2627 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2629 MSIFEATURE *feature;
2633 /* only refcount DLLs */
2634 if (comp->KeyPath == NULL ||
2635 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2636 comp->Attributes & msidbComponentAttributesODBCDataSource)
2640 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2641 write = (count > 0);
2643 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2647 /* increment counts */
2648 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2652 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2655 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2657 if ( cl->component == comp )
2662 /* decrement counts */
2663 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2667 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2670 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2672 if ( cl->component == comp )
2677 /* ref count all the files in the component */
2682 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2684 if (file->Component == comp)
2685 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2689 /* add a count for permenent */
2690 if (comp->Attributes & msidbComponentAttributesPermanent)
2693 comp->RefCount = count;
2696 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2700 * Ok further analysis makes me think that this work is
2701 * actually done in the PublishComponents and PublishFeatures
2702 * step, and not here. It appears like the keypath and all that is
2703 * resolved in this step, however actually written in the Publish steps.
2704 * But we will leave it here for now because it is unclear
2706 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2708 WCHAR squished_pc[GUID_SIZE];
2709 WCHAR squished_cc[GUID_SIZE];
2712 HKEY hkey=0,hkey2=0;
2714 /* writes the Component and Features values to the registry */
2716 rc = MSIREG_OpenComponents(&hkey);
2717 if (rc != ERROR_SUCCESS)
2720 squash_guid(package->ProductCode,squished_pc);
2721 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2723 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2727 ui_progress(package,2,0,0,0);
2728 if (!comp->ComponentId)
2731 squash_guid(comp->ComponentId,squished_cc);
2733 msi_free(comp->FullKeypath);
2734 comp->FullKeypath = resolve_keypath( package, comp );
2736 /* do the refcounting */
2737 ACTION_RefCountComponent( package, comp );
2739 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2740 debugstr_w(comp->Component),
2741 debugstr_w(squished_cc),
2742 debugstr_w(comp->FullKeypath),
2745 * Write the keypath out if the component is to be registered
2746 * and delete the key if the component is to be deregistered
2748 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2750 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2751 if (rc != ERROR_SUCCESS)
2754 if (!comp->FullKeypath)
2757 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2759 if (comp->Attributes & msidbComponentAttributesPermanent)
2761 static const WCHAR szPermKey[] =
2762 { '0','0','0','0','0','0','0','0','0','0','0','0',
2763 '0','0','0','0','0','0','0','0','0','0','0','0',
2764 '0','0','0','0','0','0','0','0',0 };
2766 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2771 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2775 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2776 if (rc != ERROR_SUCCESS)
2779 RegDeleteValueW(hkey2,squished_pc);
2781 /* if the key is empty delete it */
2782 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2784 if (res == ERROR_NO_MORE_ITEMS)
2785 RegDeleteKeyW(hkey,squished_cc);
2790 uirow = MSI_CreateRecord(3);
2791 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2792 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2793 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2794 ui_actiondata(package,szProcessComponents,uirow);
2795 msiobj_release( &uirow->hdr );
2809 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2810 LPWSTR lpszName, LONG_PTR lParam)
2813 typelib_struct *tl_struct = (typelib_struct*) lParam;
2814 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2818 if (!IS_INTRESOURCE(lpszName))
2820 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2824 sz = strlenW(tl_struct->source)+4;
2825 sz *= sizeof(WCHAR);
2827 if ((INT_PTR)lpszName == 1)
2828 tl_struct->path = strdupW(tl_struct->source);
2831 tl_struct->path = msi_alloc(sz);
2832 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2835 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2836 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2837 if (!SUCCEEDED(res))
2839 msi_free(tl_struct->path);
2840 tl_struct->path = NULL;
2845 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2846 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2848 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2852 msi_free(tl_struct->path);
2853 tl_struct->path = NULL;
2855 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2856 ITypeLib_Release(tl_struct->ptLib);
2861 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2863 MSIPACKAGE* package = (MSIPACKAGE*)param;
2867 typelib_struct tl_struct;
2869 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2871 component = MSI_RecordGetString(row,3);
2872 comp = get_loaded_component(package,component);
2874 return ERROR_SUCCESS;
2876 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2878 TRACE("Skipping typelib reg due to disabled component\n");
2880 comp->Action = comp->Installed;
2882 return ERROR_SUCCESS;
2885 comp->Action = INSTALLSTATE_LOCAL;
2887 file = get_loaded_file( package, comp->KeyPath );
2889 return ERROR_SUCCESS;
2891 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2895 guid = MSI_RecordGetString(row,1);
2896 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2897 tl_struct.source = strdupW( file->TargetPath );
2898 tl_struct.path = NULL;
2900 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2901 (LONG_PTR)&tl_struct);
2909 helpid = MSI_RecordGetString(row,6);
2912 help = resolve_folder(package,helpid,FALSE,FALSE,NULL);
2913 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2916 if (!SUCCEEDED(res))
2917 ERR("Failed to register type library %s\n",
2918 debugstr_w(tl_struct.path));
2921 ui_actiondata(package,szRegisterTypeLibraries,row);
2923 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2926 ITypeLib_Release(tl_struct.ptLib);
2927 msi_free(tl_struct.path);
2930 ERR("Failed to load type library %s\n",
2931 debugstr_w(tl_struct.source));
2933 FreeLibrary(module);
2934 msi_free(tl_struct.source);
2937 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
2939 return ERROR_SUCCESS;
2942 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2945 * OK this is a bit confusing.. I am given a _Component key and I believe
2946 * that the file that is being registered as a type library is the "key file
2947 * of that component" which I interpret to mean "The file in the KeyPath of
2952 static const WCHAR Query[] =
2953 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2954 '`','T','y','p','e','L','i','b','`',0};
2956 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2957 if (rc != ERROR_SUCCESS)
2958 return ERROR_SUCCESS;
2960 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2961 msiobj_release(&view->hdr);
2965 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
2967 MSIPACKAGE *package = (MSIPACKAGE*)param;
2968 LPWSTR target_file, target_folder, filename;
2969 LPCWSTR buffer, extension;
2971 static const WCHAR szlnk[]={'.','l','n','k',0};
2972 IShellLinkW *sl = NULL;
2973 IPersistFile *pf = NULL;
2976 buffer = MSI_RecordGetString(row,4);
2977 comp = get_loaded_component(package,buffer);
2979 return ERROR_SUCCESS;
2981 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
2983 TRACE("Skipping shortcut creation due to disabled component\n");
2985 comp->Action = comp->Installed;
2987 return ERROR_SUCCESS;
2990 comp->Action = INSTALLSTATE_LOCAL;
2992 ui_actiondata(package,szCreateShortcuts,row);
2994 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
2995 &IID_IShellLinkW, (LPVOID *) &sl );
2999 ERR("CLSID_ShellLink not available\n");
3003 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3006 ERR("QueryInterface(IID_IPersistFile) failed\n");
3010 buffer = MSI_RecordGetString(row,2);
3011 target_folder = resolve_folder(package, buffer,FALSE,FALSE,NULL);
3013 /* may be needed because of a bug somehwere else */
3014 create_full_pathW(target_folder);
3016 filename = msi_dup_record_field( row, 3 );
3017 reduce_to_longfilename(filename);
3019 extension = strchrW(filename,'.');
3020 if (!extension || strcmpiW(extension,szlnk))
3022 int len = strlenW(filename);
3023 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3024 memcpy(filename + len, szlnk, sizeof(szlnk));
3026 target_file = build_directory_name(2, target_folder, filename);
3027 msi_free(target_folder);
3030 buffer = MSI_RecordGetString(row,5);
3031 if (strchrW(buffer,'['))
3034 deformat_string(package,buffer,&deformated);
3035 IShellLinkW_SetPath(sl,deformated);
3036 msi_free(deformated);
3040 FIXME("poorly handled shortcut format, advertised shortcut\n");
3041 IShellLinkW_SetPath(sl,comp->FullKeypath);
3044 if (!MSI_RecordIsNull(row,6))
3047 buffer = MSI_RecordGetString(row,6);
3048 deformat_string(package,buffer,&deformated);
3049 IShellLinkW_SetArguments(sl,deformated);
3050 msi_free(deformated);
3053 if (!MSI_RecordIsNull(row,7))
3055 buffer = MSI_RecordGetString(row,7);
3056 IShellLinkW_SetDescription(sl,buffer);
3059 if (!MSI_RecordIsNull(row,8))
3060 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3062 if (!MSI_RecordIsNull(row,9))
3067 buffer = MSI_RecordGetString(row,9);
3069 Path = build_icon_path(package,buffer);
3070 index = MSI_RecordGetInteger(row,10);
3072 /* no value means 0 */
3073 if (index == MSI_NULL_INTEGER)
3076 IShellLinkW_SetIconLocation(sl,Path,index);
3080 if (!MSI_RecordIsNull(row,11))
3081 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3083 if (!MSI_RecordIsNull(row,12))
3086 buffer = MSI_RecordGetString(row,12);
3087 Path = resolve_folder(package, buffer, FALSE, FALSE, NULL);
3089 IShellLinkW_SetWorkingDirectory(sl,Path);
3093 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3094 IPersistFile_Save(pf,target_file,FALSE);
3096 msi_free(target_file);
3100 IPersistFile_Release( pf );
3102 IShellLinkW_Release( sl );
3104 return ERROR_SUCCESS;
3107 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3112 static const WCHAR Query[] =
3113 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3114 '`','S','h','o','r','t','c','u','t','`',0};
3116 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3117 if (rc != ERROR_SUCCESS)
3118 return ERROR_SUCCESS;
3120 res = CoInitialize( NULL );
3123 ERR("CoInitialize failed\n");
3124 return ERROR_FUNCTION_FAILED;
3127 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3128 msiobj_release(&view->hdr);
3135 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3137 MSIPACKAGE* package = (MSIPACKAGE*)param;
3146 FileName = MSI_RecordGetString(row,1);
3149 ERR("Unable to get FileName\n");
3150 return ERROR_SUCCESS;
3153 FilePath = build_icon_path(package,FileName);
3155 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3157 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3158 FILE_ATTRIBUTE_NORMAL, NULL);
3160 if (the_file == INVALID_HANDLE_VALUE)
3162 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3164 return ERROR_SUCCESS;
3171 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3172 if (rc != ERROR_SUCCESS)
3174 ERR("Failed to get stream\n");
3175 CloseHandle(the_file);
3176 DeleteFileW(FilePath);
3179 WriteFile(the_file,buffer,sz,&write,NULL);
3180 } while (sz == 1024);
3184 CloseHandle(the_file);
3186 uirow = MSI_CreateRecord(1);
3187 MSI_RecordSetStringW(uirow,1,FileName);
3188 ui_actiondata(package,szPublishProduct,uirow);
3189 msiobj_release( &uirow->hdr );
3191 return ERROR_SUCCESS;
3195 * 99% of the work done here is only done for
3196 * advertised installs. However this is where the
3197 * Icon table is processed and written out
3198 * so that is what I am going to do here.
3200 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3204 static const WCHAR Query[]=
3205 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3206 '`','I','c','o','n','`',0};
3207 /* for registry stuff */
3210 static const WCHAR szProductLanguage[] =
3211 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3212 static const WCHAR szARPProductIcon[] =
3213 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3214 static const WCHAR szProductVersion[] =
3215 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3219 MSIHANDLE hDb, hSumInfo;
3221 /* write out icon files */
3223 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3224 if (rc == ERROR_SUCCESS)
3226 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3227 msiobj_release(&view->hdr);
3230 /* ok there is a lot more done here but i need to figure out what */
3232 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3233 if (rc != ERROR_SUCCESS)
3236 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3237 if (rc != ERROR_SUCCESS)
3241 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3242 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3245 langid = msi_get_property_int( package, szProductLanguage, 0 );
3246 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3248 buffer = msi_dup_property( package, szARPProductIcon );
3251 LPWSTR path = build_icon_path(package,buffer);
3252 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3257 buffer = msi_dup_property( package, szProductVersion );
3260 DWORD verdword = msi_version_str_to_dword(buffer);
3261 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3265 /* FIXME: Need to write more keys to the user registry */
3267 hDb= alloc_msihandle( &package->db->hdr );
3269 rc = ERROR_NOT_ENOUGH_MEMORY;
3272 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3273 MsiCloseHandle(hDb);
3274 if (rc == ERROR_SUCCESS)
3276 WCHAR guidbuffer[0x200];
3278 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3280 if (rc == ERROR_SUCCESS)
3282 WCHAR squashed[GUID_SIZE];
3283 /* for now we only care about the first guid */
3284 LPWSTR ptr = strchrW(guidbuffer,';');
3286 squash_guid(guidbuffer,squashed);
3287 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3291 ERR("Unable to query Revision_Number...\n");
3294 MsiCloseHandle(hSumInfo);
3298 ERR("Unable to open Summary Information\n");
3310 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3312 MSIPACKAGE *package = (MSIPACKAGE*)param;
3313 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3314 LPWSTR deformated_section, deformated_key, deformated_value;
3315 LPWSTR folder, fullname = NULL;
3319 static const WCHAR szWindowsFolder[] =
3320 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3322 component = MSI_RecordGetString(row, 8);
3323 comp = get_loaded_component(package,component);
3325 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3327 TRACE("Skipping ini file due to disabled component %s\n",
3328 debugstr_w(component));
3330 comp->Action = comp->Installed;
3332 return ERROR_SUCCESS;
3335 comp->Action = INSTALLSTATE_LOCAL;
3337 identifier = MSI_RecordGetString(row,1);
3338 filename = MSI_RecordGetString(row,2);
3339 dirproperty = MSI_RecordGetString(row,3);
3340 section = MSI_RecordGetString(row,4);
3341 key = MSI_RecordGetString(row,5);
3342 value = MSI_RecordGetString(row,6);
3343 action = MSI_RecordGetInteger(row,7);
3345 deformat_string(package,section,&deformated_section);
3346 deformat_string(package,key,&deformated_key);
3347 deformat_string(package,value,&deformated_value);
3351 folder = resolve_folder(package, dirproperty, FALSE, FALSE, NULL);
3353 folder = msi_dup_property( package, dirproperty );
3356 folder = msi_dup_property( package, szWindowsFolder );
3360 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3364 fullname = build_directory_name(2, folder, filename);
3368 TRACE("Adding value %s to section %s in %s\n",
3369 debugstr_w(deformated_key), debugstr_w(deformated_section),
3370 debugstr_w(fullname));
3371 WritePrivateProfileStringW(deformated_section, deformated_key,
3372 deformated_value, fullname);
3374 else if (action == 1)
3377 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3378 returned, 10, fullname);
3379 if (returned[0] == 0)
3381 TRACE("Adding value %s to section %s in %s\n",
3382 debugstr_w(deformated_key), debugstr_w(deformated_section),
3383 debugstr_w(fullname));
3385 WritePrivateProfileStringW(deformated_section, deformated_key,
3386 deformated_value, fullname);
3389 else if (action == 3)
3390 FIXME("Append to existing section not yet implemented\n");
3392 uirow = MSI_CreateRecord(4);
3393 MSI_RecordSetStringW(uirow,1,identifier);
3394 MSI_RecordSetStringW(uirow,2,deformated_section);
3395 MSI_RecordSetStringW(uirow,3,deformated_key);
3396 MSI_RecordSetStringW(uirow,4,deformated_value);
3397 ui_actiondata(package,szWriteIniValues,uirow);
3398 msiobj_release( &uirow->hdr );
3402 msi_free(deformated_key);
3403 msi_free(deformated_value);
3404 msi_free(deformated_section);
3405 return ERROR_SUCCESS;
3408 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3412 static const WCHAR ExecSeqQuery[] =
3413 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3414 '`','I','n','i','F','i','l','e','`',0};
3416 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3417 if (rc != ERROR_SUCCESS)
3419 TRACE("no IniFile table\n");
3420 return ERROR_SUCCESS;
3423 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3424 msiobj_release(&view->hdr);
3428 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3430 MSIPACKAGE *package = (MSIPACKAGE*)param;
3435 static const WCHAR ExeStr[] =
3436 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3437 static const WCHAR close[] = {'\"',0};
3439 PROCESS_INFORMATION info;
3444 memset(&si,0,sizeof(STARTUPINFOW));
3446 filename = MSI_RecordGetString(row,1);
3447 file = get_loaded_file( package, filename );
3451 ERR("Unable to find file id %s\n",debugstr_w(filename));
3452 return ERROR_SUCCESS;
3455 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3457 FullName = msi_alloc(len*sizeof(WCHAR));
3458 strcpyW(FullName,ExeStr);
3459 strcatW( FullName, file->TargetPath );
3460 strcatW(FullName,close);
3462 TRACE("Registering %s\n",debugstr_w(FullName));
3463 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3467 msi_dialog_check_messages(info.hProcess);
3472 uirow = MSI_CreateRecord( 2 );
3473 uipath = strdupW( file->TargetPath );
3474 p = strrchrW(uipath,'\\');
3477 MSI_RecordSetStringW( uirow, 1, &p[2] );
3478 MSI_RecordSetStringW( uirow, 2, uipath);
3479 ui_actiondata( package, szSelfRegModules, uirow);
3480 msiobj_release( &uirow->hdr );
3482 /* FIXME: call ui_progress? */
3484 return ERROR_SUCCESS;
3487 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3491 static const WCHAR ExecSeqQuery[] =
3492 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3493 '`','S','e','l','f','R','e','g','`',0};
3495 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3496 if (rc != ERROR_SUCCESS)
3498 TRACE("no SelfReg table\n");
3499 return ERROR_SUCCESS;
3502 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3503 msiobj_release(&view->hdr);
3505 return ERROR_SUCCESS;
3508 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3510 MSIFEATURE *feature;
3515 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3516 if (rc != ERROR_SUCCESS)
3519 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3520 if (rc != ERROR_SUCCESS)
3523 /* here the guids are base 85 encoded */
3524 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3530 BOOL absent = FALSE;
3533 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3534 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3535 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3539 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3543 if (feature->Feature_Parent)
3544 size += strlenW( feature->Feature_Parent )+2;
3546 data = msi_alloc(size * sizeof(WCHAR));
3549 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3551 MSICOMPONENT* component = cl->component;
3555 if (component->ComponentId)
3557 TRACE("From %s\n",debugstr_w(component->ComponentId));
3558 CLSIDFromString(component->ComponentId, &clsid);
3559 encode_base85_guid(&clsid,buf);
3560 TRACE("to %s\n",debugstr_w(buf));
3564 if (feature->Feature_Parent)
3566 static const WCHAR sep[] = {'\2',0};
3568 strcatW(data,feature->Feature_Parent);
3571 msi_reg_set_val_str( hkey, feature->Feature, data );
3575 if (feature->Feature_Parent)
3576 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3579 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3580 (LPBYTE)feature->Feature_Parent,size);
3584 size += 2*sizeof(WCHAR);
3585 data = msi_alloc(size);
3588 if (feature->Feature_Parent)
3589 strcpyW( &data[1], feature->Feature_Parent );
3590 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3596 uirow = MSI_CreateRecord( 1 );
3597 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3598 ui_actiondata( package, szPublishFeatures, uirow);
3599 msiobj_release( &uirow->hdr );
3600 /* FIXME: call ui_progress? */
3609 static UINT msi_get_local_package_name( LPWSTR path )
3611 static const WCHAR szInstaller[] = {
3612 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3613 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3617 time = GetTickCount();
3618 GetWindowsDirectoryW( path, MAX_PATH );
3619 lstrcatW( path, szInstaller );
3620 CreateDirectoryW( path, NULL );
3622 len = lstrlenW(path);
3623 for (i=0; i<0x10000; i++)
3625 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3626 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3627 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3628 if (handle != INVALID_HANDLE_VALUE)
3630 CloseHandle(handle);
3633 if (GetLastError() != ERROR_FILE_EXISTS &&
3634 GetLastError() != ERROR_SHARING_VIOLATION)
3635 return ERROR_FUNCTION_FAILED;
3638 return ERROR_SUCCESS;
3641 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3643 static const WCHAR szOriginalDatabase[] =
3644 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3645 WCHAR packagefile[MAX_PATH];
3649 r = msi_get_local_package_name( packagefile );
3650 if (r != ERROR_SUCCESS)
3653 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3655 msiFilePath = msi_dup_property( package, szOriginalDatabase );
3656 r = CopyFileW( msiFilePath, packagefile, FALSE);
3657 msi_free( msiFilePath );
3661 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3662 debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3663 return ERROR_FUNCTION_FAILED;
3666 /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
3667 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3668 return ERROR_SUCCESS;
3671 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3673 LPWSTR prop, val, key;
3674 static const LPCSTR propval[] = {
3675 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3676 "ARPCONTACT", "Contact",
3677 "ARPCOMMENTS", "Comments",
3678 "ProductName", "DisplayName",
3679 "ProductVersion", "DisplayVersion",
3680 "ARPHELPLINK", "HelpLink",
3681 "ARPHELPTELEPHONE", "HelpTelephone",
3682 "ARPINSTALLLOCATION", "InstallLocation",
3683 "SourceDir", "InstallSource",
3684 "Manufacturer", "Publisher",
3685 "ARPREADME", "Readme",
3687 "ARPURLINFOABOUT", "URLInfoAbout",
3688 "ARPURLUPDATEINFO", "URLUpdateInfo",
3691 const LPCSTR *p = propval;
3695 prop = strdupAtoW( *p++ );
3696 key = strdupAtoW( *p++ );
3697 val = msi_dup_property( package, prop );
3698 msi_reg_set_val_str( hkey, key, val );
3703 return ERROR_SUCCESS;
3706 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3709 LPWSTR buffer = NULL;
3712 static const WCHAR szWindowsInstaller[] =
3713 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3714 static const WCHAR szUpgradeCode[] =
3715 {'U','p','g','r','a','d','e','C','o','d','e',0};
3716 static const WCHAR modpath_fmt[] =
3717 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3718 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3719 static const WCHAR szModifyPath[] =
3720 {'M','o','d','i','f','y','P','a','t','h',0};
3721 static const WCHAR szUninstallString[] =
3722 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3723 static const WCHAR szEstimatedSize[] =
3724 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3725 static const WCHAR szProductLanguage[] =
3726 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3727 static const WCHAR szProductVersion[] =
3728 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3731 static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3732 LPWSTR upgrade_code;
3735 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3736 if (rc != ERROR_SUCCESS)
3739 /* dump all the info i can grab */
3740 /* FIXME: Flesh out more information */
3742 msi_write_uninstall_property_vals( package, hkey );
3744 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3746 msi_make_package_local( package, hkey );
3748 /* do ModifyPath and UninstallString */
3749 size = deformat_string(package,modpath_fmt,&buffer);
3750 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3751 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3754 /* FIXME: Write real Estimated Size when we have it */
3755 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3757 GetLocalTime(&systime);
3758 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3759 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3761 langid = msi_get_property_int( package, szProductLanguage, 0 );
3762 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3764 buffer = msi_dup_property( package, szProductVersion );
3767 DWORD verdword = msi_version_str_to_dword(buffer);
3769 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3770 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3771 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3775 /* Handle Upgrade Codes */
3776 upgrade_code = msi_dup_property( package, szUpgradeCode );
3781 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3782 squash_guid(package->ProductCode,squashed);
3783 msi_reg_set_val_str( hkey2, squashed, NULL );
3785 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3786 squash_guid(package->ProductCode,squashed);
3787 msi_reg_set_val_str( hkey2, squashed, NULL );
3790 msi_free(upgrade_code);
3795 /* FIXME: call ui_actiondata */
3797 return ERROR_SUCCESS;
3800 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
3802 return execute_script(package,INSTALL_SCRIPT);
3805 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
3809 /* turn off scheduling */
3810 package->script->CurrentlyScripting= FALSE;
3812 /* first do the same as an InstallExecute */
3813 rc = ACTION_InstallExecute(package);
3814 if (rc != ERROR_SUCCESS)
3817 /* then handle Commit Actions */
3818 rc = execute_script(package,COMMIT_SCRIPT);
3823 static UINT ACTION_ForceReboot(MSIPACKAGE *package)
3825 static const WCHAR RunOnce[] = {
3826 'S','o','f','t','w','a','r','e','\\',
3827 'M','i','c','r','o','s','o','f','t','\\',
3828 'W','i','n','d','o','w','s','\\',
3829 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3830 'R','u','n','O','n','c','e',0};
3831 static const WCHAR InstallRunOnce[] = {
3832 'S','o','f','t','w','a','r','e','\\',
3833 'M','i','c','r','o','s','o','f','t','\\',
3834 'W','i','n','d','o','w','s','\\',
3835 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3836 'I','n','s','t','a','l','l','e','r','\\',
3837 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
3839 static const WCHAR msiexec_fmt[] = {
3841 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
3842 '\"','%','s','\"',0};
3843 static const WCHAR install_fmt[] = {
3844 '/','I',' ','\"','%','s','\"',' ',
3845 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
3846 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
3847 WCHAR buffer[256], sysdir[MAX_PATH];
3849 WCHAR squished_pc[100];
3851 squash_guid(package->ProductCode,squished_pc);
3853 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
3854 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
3855 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
3858 msi_reg_set_val_str( hkey, squished_pc, buffer );
3861 TRACE("Reboot command %s\n",debugstr_w(buffer));
3863 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
3864 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
3866 msi_reg_set_val_str( hkey, squished_pc, buffer );
3869 return ERROR_INSTALL_SUSPEND;
3872 UINT msi_set_sourcedir_props(MSIPACKAGE *package)
3877 p = strrchrW( package->PackagePath, '\\' );
3879 return ERROR_SUCCESS;
3881 len = p - package->PackagePath + 2;
3882 source = msi_alloc( len * sizeof(WCHAR) );
3883 lstrcpynW( source, package->PackagePath, len );
3885 MSI_SetPropertyW( package, cszSourceDir, source );
3886 MSI_SetPropertyW( package, cszSOURCEDIR, source );
3890 return ERROR_SUCCESS;
3893 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
3899 * We are currently doing what should be done here in the top level Install
3900 * however for Administrative and uninstalls this step will be needed
3902 if (!package->PackagePath)
3903 return ERROR_SUCCESS;
3905 msi_set_sourcedir_props(package);
3907 attrib = GetFileAttributesW(package->PackagePath);
3908 if (attrib == INVALID_FILE_ATTRIBUTES)
3914 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
3915 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3916 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
3917 if (rc == ERROR_MORE_DATA)
3919 prompt = msi_alloc(size * sizeof(WCHAR));
3920 MsiSourceListGetInfoW(package->ProductCode, NULL,
3921 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
3922 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
3925 prompt = strdupW(package->PackagePath);
3927 msg = generate_error_string(package,1302,1,prompt);
3928 while(attrib == INVALID_FILE_ATTRIBUTES)
3930 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
3933 rc = ERROR_INSTALL_USEREXIT;
3936 attrib = GetFileAttributesW(package->PackagePath);
3942 return ERROR_SUCCESS;
3947 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
3954 static const WCHAR szPropKeys[][80] =
3956 {'P','r','o','d','u','c','t','I','D',0},
3957 {'U','S','E','R','N','A','M','E',0},
3958 {'C','O','M','P','A','N','Y','N','A','M','E',0},
3962 static const WCHAR szRegKeys[][80] =
3964 {'P','r','o','d','u','c','t','I','D',0},
3965 {'R','e','g','O','w','n','e','r',0},
3966 {'R','e','g','C','o','m','p','a','n','y',0},
3970 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
3972 return ERROR_SUCCESS;
3974 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3975 if (rc != ERROR_SUCCESS)
3978 for( i = 0; szPropKeys[i][0]; i++ )
3980 buffer = msi_dup_property( package, szPropKeys[i] );
3981 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
3986 msi_free(productid);
3989 /* FIXME: call ui_actiondata */
3991 return ERROR_SUCCESS;
3995 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
3999 package->script->InWhatSequence |= SEQUENCE_EXEC;
4000 rc = ACTION_ProcessExecSequence(package,FALSE);
4005 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4007 MSIPACKAGE *package = (MSIPACKAGE*)param;
4008 LPCWSTR compgroupid=NULL;
4009 LPCWSTR feature=NULL;
4010 LPCWSTR text = NULL;
4011 LPCWSTR qualifier = NULL;
4012 LPCWSTR component = NULL;
4013 LPWSTR advertise = NULL;
4014 LPWSTR output = NULL;
4016 UINT rc = ERROR_SUCCESS;
4021 component = MSI_RecordGetString(rec,3);
4022 comp = get_loaded_component(package,component);
4024 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4025 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4026 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4028 TRACE("Skipping: Component %s not scheduled for install\n",
4029 debugstr_w(component));
4031 return ERROR_SUCCESS;
4034 compgroupid = MSI_RecordGetString(rec,1);
4035 qualifier = MSI_RecordGetString(rec,2);
4037 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4038 if (rc != ERROR_SUCCESS)
4041 text = MSI_RecordGetString(rec,4);
4042 feature = MSI_RecordGetString(rec,5);
4044 advertise = create_component_advertise_string(package, comp, feature);
4046 sz = strlenW(advertise);
4049 sz += lstrlenW(text);
4052 sz *= sizeof(WCHAR);
4054 output = msi_alloc_zero(sz);
4055 strcpyW(output,advertise);
4056 msi_free(advertise);
4059 strcatW(output,text);
4061 msi_reg_set_val_multi_str( hkey, qualifier, output );
4068 uirow = MSI_CreateRecord( 2 );
4069 MSI_RecordSetStringW( uirow, 1, compgroupid );
4070 MSI_RecordSetStringW( uirow, 2, qualifier);
4071 ui_actiondata( package, szPublishComponents, uirow);
4072 msiobj_release( &uirow->hdr );
4073 /* FIXME: call ui_progress? */
4079 * At present I am ignorning the advertised components part of this and only
4080 * focusing on the qualified component sets
4082 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4086 static const WCHAR ExecSeqQuery[] =
4087 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4088 '`','P','u','b','l','i','s','h',
4089 'C','o','m','p','o','n','e','n','t','`',0};
4091 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4092 if (rc != ERROR_SUCCESS)
4093 return ERROR_SUCCESS;
4095 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4096 msiobj_release(&view->hdr);
4101 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4103 MSIPACKAGE *package = (MSIPACKAGE*)param;
4106 SC_HANDLE hscm, service = NULL;
4107 LPCWSTR name, disp, comp, depends, pass;
4108 LPCWSTR load_order, serv_name, key;
4109 DWORD serv_type, start_type;
4112 static const WCHAR query[] =
4113 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4114 '`','C','o','m','p','o','n','e','n','t','`',' ',
4115 'W','H','E','R','E',' ',
4116 '`','C','o','m','p','o','n','e','n','t','`',' ',
4117 '=','\'','%','s','\'',0};
4119 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4122 ERR("Failed to open the SC Manager!\n");
4126 start_type = MSI_RecordGetInteger(rec, 5);
4127 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4130 depends = MSI_RecordGetString(rec, 8);
4131 if (depends && *depends)
4132 FIXME("Dependency list unhandled!\n");
4134 name = MSI_RecordGetString(rec, 2);
4135 disp = MSI_RecordGetString(rec, 3);
4136 serv_type = MSI_RecordGetInteger(rec, 4);
4137 err_control = MSI_RecordGetInteger(rec, 6);
4138 load_order = MSI_RecordGetString(rec, 7);
4139 serv_name = MSI_RecordGetString(rec, 9);
4140 pass = MSI_RecordGetString(rec, 10);
4141 comp = MSI_RecordGetString(rec, 12);
4143 /* fetch the service path */
4144 row = MSI_QueryGetRecord(package->db, query, comp);
4147 ERR("Control query failed!\n");
4151 key = MSI_RecordGetString(row, 6);
4152 msiobj_release(&row->hdr);
4154 file = get_loaded_file(package, key);
4157 ERR("Failed to load the service file\n");
4161 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4162 start_type, err_control, file->TargetPath,
4163 load_order, NULL, NULL, serv_name, pass);
4166 if (GetLastError() != ERROR_SERVICE_EXISTS)
4167 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4171 CloseServiceHandle(service);
4172 CloseServiceHandle(hscm);
4174 return ERROR_SUCCESS;
4177 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4181 static const WCHAR ExecSeqQuery[] =
4182 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4183 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4185 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4186 if (rc != ERROR_SUCCESS)
4187 return ERROR_SUCCESS;
4189 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4190 msiobj_release(&view->hdr);
4195 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4196 static LPCWSTR *msi_service_args_to_vector(LPCWSTR name, LPWSTR args, DWORD *numargs)
4202 static const WCHAR separator[] = {'[','~',']',0};
4205 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4210 vector = msi_alloc(sizeof(LPWSTR));
4218 vector[*numargs - 1] = p;
4220 if ((q = strstrW(p, separator)))
4224 vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4235 static MSICOMPONENT *msi_find_component( MSIPACKAGE *package, LPCWSTR component )
4239 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
4241 if (!lstrcmpW(comp->Component, component))
4248 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4250 MSIPACKAGE *package = (MSIPACKAGE *)param;
4252 SC_HANDLE scm, service = NULL;
4253 LPCWSTR name, *vector = NULL;
4255 DWORD event, numargs;
4256 UINT r = ERROR_FUNCTION_FAILED;
4258 comp = msi_find_component(package, MSI_RecordGetString(rec, 6));
4259 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4260 return ERROR_SUCCESS;
4262 name = MSI_RecordGetString(rec, 2);
4263 event = MSI_RecordGetInteger(rec, 3);
4264 args = strdupW(MSI_RecordGetString(rec, 4));
4266 if (!(event & msidbServiceControlEventStart))
4267 return ERROR_SUCCESS;
4269 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4272 ERR("Failed to open the service control manager\n");
4276 service = OpenServiceW(scm, name, SERVICE_START);
4279 ERR("Failed to open service '%s'\n", debugstr_w(name));
4283 vector = msi_service_args_to_vector(name, args, &numargs);
4285 if (!StartServiceW(service, numargs, vector))
4287 ERR("Failed to start service '%s'\n", debugstr_w(name));
4294 CloseServiceHandle(service);
4295 CloseServiceHandle(scm);
4302 static UINT ACTION_StartServices( MSIPACKAGE *package )
4307 static const WCHAR query[] = {
4308 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4309 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4311 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4312 if (rc != ERROR_SUCCESS)
4313 return ERROR_SUCCESS;
4315 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4316 msiobj_release(&view->hdr);
4321 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4325 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4327 if (!lstrcmpW(file->File, filename))
4334 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4336 MSIPACKAGE *package = (MSIPACKAGE*)param;
4337 LPWSTR driver, driver_path, ptr;
4338 WCHAR outpath[MAX_PATH];
4339 MSIFILE *driver_file, *setup_file;
4342 UINT r = ERROR_SUCCESS;
4344 static const WCHAR driver_fmt[] = {
4345 'D','r','i','v','e','r','=','%','s',0};
4346 static const WCHAR setup_fmt[] = {
4347 'S','e','t','u','p','=','%','s',0};
4348 static const WCHAR usage_fmt[] = {
4349 'F','i','l','e','U','s','a','g','e','=','1',0};
4351 desc = MSI_RecordGetString(rec, 3);
4353 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4354 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4356 if (!driver_file || !setup_file)
4358 ERR("ODBC Driver entry not found!\n");
4359 return ERROR_FUNCTION_FAILED;
4362 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4363 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4364 lstrlenW(usage_fmt) + 1;
4365 driver = msi_alloc(len * sizeof(WCHAR));
4367 return ERROR_OUTOFMEMORY;
4370 lstrcpyW(ptr, desc);
4371 ptr += lstrlenW(ptr) + 1;
4373 sprintfW(ptr, driver_fmt, driver_file->FileName);
4374 ptr += lstrlenW(ptr) + 1;
4376 sprintfW(ptr, setup_fmt, setup_file->FileName);
4377 ptr += lstrlenW(ptr) + 1;
4379 lstrcpyW(ptr, usage_fmt);
4380 ptr += lstrlenW(ptr) + 1;
4383 driver_path = strdupW(driver_file->TargetPath);
4384 ptr = strrchrW(driver_path, '\\');
4385 if (ptr) *ptr = '\0';
4387 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4388 NULL, ODBC_INSTALL_COMPLETE, &usage))
4390 ERR("Failed to install SQL driver!\n");
4391 r = ERROR_FUNCTION_FAILED;
4395 msi_free(driver_path);
4400 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4405 static const WCHAR query[] = {
4406 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4407 'O','D','B','C','D','r','i','v','e','r',0 };
4409 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4410 if (rc != ERROR_SUCCESS)
4411 return ERROR_SUCCESS;
4413 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4414 msiobj_release(&view->hdr);
4419 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
4420 LPCSTR action, LPCWSTR table )
4422 static const WCHAR query[] = {
4423 'S','E','L','E','C','T',' ','*',' ',
4424 'F','R','O','M',' ','`','%','s','`',0 };
4425 MSIQUERY *view = NULL;
4429 r = MSI_OpenQuery( package->db, &view, query, table );
4430 if (r == ERROR_SUCCESS)
4432 r = MSI_IterateRecords(view, &count, NULL, package);
4433 msiobj_release(&view->hdr);
4437 FIXME("%s -> %u ignored %s table values\n",
4438 action, count, debugstr_w(table));
4440 return ERROR_SUCCESS;
4443 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
4445 TRACE("%p\n", package);
4446 return ERROR_SUCCESS;
4449 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4451 static const WCHAR table[] =
4452 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
4453 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
4456 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
4458 static const WCHAR table[] = { 'M','o','v','e','F','i','l','e',0 };
4459 return msi_unimplemented_action_stub( package, "MoveFiles", table );
4462 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
4464 static const WCHAR table[] = { 'P','a','t','c','h',0 };
4465 return msi_unimplemented_action_stub( package, "PatchFiles", table );
4468 static UINT ACTION_BindImage( MSIPACKAGE *package )
4470 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
4471 return msi_unimplemented_action_stub( package, "BindImage", table );
4474 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
4476 static const WCHAR table[] = {
4477 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
4478 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
4481 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
4483 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
4484 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
4487 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4489 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
4490 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
4493 static UINT ACTION_StopServices( MSIPACKAGE *package )
4495 static const WCHAR table[] = {
4496 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4497 return msi_unimplemented_action_stub( package, "StopServices", table );
4500 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
4502 static const WCHAR table[] = {
4503 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4504 return msi_unimplemented_action_stub( package, "DeleteServices", table );
4507 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
4509 static const WCHAR table[] = {
4510 'E','n','v','i','r','o','n','m','e','n','t',0 };
4511 return msi_unimplemented_action_stub( package, "WriteEnvironmentStrings", table );
4514 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
4516 static const WCHAR table[] = {
4517 'E','n','v','i','r','o','n','m','e','n','t',0 };
4518 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
4521 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
4523 static const WCHAR table[] = {
4524 'M','s','i','A','s','s','e','m','b','l','y',0 };
4525 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
4528 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
4530 static const WCHAR table[] = {
4531 'M','s','i','A','s','s','e','m','b','l','y',0 };
4532 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
4535 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
4537 static const WCHAR table[] = { 'F','o','n','t',0 };
4538 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
4541 static UINT ACTION_CCPSearch( MSIPACKAGE *package )
4543 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4544 return msi_unimplemented_action_stub( package, "CCPSearch", table );
4547 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
4549 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
4550 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
4553 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
4555 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4556 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
4559 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
4561 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
4562 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
4565 static const struct _actions StandardActions[] = {
4566 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
4567 { szAppSearch, ACTION_AppSearch },
4568 { szBindImage, ACTION_BindImage },
4569 { szCCPSearch, ACTION_CCPSearch},
4570 { szCostFinalize, ACTION_CostFinalize },
4571 { szCostInitialize, ACTION_CostInitialize },
4572 { szCreateFolders, ACTION_CreateFolders },
4573 { szCreateShortcuts, ACTION_CreateShortcuts },
4574 { szDeleteServices, ACTION_DeleteServices },
4575 { szDisableRollback, NULL},
4576 { szDuplicateFiles, ACTION_DuplicateFiles },
4577 { szExecuteAction, ACTION_ExecuteAction },
4578 { szFileCost, ACTION_FileCost },
4579 { szFindRelatedProducts, ACTION_FindRelatedProducts },
4580 { szForceReboot, ACTION_ForceReboot },
4581 { szInstallAdminPackage, NULL},
4582 { szInstallExecute, ACTION_InstallExecute },
4583 { szInstallExecuteAgain, ACTION_InstallExecute },
4584 { szInstallFiles, ACTION_InstallFiles},
4585 { szInstallFinalize, ACTION_InstallFinalize },
4586 { szInstallInitialize, ACTION_InstallInitialize },
4587 { szInstallSFPCatalogFile, NULL},
4588 { szInstallValidate, ACTION_InstallValidate },
4589 { szIsolateComponents, ACTION_IsolateComponents },
4590 { szLaunchConditions, ACTION_LaunchConditions },
4591 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
4592 { szMoveFiles, ACTION_MoveFiles },
4593 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
4594 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
4595 { szInstallODBC, ACTION_InstallODBC },
4596 { szInstallServices, ACTION_InstallServices },
4597 { szPatchFiles, ACTION_PatchFiles },
4598 { szProcessComponents, ACTION_ProcessComponents },
4599 { szPublishComponents, ACTION_PublishComponents },
4600 { szPublishFeatures, ACTION_PublishFeatures },
4601 { szPublishProduct, ACTION_PublishProduct },
4602 { szRegisterClassInfo, ACTION_RegisterClassInfo },
4603 { szRegisterComPlus, ACTION_RegisterComPlus},
4604 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
4605 { szRegisterFonts, ACTION_RegisterFonts },
4606 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
4607 { szRegisterProduct, ACTION_RegisterProduct },
4608 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
4609 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
4610 { szRegisterUser, ACTION_RegisterUser},
4611 { szRemoveDuplicateFiles, NULL},
4612 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
4613 { szRemoveExistingProducts, NULL},
4614 { szRemoveFiles, ACTION_RemoveFiles},
4615 { szRemoveFolders, NULL},
4616 { szRemoveIniValues, ACTION_RemoveIniValues },
4617 { szRemoveODBC, NULL},
4618 { szRemoveRegistryValues, NULL},
4619 { szRemoveShortcuts, NULL},
4620 { szResolveSource, ACTION_ResolveSource},
4621 { szRMCCPSearch, ACTION_RMCCPSearch},
4622 { szScheduleReboot, NULL},
4623 { szSelfRegModules, ACTION_SelfRegModules },
4624 { szSelfUnregModules, ACTION_SelfUnregModules },
4625 { szSetODBCFolders, NULL},
4626 { szStartServices, ACTION_StartServices },
4627 { szStopServices, ACTION_StopServices },
4628 { szUnpublishComponents, NULL},
4629 { szUnpublishFeatures, NULL},
4630 { szUnregisterClassInfo, NULL},
4631 { szUnregisterComPlus, ACTION_UnregisterComPlus},
4632 { szUnregisterExtensionInfo, NULL},
4633 { szUnregisterFonts, ACTION_UnregisterFonts },
4634 { szUnregisterMIMEInfo, NULL},
4635 { szUnregisterProgIdInfo, NULL},
4636 { szUnregisterTypeLibraries, NULL},
4637 { szValidateProductID, NULL},
4638 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
4639 { szWriteIniValues, ACTION_WriteIniValues },
4640 { szWriteRegistryValues, ACTION_WriteRegistryValues},