user32: Fix a message test that would only pass on wine.
[wine.git] / dlls / msi / action.c
blob2efcbbbe0cc9313a96fe2d21b1d40a2be5bf21b3
1 /*
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
22 * Pages I need
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
29 #include <stdarg.h>
31 #define COBJMACROS
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winerror.h"
36 #include "winreg.h"
37 #include "winsvc.h"
38 #include "odbcinst.h"
39 #include "wine/debug.h"
40 #include "msidefs.h"
41 #include "msipriv.h"
42 #include "winuser.h"
43 #include "shlobj.h"
44 #include "wine/unicode.h"
45 #include "winver.h"
47 #define REG_PROGRESS_VALUE 13200
48 #define COMPONENT_PROGRESS_VALUE 24000
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
53 * Prototypes
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',
140 'F','i','l','e',0};
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',
166 'I','n','f','o',0};
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',
214 'I','n','f','o',0};
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',
226 'I','n','f','o',0};
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*);
239 struct _actions {
240 LPCWSTR action;
241 STANDARDACTIONHANDLER handler;
244 static const struct _actions StandardActions[];
247 /********************************************************
248 * helper functions
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};
258 MSIRECORD * row;
260 row = MSI_QueryGetRecord( package->db, Query_t, action );
261 if (!row)
262 return;
263 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
264 msiobj_release(&row->hdr);
267 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
268 UINT rc)
270 MSIRECORD * row;
271 static const WCHAR template_s[]=
272 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
273 '%','s', '.',0};
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',' ',
277 '%','i','.',0};
278 static const WCHAR format[] =
279 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
280 WCHAR message[1024];
281 WCHAR timet[0x100];
283 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
284 if (start)
285 sprintfW(message,template_s,timet,action);
286 else
287 sprintfW(message,template_e,timet,action,rc);
289 row = MSI_CreateRecord(1);
290 MSI_RecordSetStringW(row,1,message);
292 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
293 msiobj_release(&row->hdr);
296 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
298 LPCWSTR ptr,ptr2;
299 BOOL quote;
300 DWORD len;
301 LPWSTR prop = NULL, val = NULL;
303 if (!szCommandLine)
304 return ERROR_SUCCESS;
306 ptr = szCommandLine;
308 while (*ptr)
310 if (*ptr==' ')
312 ptr++;
313 continue;
316 TRACE("Looking at %s\n",debugstr_w(ptr));
318 ptr2 = strchrW(ptr,'=');
319 if (!ptr2)
321 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
322 break;
325 quote = FALSE;
327 len = ptr2-ptr;
328 prop = msi_alloc((len+1)*sizeof(WCHAR));
329 memcpy(prop,ptr,len*sizeof(WCHAR));
330 prop[len]=0;
331 ptr2++;
333 len = 0;
334 ptr = ptr2;
335 while (*ptr && (quote || (!quote && *ptr!=' ')))
337 if (*ptr == '"')
338 quote = !quote;
339 ptr++;
340 len++;
343 if (*ptr2=='"')
345 ptr2++;
346 len -= 2;
348 val = msi_alloc((len+1)*sizeof(WCHAR));
349 memcpy(val,ptr2,len*sizeof(WCHAR));
350 val[len] = 0;
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);
358 msi_free(val);
359 msi_free(prop);
362 return ERROR_SUCCESS;
366 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
368 LPCWSTR pc;
369 LPWSTR p, *ret = NULL;
370 UINT count = 0;
372 if (!str)
373 return ret;
375 /* count the number of substrings */
376 for ( pc = str, count = 0; pc; count++ )
378 pc = strchrW( pc, sep );
379 if (pc)
380 pc++;
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) );
386 if (!ret)
387 return ret;
389 /* copy the string and set the pointers */
390 p = (LPWSTR) &ret[count+1];
391 lstrcpyW( p, str );
392 for( count = 0; (ret[count] = p); count++ )
394 p = strchrW( p, sep );
395 if (p)
396 *p++ = 0;
399 return ret;
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;
406 UINT ret;
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 ) )
414 ret = ERROR_SUCCESS;
415 else
416 ret = ERROR_FUNCTION_FAILED;
418 msi_free( patch_product );
419 msi_free( prod_code );
421 return ret;
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;
429 HRESULT r;
431 TRACE("%p %s\n", package, debugstr_w(name) );
433 if (*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 );
440 if (SUCCEEDED(r))
442 ret = msi_check_transform_applicable( package, stg );
443 if (ret == ERROR_SUCCESS)
444 msi_table_apply_transform( package->db, stg );
445 else
446 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
447 IStorage_Release( stg );
449 else
450 ERR("failed to open substorage %s\n", debugstr_w(name));
452 return ERROR_SUCCESS;
455 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
457 static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
458 LPWSTR guid_list, *guids, product_code;
459 UINT i, ret = ERROR_FUNCTION_FAILED;
461 product_code = msi_dup_property( package, szProdCode );
462 if (!product_code)
464 /* FIXME: the property ProductCode should be written into the DB somewhere */
465 ERR("no product code to check\n");
466 return ERROR_SUCCESS;
469 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
470 guids = msi_split_string( guid_list, ';' );
471 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
473 if (!lstrcmpW( guids[i], product_code ))
474 ret = ERROR_SUCCESS;
476 msi_free( guids );
477 msi_free( guid_list );
478 msi_free( product_code );
480 return ret;
483 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
485 MSISUMMARYINFO *si;
486 LPWSTR str, *substorage;
487 UINT i, r = ERROR_SUCCESS;
489 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
490 if (!si)
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 );
501 msi_free( str );
503 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
505 msiobj_release( &si->hdr );
507 return r;
510 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
512 MSIDATABASE *patch_db = NULL;
513 UINT r;
515 TRACE("%p %s\n", package, debugstr_w( file ) );
517 /* FIXME:
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 ) );
526 return r;
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] );
557 msi_free( patches );
558 msi_free( patch_list );
560 return r;
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] );
577 else
578 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
581 msi_free( xforms );
582 msi_free( xform_list );
584 return r;
587 BOOL ui_sequence_exists( MSIPACKAGE *package )
589 MSIQUERY *view;
590 UINT rc;
592 static const WCHAR ExecSeqQuery [] =
593 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
594 '`','I','n','s','t','a','l','l',
595 'U','I','S','e','q','u','e','n','c','e','`',
596 ' ','W','H','E','R','E',' ',
597 '`','S','e','q','u','e','n','c','e','`',' ',
598 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
599 '`','S','e','q','u','e','n','c','e','`',0};
601 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
602 if (rc == ERROR_SUCCESS)
604 msiobj_release(&view->hdr);
605 return TRUE;
608 return FALSE;
611 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
613 LPWSTR p, db;
614 LPWSTR source, check;
615 DWORD len;
617 static const WCHAR szOriginalDatabase[] =
618 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
620 db = msi_dup_property( package, szOriginalDatabase );
621 if (!db)
622 return ERROR_OUTOFMEMORY;
624 p = strrchrW( db, '\\' );
625 if (!p)
627 p = strrchrW( db, '/' );
628 if (!p)
630 msi_free(db);
631 return ERROR_SUCCESS;
635 len = p - db + 2;
636 source = msi_alloc( len * sizeof(WCHAR) );
637 lstrcpynW( source, db, len );
639 check = msi_dup_property( package, cszSourceDir );
640 if (!check || replace)
641 MSI_SetPropertyW( package, cszSourceDir, source );
643 msi_free( check );
645 check = msi_dup_property( package, cszSOURCEDIR );
646 if (!check || replace)
647 MSI_SetPropertyW( package, cszSOURCEDIR, source );
649 msi_free( check );
650 msi_free( source );
651 msi_free( db );
653 return ERROR_SUCCESS;
656 /****************************************************
657 * TOP level entry points
658 *****************************************************/
660 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
661 LPCWSTR szCommandLine )
663 UINT rc;
664 BOOL ui = FALSE, ui_exists;
665 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
666 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
667 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
669 MSI_SetPropertyW(package, szAction, szInstall);
671 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
673 package->script->InWhatSequence = SEQUENCE_INSTALL;
675 if (szPackagePath)
677 LPWSTR p, dir;
678 LPCWSTR file;
680 dir = strdupW(szPackagePath);
681 p = strrchrW(dir, '\\');
682 if (p)
684 *(++p) = 0;
685 file = szPackagePath + (p - dir);
687 else
689 msi_free(dir);
690 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
691 GetCurrentDirectoryW(MAX_PATH, dir);
692 lstrcatW(dir, cszbs);
693 file = szPackagePath;
696 msi_free( package->PackagePath );
697 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
698 if (!package->PackagePath)
700 msi_free(dir);
701 return ERROR_OUTOFMEMORY;
704 lstrcpyW(package->PackagePath, dir);
705 lstrcatW(package->PackagePath, file);
706 msi_free(dir);
708 msi_set_sourcedir_props(package, FALSE);
711 msi_parse_command_line( package, szCommandLine );
713 msi_apply_transforms( package );
714 msi_apply_patches( package );
716 /* properties may have been added by a transform */
717 msi_clone_properties( package );
719 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
721 package->script->InWhatSequence |= SEQUENCE_UI;
722 rc = ACTION_ProcessUISequence(package);
723 ui = TRUE;
724 ui_exists = ui_sequence_exists(package);
725 if (rc == ERROR_SUCCESS || !ui_exists)
727 package->script->InWhatSequence |= SEQUENCE_EXEC;
728 rc = ACTION_ProcessExecSequence(package,ui_exists);
731 else
732 rc = ACTION_ProcessExecSequence(package,FALSE);
734 package->script->CurrentlyScripting= FALSE;
736 /* process the ending type action */
737 if (rc == ERROR_SUCCESS)
738 ACTION_PerformActionSequence(package,-1,ui);
739 else if (rc == ERROR_INSTALL_USEREXIT)
740 ACTION_PerformActionSequence(package,-2,ui);
741 else if (rc == ERROR_INSTALL_SUSPEND)
742 ACTION_PerformActionSequence(package,-4,ui);
743 else /* failed */
744 ACTION_PerformActionSequence(package,-3,ui);
746 /* finish up running custom actions */
747 ACTION_FinishCustomActions(package);
749 return rc;
752 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
754 UINT rc = ERROR_SUCCESS;
755 MSIRECORD * row = 0;
756 static const WCHAR ExecSeqQuery[] =
757 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
758 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
759 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
760 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
762 static const WCHAR UISeqQuery[] =
763 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
764 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
765 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
766 ' ', '=',' ','%','i',0};
768 if (UI)
769 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
770 else
771 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
773 if (row)
775 LPCWSTR action, cond;
777 TRACE("Running the actions\n");
779 /* check conditions */
780 cond = MSI_RecordGetString(row,2);
782 /* this is a hack to skip errors in the condition code */
783 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
784 goto end;
786 action = MSI_RecordGetString(row,1);
787 if (!action)
789 ERR("failed to fetch action\n");
790 rc = ERROR_FUNCTION_FAILED;
791 goto end;
794 if (UI)
795 rc = ACTION_PerformUIAction(package,action,-1);
796 else
797 rc = ACTION_PerformAction(package,action,-1,FALSE);
798 end:
799 msiobj_release(&row->hdr);
801 else
802 rc = ERROR_SUCCESS;
804 return rc;
807 typedef struct {
808 MSIPACKAGE* package;
809 BOOL UI;
810 } iterate_action_param;
812 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
814 iterate_action_param *iap= (iterate_action_param*)param;
815 UINT rc;
816 LPCWSTR cond, action;
818 action = MSI_RecordGetString(row,1);
819 if (!action)
821 ERR("Error is retrieving action name\n");
822 return ERROR_FUNCTION_FAILED;
825 /* check conditions */
826 cond = MSI_RecordGetString(row,2);
828 /* this is a hack to skip errors in the condition code */
829 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
831 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
832 return ERROR_SUCCESS;
835 if (iap->UI)
836 rc = ACTION_PerformUIAction(iap->package,action,-1);
837 else
838 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
840 msi_dialog_check_messages( NULL );
842 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
843 rc = iap->package->CurrentInstallState;
845 if (rc == ERROR_FUNCTION_NOT_CALLED)
846 rc = ERROR_SUCCESS;
848 if (rc != ERROR_SUCCESS)
849 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
851 return rc;
854 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
856 MSIQUERY * view;
857 UINT r;
858 static const WCHAR query[] =
859 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
860 '`','%','s','`',
861 ' ','W','H','E','R','E',' ',
862 '`','S','e','q','u','e','n','c','e','`',' ',
863 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
864 '`','S','e','q','u','e','n','c','e','`',0};
865 iterate_action_param iap;
868 * FIXME: probably should be checking UILevel in the
869 * ACTION_PerformUIAction/ACTION_PerformAction
870 * rather than saving the UI level here. Those
871 * two functions can be merged too.
873 iap.package = package;
874 iap.UI = TRUE;
876 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
878 r = MSI_OpenQuery( package->db, &view, query, szTable );
879 if (r == ERROR_SUCCESS)
881 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
882 msiobj_release(&view->hdr);
885 return r;
888 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
890 MSIQUERY * view;
891 UINT rc;
892 static const WCHAR ExecSeqQuery[] =
893 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
894 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
895 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
896 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
897 'O','R','D','E','R',' ', 'B','Y',' ',
898 '`','S','e','q','u','e','n','c','e','`',0 };
899 MSIRECORD * row = 0;
900 static const WCHAR IVQuery[] =
901 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
902 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
903 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
904 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
905 ' ','\'', 'I','n','s','t','a','l','l',
906 'V','a','l','i','d','a','t','e','\'', 0};
907 INT seq = 0;
908 iterate_action_param iap;
910 iap.package = package;
911 iap.UI = FALSE;
913 if (package->script->ExecuteSequenceRun)
915 TRACE("Execute Sequence already Run\n");
916 return ERROR_SUCCESS;
919 package->script->ExecuteSequenceRun = TRUE;
921 /* get the sequence number */
922 if (UIran)
924 row = MSI_QueryGetRecord(package->db, IVQuery);
925 if( !row )
926 return ERROR_FUNCTION_FAILED;
927 seq = MSI_RecordGetInteger(row,1);
928 msiobj_release(&row->hdr);
931 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
932 if (rc == ERROR_SUCCESS)
934 TRACE("Running the actions\n");
936 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
937 msiobj_release(&view->hdr);
940 return rc;
943 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
945 MSIQUERY * view;
946 UINT rc;
947 static const WCHAR ExecSeqQuery [] =
948 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
949 '`','I','n','s','t','a','l','l',
950 'U','I','S','e','q','u','e','n','c','e','`',
951 ' ','W','H','E','R','E',' ',
952 '`','S','e','q','u','e','n','c','e','`',' ',
953 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
954 '`','S','e','q','u','e','n','c','e','`',0};
955 iterate_action_param iap;
957 iap.package = package;
958 iap.UI = TRUE;
960 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
962 if (rc == ERROR_SUCCESS)
964 TRACE("Running the actions\n");
966 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
967 msiobj_release(&view->hdr);
970 return rc;
973 /********************************************************
974 * ACTION helper functions and functions that perform the actions
975 *******************************************************/
976 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
977 UINT* rc, BOOL force )
979 BOOL ret = FALSE;
980 BOOL run = force;
981 int i;
983 if (!run && !package->script->CurrentlyScripting)
984 run = TRUE;
986 if (!run)
988 if (strcmpW(action,szInstallFinalize) == 0 ||
989 strcmpW(action,szInstallExecute) == 0 ||
990 strcmpW(action,szInstallExecuteAgain) == 0)
991 run = TRUE;
994 i = 0;
995 while (StandardActions[i].action != NULL)
997 if (strcmpW(StandardActions[i].action, action)==0)
999 if (!run)
1001 ui_actioninfo(package, action, TRUE, 0);
1002 *rc = schedule_action(package,INSTALL_SCRIPT,action);
1003 ui_actioninfo(package, action, FALSE, *rc);
1005 else
1007 ui_actionstart(package, action);
1008 if (StandardActions[i].handler)
1010 *rc = StandardActions[i].handler(package);
1012 else
1014 FIXME("unhandled standard action %s\n",debugstr_w(action));
1015 *rc = ERROR_SUCCESS;
1018 ret = TRUE;
1019 break;
1021 i++;
1023 return ret;
1026 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1027 UINT* rc, UINT script, BOOL force )
1029 BOOL ret=FALSE;
1030 UINT arc;
1032 arc = ACTION_CustomAction(package, action, script, force);
1034 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1036 *rc = arc;
1037 ret = TRUE;
1039 return ret;
1043 * A lot of actions are really important even if they don't do anything
1044 * explicit... Lots of properties are set at the beginning of the installation
1045 * CostFinalize does a bunch of work to translate the directories and such
1047 * But until I get write access to the database that is hard, so I am going to
1048 * hack it to see if I can get something to run.
1050 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1052 UINT rc = ERROR_SUCCESS;
1053 BOOL handled;
1055 TRACE("Performing action (%s)\n",debugstr_w(action));
1057 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1059 if (!handled)
1060 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1062 if (!handled)
1064 FIXME("unhandled msi action %s\n",debugstr_w(action));
1065 rc = ERROR_FUNCTION_NOT_CALLED;
1068 return rc;
1071 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1073 UINT rc = ERROR_SUCCESS;
1074 BOOL handled = FALSE;
1076 TRACE("Performing action (%s)\n",debugstr_w(action));
1078 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1080 if (!handled)
1081 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1083 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1084 handled = TRUE;
1086 if (!handled)
1088 FIXME("unhandled msi action %s\n",debugstr_w(action));
1089 rc = ERROR_FUNCTION_NOT_CALLED;
1092 return rc;
1097 * Actual Action Handlers
1100 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1102 MSIPACKAGE *package = (MSIPACKAGE*)param;
1103 LPCWSTR dir;
1104 LPWSTR full_path;
1105 MSIRECORD *uirow;
1106 MSIFOLDER *folder;
1108 dir = MSI_RecordGetString(row,1);
1109 if (!dir)
1111 ERR("Unable to get folder id\n");
1112 return ERROR_SUCCESS;
1115 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1116 if (!full_path)
1118 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1119 return ERROR_SUCCESS;
1122 TRACE("Folder is %s\n",debugstr_w(full_path));
1124 /* UI stuff */
1125 uirow = MSI_CreateRecord(1);
1126 MSI_RecordSetStringW(uirow,1,full_path);
1127 ui_actiondata(package,szCreateFolders,uirow);
1128 msiobj_release( &uirow->hdr );
1130 if (folder->State == 0)
1131 create_full_pathW(full_path);
1133 folder->State = 3;
1135 msi_free(full_path);
1136 return ERROR_SUCCESS;
1139 /* FIXME: probably should merge this with the above function */
1140 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1142 UINT rc = ERROR_SUCCESS;
1143 MSIFOLDER *folder;
1144 LPWSTR install_path;
1146 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1147 if (!install_path)
1148 return ERROR_FUNCTION_FAILED;
1150 /* create the path */
1151 if (folder->State == 0)
1153 create_full_pathW(install_path);
1154 folder->State = 2;
1156 msi_free(install_path);
1158 return rc;
1161 UINT msi_create_component_directories( MSIPACKAGE *package )
1163 MSICOMPONENT *comp;
1165 /* create all the folders required by the components are going to install */
1166 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1168 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1169 continue;
1170 msi_create_directory( package, comp->Directory );
1173 return ERROR_SUCCESS;
1177 * Also we cannot enable/disable components either, so for now I am just going
1178 * to do all the directories for all the components.
1180 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1182 static const WCHAR ExecSeqQuery[] =
1183 {'S','E','L','E','C','T',' ',
1184 '`','D','i','r','e','c','t','o','r','y','_','`',
1185 ' ','F','R','O','M',' ',
1186 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1187 UINT rc;
1188 MSIQUERY *view;
1190 /* create all the empty folders specified in the CreateFolder table */
1191 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1192 if (rc != ERROR_SUCCESS)
1193 return ERROR_SUCCESS;
1195 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1196 msiobj_release(&view->hdr);
1198 msi_create_component_directories( package );
1200 return rc;
1203 static UINT load_component( MSIRECORD *row, LPVOID param )
1205 MSIPACKAGE *package = param;
1206 MSICOMPONENT *comp;
1208 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1209 if (!comp)
1210 return ERROR_FUNCTION_FAILED;
1212 list_add_tail( &package->components, &comp->entry );
1214 /* fill in the data */
1215 comp->Component = msi_dup_record_field( row, 1 );
1217 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1219 comp->ComponentId = msi_dup_record_field( row, 2 );
1220 comp->Directory = msi_dup_record_field( row, 3 );
1221 comp->Attributes = MSI_RecordGetInteger(row,4);
1222 comp->Condition = msi_dup_record_field( row, 5 );
1223 comp->KeyPath = msi_dup_record_field( row, 6 );
1225 comp->Installed = INSTALLSTATE_UNKNOWN;
1226 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1228 return ERROR_SUCCESS;
1231 static UINT load_all_components( MSIPACKAGE *package )
1233 static const WCHAR query[] = {
1234 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1235 '`','C','o','m','p','o','n','e','n','t','`',0 };
1236 MSIQUERY *view;
1237 UINT r;
1239 if (!list_empty(&package->components))
1240 return ERROR_SUCCESS;
1242 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1243 if (r != ERROR_SUCCESS)
1244 return r;
1246 r = MSI_IterateRecords(view, NULL, load_component, package);
1247 msiobj_release(&view->hdr);
1248 return r;
1251 typedef struct {
1252 MSIPACKAGE *package;
1253 MSIFEATURE *feature;
1254 } _ilfs;
1256 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1258 ComponentList *cl;
1260 cl = msi_alloc( sizeof (*cl) );
1261 if ( !cl )
1262 return ERROR_NOT_ENOUGH_MEMORY;
1263 cl->component = comp;
1264 list_add_tail( &feature->Components, &cl->entry );
1266 return ERROR_SUCCESS;
1269 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1271 FeatureList *fl;
1273 fl = msi_alloc( sizeof(*fl) );
1274 if ( !fl )
1275 return ERROR_NOT_ENOUGH_MEMORY;
1276 fl->feature = child;
1277 list_add_tail( &parent->Children, &fl->entry );
1279 return ERROR_SUCCESS;
1282 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1284 _ilfs* ilfs= (_ilfs*)param;
1285 LPCWSTR component;
1286 MSICOMPONENT *comp;
1288 component = MSI_RecordGetString(row,1);
1290 /* check to see if the component is already loaded */
1291 comp = get_loaded_component( ilfs->package, component );
1292 if (!comp)
1294 ERR("unknown component %s\n", debugstr_w(component));
1295 return ERROR_FUNCTION_FAILED;
1298 add_feature_component( ilfs->feature, comp );
1299 comp->Enabled = TRUE;
1301 return ERROR_SUCCESS;
1304 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1306 MSIFEATURE *feature;
1308 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1310 if ( !lstrcmpW( feature->Feature, name ) )
1311 return feature;
1314 return NULL;
1317 static UINT load_feature(MSIRECORD * row, LPVOID param)
1319 MSIPACKAGE* package = (MSIPACKAGE*)param;
1320 MSIFEATURE* feature;
1321 static const WCHAR Query1[] =
1322 {'S','E','L','E','C','T',' ',
1323 '`','C','o','m','p','o','n','e','n','t','_','`',
1324 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1325 'C','o','m','p','o','n','e','n','t','s','`',' ',
1326 'W','H','E','R','E',' ',
1327 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1328 MSIQUERY * view;
1329 UINT rc;
1330 _ilfs ilfs;
1332 /* fill in the data */
1334 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1335 if (!feature)
1336 return ERROR_NOT_ENOUGH_MEMORY;
1338 list_init( &feature->Children );
1339 list_init( &feature->Components );
1341 feature->Feature = msi_dup_record_field( row, 1 );
1343 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1345 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1346 feature->Title = msi_dup_record_field( row, 3 );
1347 feature->Description = msi_dup_record_field( row, 4 );
1349 if (!MSI_RecordIsNull(row,5))
1350 feature->Display = MSI_RecordGetInteger(row,5);
1352 feature->Level= MSI_RecordGetInteger(row,6);
1353 feature->Directory = msi_dup_record_field( row, 7 );
1354 feature->Attributes = MSI_RecordGetInteger(row,8);
1356 feature->Installed = INSTALLSTATE_UNKNOWN;
1357 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1359 list_add_tail( &package->features, &feature->entry );
1361 /* load feature components */
1363 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1364 if (rc != ERROR_SUCCESS)
1365 return ERROR_SUCCESS;
1367 ilfs.package = package;
1368 ilfs.feature = feature;
1370 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1371 msiobj_release(&view->hdr);
1373 return ERROR_SUCCESS;
1376 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1378 MSIPACKAGE* package = (MSIPACKAGE*)param;
1379 MSIFEATURE *parent, *child;
1381 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1382 if (!child)
1383 return ERROR_FUNCTION_FAILED;
1385 if (!child->Feature_Parent)
1386 return ERROR_SUCCESS;
1388 parent = find_feature_by_name( package, child->Feature_Parent );
1389 if (!parent)
1390 return ERROR_FUNCTION_FAILED;
1392 add_feature_child( parent, child );
1393 return ERROR_SUCCESS;
1396 static UINT load_all_features( MSIPACKAGE *package )
1398 static const WCHAR query[] = {
1399 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1400 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1401 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1402 MSIQUERY *view;
1403 UINT r;
1405 if (!list_empty(&package->features))
1406 return ERROR_SUCCESS;
1408 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1409 if (r != ERROR_SUCCESS)
1410 return r;
1412 r = MSI_IterateRecords( view, NULL, load_feature, package );
1413 if (r != ERROR_SUCCESS)
1414 return r;
1416 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1417 msiobj_release( &view->hdr );
1419 return r;
1422 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1424 if (!p)
1425 return p;
1426 p = strchrW(p, ch);
1427 if (!p)
1428 return p;
1429 *p = 0;
1430 return p+1;
1433 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1435 static const WCHAR query[] = {
1436 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1437 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1438 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1439 MSIQUERY *view = NULL;
1440 MSIRECORD *row = NULL;
1441 UINT r;
1443 TRACE("%s\n", debugstr_w(file->File));
1445 r = MSI_OpenQuery(package->db, &view, query, file->File);
1446 if (r != ERROR_SUCCESS)
1447 goto done;
1449 r = MSI_ViewExecute(view, NULL);
1450 if (r != ERROR_SUCCESS)
1451 goto done;
1453 r = MSI_ViewFetch(view, &row);
1454 if (r != ERROR_SUCCESS)
1455 goto done;
1457 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1458 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1459 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1460 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1461 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1463 done:
1464 if (view) msiobj_release(&view->hdr);
1465 if (row) msiobj_release(&row->hdr);
1466 return r;
1469 static UINT load_file(MSIRECORD *row, LPVOID param)
1471 MSIPACKAGE* package = (MSIPACKAGE*)param;
1472 LPCWSTR component;
1473 MSIFILE *file;
1475 /* fill in the data */
1477 file = msi_alloc_zero( sizeof (MSIFILE) );
1478 if (!file)
1479 return ERROR_NOT_ENOUGH_MEMORY;
1481 file->File = msi_dup_record_field( row, 1 );
1483 component = MSI_RecordGetString( row, 2 );
1484 file->Component = get_loaded_component( package, component );
1486 if (!file->Component)
1487 ERR("Unfound Component %s\n",debugstr_w(component));
1489 file->FileName = msi_dup_record_field( row, 3 );
1490 reduce_to_longfilename( file->FileName );
1492 file->ShortName = msi_dup_record_field( row, 3 );
1493 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1495 file->FileSize = MSI_RecordGetInteger( row, 4 );
1496 file->Version = msi_dup_record_field( row, 5 );
1497 file->Language = msi_dup_record_field( row, 6 );
1498 file->Attributes = MSI_RecordGetInteger( row, 7 );
1499 file->Sequence = MSI_RecordGetInteger( row, 8 );
1501 file->state = msifs_invalid;
1503 /* if the compressed bits are not set in the file attributes,
1504 * then read the information from the package word count property
1506 if (file->Attributes & msidbFileAttributesCompressed)
1508 file->IsCompressed = TRUE;
1510 else if (file->Attributes & msidbFileAttributesNoncompressed)
1512 file->IsCompressed = FALSE;
1514 else
1516 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1519 load_file_hash(package, file);
1521 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1523 list_add_tail( &package->files, &file->entry );
1525 return ERROR_SUCCESS;
1528 static UINT load_all_files(MSIPACKAGE *package)
1530 MSIQUERY * view;
1531 UINT rc;
1532 static const WCHAR Query[] =
1533 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1534 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1535 '`','S','e','q','u','e','n','c','e','`', 0};
1537 if (!list_empty(&package->files))
1538 return ERROR_SUCCESS;
1540 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1541 if (rc != ERROR_SUCCESS)
1542 return ERROR_SUCCESS;
1544 rc = MSI_IterateRecords(view, NULL, load_file, package);
1545 msiobj_release(&view->hdr);
1547 return ERROR_SUCCESS;
1550 static UINT load_folder( MSIRECORD *row, LPVOID param )
1552 MSIPACKAGE *package = param;
1553 static const WCHAR szDot[] = { '.',0 };
1554 static WCHAR szEmpty[] = { 0 };
1555 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1556 MSIFOLDER *folder;
1558 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1559 if (!folder)
1560 return ERROR_NOT_ENOUGH_MEMORY;
1562 folder->Directory = msi_dup_record_field( row, 1 );
1564 TRACE("%s\n", debugstr_w(folder->Directory));
1566 p = msi_dup_record_field(row, 3);
1568 /* split src and target dir */
1569 tgt_short = p;
1570 src_short = folder_split_path( p, ':' );
1572 /* split the long and short paths */
1573 tgt_long = folder_split_path( tgt_short, '|' );
1574 src_long = folder_split_path( src_short, '|' );
1576 /* check for no-op dirs */
1577 if (!lstrcmpW(szDot, tgt_short))
1578 tgt_short = szEmpty;
1579 if (!lstrcmpW(szDot, src_short))
1580 src_short = szEmpty;
1582 if (!tgt_long)
1583 tgt_long = tgt_short;
1585 if (!src_short) {
1586 src_short = tgt_short;
1587 src_long = tgt_long;
1590 if (!src_long)
1591 src_long = src_short;
1593 /* FIXME: use the target short path too */
1594 folder->TargetDefault = strdupW(tgt_long);
1595 folder->SourceShortPath = strdupW(src_short);
1596 folder->SourceLongPath = strdupW(src_long);
1597 msi_free(p);
1599 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1600 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1601 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1603 folder->Parent = msi_dup_record_field( row, 2 );
1605 folder->Property = msi_dup_property( package, folder->Directory );
1607 list_add_tail( &package->folders, &folder->entry );
1609 TRACE("returning %p\n", folder);
1611 return ERROR_SUCCESS;
1614 static UINT load_all_folders( MSIPACKAGE *package )
1616 static const WCHAR query[] = {
1617 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1618 '`','D','i','r','e','c','t','o','r','y','`',0 };
1619 MSIQUERY *view;
1620 UINT r;
1622 if (!list_empty(&package->folders))
1623 return ERROR_SUCCESS;
1625 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1626 if (r != ERROR_SUCCESS)
1627 return r;
1629 r = MSI_IterateRecords(view, NULL, load_folder, package);
1630 msiobj_release(&view->hdr);
1631 return r;
1635 * I am not doing any of the costing functionality yet.
1636 * Mostly looking at doing the Component and Feature loading
1638 * The native MSI does A LOT of modification to tables here. Mostly adding
1639 * a lot of temporary columns to the Feature and Component tables.
1641 * note: Native msi also tracks the short filename. But I am only going to
1642 * track the long ones. Also looking at this directory table
1643 * it appears that the directory table does not get the parents
1644 * resolved base on property only based on their entries in the
1645 * directory table.
1647 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1649 static const WCHAR szCosting[] =
1650 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1651 static const WCHAR szZero[] = { '0', 0 };
1653 MSI_SetPropertyW(package, szCosting, szZero);
1654 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1656 load_all_components( package );
1657 load_all_features( package );
1658 load_all_files( package );
1659 load_all_folders( package );
1661 return ERROR_SUCCESS;
1664 static UINT execute_script(MSIPACKAGE *package, UINT script )
1666 UINT i;
1667 UINT rc = ERROR_SUCCESS;
1669 TRACE("Executing Script %i\n",script);
1671 if (!package->script)
1673 ERR("no script!\n");
1674 return ERROR_FUNCTION_FAILED;
1677 for (i = 0; i < package->script->ActionCount[script]; i++)
1679 LPWSTR action;
1680 action = package->script->Actions[script][i];
1681 ui_actionstart(package, action);
1682 TRACE("Executing Action (%s)\n",debugstr_w(action));
1683 rc = ACTION_PerformAction(package, action, script, TRUE);
1684 if (rc != ERROR_SUCCESS)
1685 break;
1687 msi_free_action_script(package, script);
1688 return rc;
1691 static UINT ACTION_FileCost(MSIPACKAGE *package)
1693 return ERROR_SUCCESS;
1696 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1698 MSICOMPONENT *comp;
1700 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1702 INSTALLSTATE res;
1704 if (!comp->ComponentId)
1705 continue;
1707 res = MsiGetComponentPathW( package->ProductCode,
1708 comp->ComponentId, NULL, NULL);
1709 if (res < 0)
1710 res = INSTALLSTATE_ABSENT;
1711 comp->Installed = res;
1715 /* scan for and update current install states */
1716 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1718 MSICOMPONENT *comp;
1719 MSIFEATURE *feature;
1721 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1723 ComponentList *cl;
1724 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1726 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1728 comp= cl->component;
1730 if (!comp->ComponentId)
1732 res = INSTALLSTATE_ABSENT;
1733 break;
1736 if (res == INSTALLSTATE_ABSENT)
1737 res = comp->Installed;
1738 else
1740 if (res == comp->Installed)
1741 continue;
1743 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1744 res != INSTALLSTATE_SOURCE)
1746 res = INSTALLSTATE_INCOMPLETE;
1750 feature->Installed = res;
1754 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1755 INSTALLSTATE state)
1757 static const WCHAR all[]={'A','L','L',0};
1758 LPWSTR override;
1759 MSIFEATURE *feature;
1761 override = msi_dup_property( package, property );
1762 if (!override)
1763 return FALSE;
1765 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1767 if (strcmpiW(override,all)==0)
1768 msi_feature_set_state( feature, state );
1769 else
1771 LPWSTR ptr = override;
1772 LPWSTR ptr2 = strchrW(override,',');
1774 while (ptr)
1776 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1777 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1779 msi_feature_set_state( feature, state );
1780 break;
1782 if (ptr2)
1784 ptr=ptr2+1;
1785 ptr2 = strchrW(ptr,',');
1787 else
1788 break;
1792 msi_free(override);
1794 return TRUE;
1797 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1799 int install_level;
1800 static const WCHAR szlevel[] =
1801 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1802 static const WCHAR szAddLocal[] =
1803 {'A','D','D','L','O','C','A','L',0};
1804 static const WCHAR szAddSource[] =
1805 {'A','D','D','S','O','U','R','C','E',0};
1806 static const WCHAR szRemove[] =
1807 {'R','E','M','O','V','E',0};
1808 static const WCHAR szReinstall[] =
1809 {'R','E','I','N','S','T','A','L','L',0};
1810 BOOL override = FALSE;
1811 MSICOMPONENT* component;
1812 MSIFEATURE *feature;
1815 /* I do not know if this is where it should happen.. but */
1817 TRACE("Checking Install Level\n");
1819 install_level = msi_get_property_int( package, szlevel, 1 );
1821 /* ok here is the _real_ rub
1822 * all these activation/deactivation things happen in order and things
1823 * later on the list override things earlier on the list.
1824 * 1) INSTALLLEVEL processing
1825 * 2) ADDLOCAL
1826 * 3) REMOVE
1827 * 4) ADDSOURCE
1828 * 5) ADDDEFAULT
1829 * 6) REINSTALL
1830 * 7) COMPADDLOCAL
1831 * 8) COMPADDSOURCE
1832 * 9) FILEADDLOCAL
1833 * 10) FILEADDSOURCE
1834 * 11) FILEADDDEFAULT
1835 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1836 * ignored for all the features. seems strange, especially since it is not
1837 * documented anywhere, but it is how it works.
1839 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1840 * REMOVE are the big ones, since we don't handle administrative installs
1841 * yet anyway.
1843 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1844 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1845 override |= process_state_property(package,szAddSource,INSTALLSTATE_SOURCE);
1846 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1848 if (!override)
1850 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1852 BOOL feature_state = ((feature->Level > 0) &&
1853 (feature->Level <= install_level));
1855 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1857 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1858 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1859 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1860 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1861 else
1862 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1866 /* disable child features of unselected parent features */
1867 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1869 FeatureList *fl;
1871 if (feature->Level > 0 && feature->Level <= install_level)
1872 continue;
1874 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1875 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1878 else
1880 /* set the Preselected Property */
1881 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1882 static const WCHAR szOne[] = { '1', 0 };
1884 MSI_SetPropertyW(package,szPreselected,szOne);
1888 * now we want to enable or disable components base on feature
1891 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1893 ComponentList *cl;
1895 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1896 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1898 /* features with components that have compressed files are made local */
1899 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1901 if (cl->component->Enabled &&
1902 cl->component->ForceLocalState &&
1903 feature->Action == INSTALLSTATE_SOURCE)
1905 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1906 break;
1910 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1912 component = cl->component;
1914 if (!component->Enabled)
1915 continue;
1917 switch (feature->Action)
1919 case INSTALLSTATE_ABSENT:
1920 component->anyAbsent = 1;
1921 break;
1922 case INSTALLSTATE_ADVERTISED:
1923 component->hasAdvertiseFeature = 1;
1924 break;
1925 case INSTALLSTATE_SOURCE:
1926 component->hasSourceFeature = 1;
1927 break;
1928 case INSTALLSTATE_LOCAL:
1929 component->hasLocalFeature = 1;
1930 break;
1931 case INSTALLSTATE_DEFAULT:
1932 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1933 component->hasAdvertiseFeature = 1;
1934 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1935 component->hasSourceFeature = 1;
1936 else
1937 component->hasLocalFeature = 1;
1938 break;
1939 default:
1940 break;
1945 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1947 /* if the component isn't enabled, leave it alone */
1948 if (!component->Enabled)
1949 continue;
1951 /* check if it's local or source */
1952 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1953 (component->hasLocalFeature || component->hasSourceFeature))
1955 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1956 !component->ForceLocalState)
1957 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1958 else
1959 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1960 continue;
1963 /* if any feature is local, the component must be local too */
1964 if (component->hasLocalFeature)
1966 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1967 continue;
1970 if (component->hasSourceFeature)
1972 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1973 continue;
1976 if (component->hasAdvertiseFeature)
1978 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1979 continue;
1982 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1983 if (component->anyAbsent)
1984 msi_component_set_state(component, INSTALLSTATE_ABSENT);
1987 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1989 if (component->Action == INSTALLSTATE_DEFAULT)
1991 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1992 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1995 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1996 debugstr_w(component->Component), component->Installed, component->Action);
2000 return ERROR_SUCCESS;
2003 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
2005 MSIPACKAGE *package = (MSIPACKAGE*)param;
2006 LPCWSTR name;
2007 LPWSTR path;
2008 MSIFOLDER *f;
2010 name = MSI_RecordGetString(row,1);
2012 f = get_loaded_folder(package, name);
2013 if (!f) return ERROR_SUCCESS;
2015 /* reset the ResolvedTarget */
2016 msi_free(f->ResolvedTarget);
2017 f->ResolvedTarget = NULL;
2019 /* This helper function now does ALL the work */
2020 TRACE("Dir %s ...\n",debugstr_w(name));
2021 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2022 TRACE("resolves to %s\n",debugstr_w(path));
2023 msi_free(path);
2025 return ERROR_SUCCESS;
2028 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2030 MSIPACKAGE *package = (MSIPACKAGE*)param;
2031 LPCWSTR name;
2032 MSIFEATURE *feature;
2034 name = MSI_RecordGetString( row, 1 );
2036 feature = get_loaded_feature( package, name );
2037 if (!feature)
2038 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2039 else
2041 LPCWSTR Condition;
2042 Condition = MSI_RecordGetString(row,3);
2044 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2046 int level = MSI_RecordGetInteger(row,2);
2047 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
2048 feature->Level = level;
2051 return ERROR_SUCCESS;
2054 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2056 static const WCHAR name_fmt[] =
2057 {'%','u','.','%','u','.','%','u','.','%','u',0};
2058 static WCHAR name[] = {'\\',0};
2059 VS_FIXEDFILEINFO *lpVer;
2060 WCHAR filever[0x100];
2061 LPVOID version;
2062 DWORD versize;
2063 DWORD handle;
2064 UINT sz;
2066 TRACE("%s\n", debugstr_w(filename));
2068 versize = GetFileVersionInfoSizeW( filename, &handle );
2069 if (!versize)
2070 return NULL;
2072 version = msi_alloc( versize );
2073 GetFileVersionInfoW( filename, 0, versize, version );
2075 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2077 msi_free( version );
2078 return NULL;
2081 sprintfW( filever, name_fmt,
2082 HIWORD(lpVer->dwFileVersionMS),
2083 LOWORD(lpVer->dwFileVersionMS),
2084 HIWORD(lpVer->dwFileVersionLS),
2085 LOWORD(lpVer->dwFileVersionLS));
2087 msi_free( version );
2089 return strdupW( filever );
2092 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2094 LPWSTR file_version;
2095 MSIFILE *file;
2097 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2099 MSICOMPONENT* comp = file->Component;
2100 LPWSTR p;
2102 if (!comp)
2103 continue;
2105 if (file->IsCompressed)
2106 comp->ForceLocalState = TRUE;
2108 /* calculate target */
2109 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2111 msi_free(file->TargetPath);
2113 TRACE("file %s is named %s\n",
2114 debugstr_w(file->File), debugstr_w(file->FileName));
2116 file->TargetPath = build_directory_name(2, p, file->FileName);
2118 msi_free(p);
2120 TRACE("file %s resolves to %s\n",
2121 debugstr_w(file->File), debugstr_w(file->TargetPath));
2123 /* don't check files of components that aren't installed */
2124 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2125 comp->Installed == INSTALLSTATE_ABSENT)
2127 file->state = msifs_missing; /* assume files are missing */
2128 continue;
2131 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2133 file->state = msifs_missing;
2134 comp->Cost += file->FileSize;
2135 comp->Installed = INSTALLSTATE_INCOMPLETE;
2136 continue;
2139 if (file->Version &&
2140 (file_version = msi_get_disk_file_version( file->TargetPath )))
2142 TRACE("new %s old %s\n", debugstr_w(file->Version),
2143 debugstr_w(file_version));
2144 /* FIXME: seems like a bad way to compare version numbers */
2145 if (lstrcmpiW(file_version, file->Version)<0)
2147 file->state = msifs_overwrite;
2148 comp->Cost += file->FileSize;
2149 comp->Installed = INSTALLSTATE_INCOMPLETE;
2151 else
2152 file->state = msifs_present;
2153 msi_free( file_version );
2155 else
2156 file->state = msifs_present;
2159 return ERROR_SUCCESS;
2163 * A lot is done in this function aside from just the costing.
2164 * The costing needs to be implemented at some point but for now I am going
2165 * to focus on the directory building
2168 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2170 static const WCHAR ExecSeqQuery[] =
2171 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2172 '`','D','i','r','e','c','t','o','r','y','`',0};
2173 static const WCHAR ConditionQuery[] =
2174 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2175 '`','C','o','n','d','i','t','i','o','n','`',0};
2176 static const WCHAR szCosting[] =
2177 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2178 static const WCHAR szlevel[] =
2179 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2180 static const WCHAR szOne[] = { '1', 0 };
2181 MSICOMPONENT *comp;
2182 UINT rc;
2183 MSIQUERY * view;
2184 LPWSTR level;
2186 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2187 return ERROR_SUCCESS;
2189 TRACE("Building Directory properties\n");
2191 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2192 if (rc == ERROR_SUCCESS)
2194 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2195 package);
2196 msiobj_release(&view->hdr);
2199 /* read components states from the registry */
2200 ACTION_GetComponentInstallStates(package);
2202 TRACE("File calculations\n");
2203 msi_check_file_install_states( package );
2205 TRACE("Evaluating Condition Table\n");
2207 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2208 if (rc == ERROR_SUCCESS)
2210 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2211 package);
2212 msiobj_release(&view->hdr);
2215 TRACE("Enabling or Disabling Components\n");
2216 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2218 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2220 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2221 comp->Enabled = FALSE;
2225 MSI_SetPropertyW(package,szCosting,szOne);
2226 /* set default run level if not set */
2227 level = msi_dup_property( package, szlevel );
2228 if (!level)
2229 MSI_SetPropertyW(package,szlevel, szOne);
2230 msi_free(level);
2232 ACTION_UpdateFeatureInstallStates(package);
2234 return MSI_SetFeatureStates(package);
2237 /* OK this value is "interpreted" and then formatted based on the
2238 first few characters */
2239 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2240 DWORD *size)
2242 LPSTR data = NULL;
2244 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2246 if (value[1]=='x')
2248 LPWSTR ptr;
2249 CHAR byte[5];
2250 LPWSTR deformated = NULL;
2251 int count;
2253 deformat_string(package, &value[2], &deformated);
2255 /* binary value type */
2256 ptr = deformated;
2257 *type = REG_BINARY;
2258 if (strlenW(ptr)%2)
2259 *size = (strlenW(ptr)/2)+1;
2260 else
2261 *size = strlenW(ptr)/2;
2263 data = msi_alloc(*size);
2265 byte[0] = '0';
2266 byte[1] = 'x';
2267 byte[4] = 0;
2268 count = 0;
2269 /* if uneven pad with a zero in front */
2270 if (strlenW(ptr)%2)
2272 byte[2]= '0';
2273 byte[3]= *ptr;
2274 ptr++;
2275 data[count] = (BYTE)strtol(byte,NULL,0);
2276 count ++;
2277 TRACE("Uneven byte count\n");
2279 while (*ptr)
2281 byte[2]= *ptr;
2282 ptr++;
2283 byte[3]= *ptr;
2284 ptr++;
2285 data[count] = (BYTE)strtol(byte,NULL,0);
2286 count ++;
2288 msi_free(deformated);
2290 TRACE("Data %i bytes(%i)\n",*size,count);
2292 else
2294 LPWSTR deformated;
2295 LPWSTR p;
2296 DWORD d = 0;
2297 deformat_string(package, &value[1], &deformated);
2299 *type=REG_DWORD;
2300 *size = sizeof(DWORD);
2301 data = msi_alloc(*size);
2302 p = deformated;
2303 if (*p == '-')
2304 p++;
2305 while (*p)
2307 if ( (*p < '0') || (*p > '9') )
2308 break;
2309 d *= 10;
2310 d += (*p - '0');
2311 p++;
2313 if (deformated[0] == '-')
2314 d = -d;
2315 *(LPDWORD)data = d;
2316 TRACE("DWORD %i\n",*(LPDWORD)data);
2318 msi_free(deformated);
2321 else
2323 static const WCHAR szMulti[] = {'[','~',']',0};
2324 LPCWSTR ptr;
2325 LPWSTR newdata;
2326 *type=REG_SZ;
2328 if (value[0]=='#')
2330 if (value[1]=='%')
2332 ptr = &value[2];
2333 *type=REG_EXPAND_SZ;
2335 else
2336 ptr = &value[1];
2338 else
2339 ptr=value;
2341 if (strstrW(value,szMulti))
2342 *type = REG_MULTI_SZ;
2344 /* remove initial delimiter */
2345 if (!strncmpW(value, szMulti, 3))
2346 ptr = value + 3;
2348 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2350 /* add double NULL terminator */
2351 if (*type == REG_MULTI_SZ)
2353 *size += sizeof(WCHAR);
2354 newdata = msi_alloc(*size);
2355 if (!newdata)
2357 msi_free(data);
2358 return NULL;
2361 memcpy(newdata, data, *size - 1);
2362 newdata[*size] = '\0';
2364 msi_free(data);
2365 data = (LPSTR)newdata;
2368 return data;
2371 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2373 MSIPACKAGE *package = (MSIPACKAGE*)param;
2374 static const WCHAR szHCR[] =
2375 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2376 'R','O','O','T','\\',0};
2377 static const WCHAR szHCU[] =
2378 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2379 'U','S','E','R','\\',0};
2380 static const WCHAR szHLM[] =
2381 {'H','K','E','Y','_','L','O','C','A','L','_',
2382 'M','A','C','H','I','N','E','\\',0};
2383 static const WCHAR szHU[] =
2384 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2386 LPSTR value_data = NULL;
2387 HKEY root_key, hkey;
2388 DWORD type,size;
2389 LPWSTR deformated;
2390 LPCWSTR szRoot, component, name, key, value;
2391 MSICOMPONENT *comp;
2392 MSIRECORD * uirow;
2393 LPWSTR uikey;
2394 INT root;
2395 BOOL check_first = FALSE;
2396 UINT rc;
2398 ui_progress(package,2,0,0,0);
2400 value = NULL;
2401 key = NULL;
2402 uikey = NULL;
2403 name = NULL;
2405 component = MSI_RecordGetString(row, 6);
2406 comp = get_loaded_component(package,component);
2407 if (!comp)
2408 return ERROR_SUCCESS;
2410 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2412 TRACE("Skipping write due to disabled component %s\n",
2413 debugstr_w(component));
2415 comp->Action = comp->Installed;
2417 return ERROR_SUCCESS;
2420 comp->Action = INSTALLSTATE_LOCAL;
2422 name = MSI_RecordGetString(row, 4);
2423 if( MSI_RecordIsNull(row,5) && name )
2425 /* null values can have special meanings */
2426 if (name[0]=='-' && name[1] == 0)
2427 return ERROR_SUCCESS;
2428 else if ((name[0]=='+' && name[1] == 0) ||
2429 (name[0] == '*' && name[1] == 0))
2430 name = NULL;
2431 check_first = TRUE;
2434 root = MSI_RecordGetInteger(row,2);
2435 key = MSI_RecordGetString(row, 3);
2437 /* get the root key */
2438 switch (root)
2440 case -1:
2442 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2443 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2444 if (all_users && all_users[0] == '1')
2446 root_key = HKEY_LOCAL_MACHINE;
2447 szRoot = szHLM;
2449 else
2451 root_key = HKEY_CURRENT_USER;
2452 szRoot = szHCU;
2454 msi_free(all_users);
2456 break;
2457 case 0: root_key = HKEY_CLASSES_ROOT;
2458 szRoot = szHCR;
2459 break;
2460 case 1: root_key = HKEY_CURRENT_USER;
2461 szRoot = szHCU;
2462 break;
2463 case 2: root_key = HKEY_LOCAL_MACHINE;
2464 szRoot = szHLM;
2465 break;
2466 case 3: root_key = HKEY_USERS;
2467 szRoot = szHU;
2468 break;
2469 default:
2470 ERR("Unknown root %i\n",root);
2471 root_key=NULL;
2472 szRoot = NULL;
2473 break;
2475 if (!root_key)
2476 return ERROR_SUCCESS;
2478 deformat_string(package, key , &deformated);
2479 size = strlenW(deformated) + strlenW(szRoot) + 1;
2480 uikey = msi_alloc(size*sizeof(WCHAR));
2481 strcpyW(uikey,szRoot);
2482 strcatW(uikey,deformated);
2484 if (RegCreateKeyW( root_key, deformated, &hkey))
2486 ERR("Could not create key %s\n",debugstr_w(deformated));
2487 msi_free(deformated);
2488 msi_free(uikey);
2489 return ERROR_SUCCESS;
2491 msi_free(deformated);
2493 value = MSI_RecordGetString(row,5);
2494 if (value)
2495 value_data = parse_value(package, value, &type, &size);
2496 else
2498 static const WCHAR szEmpty[] = {0};
2499 value_data = (LPSTR)strdupW(szEmpty);
2500 size = 0;
2501 type = REG_SZ;
2504 deformat_string(package, name, &deformated);
2506 /* get the double nulls to terminate SZ_MULTI */
2507 if (type == REG_MULTI_SZ)
2508 size +=sizeof(WCHAR);
2510 if (!check_first)
2512 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2513 debugstr_w(uikey));
2514 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2516 else
2518 DWORD sz = 0;
2519 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2520 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2522 TRACE("value %s of %s checked already exists\n",
2523 debugstr_w(deformated), debugstr_w(uikey));
2525 else
2527 TRACE("Checked and setting value %s of %s\n",
2528 debugstr_w(deformated), debugstr_w(uikey));
2529 if (deformated || size)
2530 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2533 RegCloseKey(hkey);
2535 uirow = MSI_CreateRecord(3);
2536 MSI_RecordSetStringW(uirow,2,deformated);
2537 MSI_RecordSetStringW(uirow,1,uikey);
2539 if (type == REG_SZ)
2540 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2541 else
2542 MSI_RecordSetStringW(uirow,3,value);
2544 ui_actiondata(package,szWriteRegistryValues,uirow);
2545 msiobj_release( &uirow->hdr );
2547 msi_free(value_data);
2548 msi_free(deformated);
2549 msi_free(uikey);
2551 return ERROR_SUCCESS;
2554 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2556 UINT rc;
2557 MSIQUERY * view;
2558 static const WCHAR ExecSeqQuery[] =
2559 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2560 '`','R','e','g','i','s','t','r','y','`',0 };
2562 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2563 if (rc != ERROR_SUCCESS)
2564 return ERROR_SUCCESS;
2566 /* increment progress bar each time action data is sent */
2567 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2569 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2571 msiobj_release(&view->hdr);
2572 return rc;
2575 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2577 package->script->CurrentlyScripting = TRUE;
2579 return ERROR_SUCCESS;
2583 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2585 MSICOMPONENT *comp;
2586 DWORD progress = 0;
2587 DWORD total = 0;
2588 static const WCHAR q1[]=
2589 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2590 '`','R','e','g','i','s','t','r','y','`',0};
2591 UINT rc;
2592 MSIQUERY * view;
2593 MSIFEATURE *feature;
2594 MSIFILE *file;
2596 TRACE("InstallValidate\n");
2598 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2599 if (rc == ERROR_SUCCESS)
2601 MSI_IterateRecords( view, &progress, NULL, package );
2602 msiobj_release( &view->hdr );
2603 total += progress * REG_PROGRESS_VALUE;
2606 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2607 total += COMPONENT_PROGRESS_VALUE;
2609 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2610 total += file->FileSize;
2612 ui_progress(package,0,total,0,0);
2614 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2616 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2617 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2618 feature->ActionRequest);
2621 return ERROR_SUCCESS;
2624 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2626 MSIPACKAGE* package = (MSIPACKAGE*)param;
2627 LPCWSTR cond = NULL;
2628 LPCWSTR message = NULL;
2629 UINT r;
2631 static const WCHAR title[]=
2632 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2634 cond = MSI_RecordGetString(row,1);
2636 r = MSI_EvaluateConditionW(package,cond);
2637 if (r == MSICONDITION_FALSE)
2639 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2641 LPWSTR deformated;
2642 message = MSI_RecordGetString(row,2);
2643 deformat_string(package,message,&deformated);
2644 MessageBoxW(NULL,deformated,title,MB_OK);
2645 msi_free(deformated);
2648 return ERROR_INSTALL_FAILURE;
2651 return ERROR_SUCCESS;
2654 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2656 UINT rc;
2657 MSIQUERY * view = NULL;
2658 static const WCHAR ExecSeqQuery[] =
2659 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2660 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2662 TRACE("Checking launch conditions\n");
2664 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2665 if (rc != ERROR_SUCCESS)
2666 return ERROR_SUCCESS;
2668 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2669 msiobj_release(&view->hdr);
2671 return rc;
2674 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2677 if (!cmp->KeyPath)
2678 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2680 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2682 MSIRECORD * row = 0;
2683 UINT root,len;
2684 LPWSTR deformated,buffer,deformated_name;
2685 LPCWSTR key,name;
2686 static const WCHAR ExecSeqQuery[] =
2687 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2688 '`','R','e','g','i','s','t','r','y','`',' ',
2689 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2690 ' ','=',' ' ,'\'','%','s','\'',0 };
2691 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2692 static const WCHAR fmt2[]=
2693 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2695 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2696 if (!row)
2697 return NULL;
2699 root = MSI_RecordGetInteger(row,2);
2700 key = MSI_RecordGetString(row, 3);
2701 name = MSI_RecordGetString(row, 4);
2702 deformat_string(package, key , &deformated);
2703 deformat_string(package, name, &deformated_name);
2705 len = strlenW(deformated) + 6;
2706 if (deformated_name)
2707 len+=strlenW(deformated_name);
2709 buffer = msi_alloc( len *sizeof(WCHAR));
2711 if (deformated_name)
2712 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2713 else
2714 sprintfW(buffer,fmt,root,deformated);
2716 msi_free(deformated);
2717 msi_free(deformated_name);
2718 msiobj_release(&row->hdr);
2720 return buffer;
2722 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2724 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2725 return NULL;
2727 else
2729 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2731 if (file)
2732 return strdupW( file->TargetPath );
2734 return NULL;
2737 static HKEY openSharedDLLsKey(void)
2739 HKEY hkey=0;
2740 static const WCHAR path[] =
2741 {'S','o','f','t','w','a','r','e','\\',
2742 'M','i','c','r','o','s','o','f','t','\\',
2743 'W','i','n','d','o','w','s','\\',
2744 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2745 'S','h','a','r','e','d','D','L','L','s',0};
2747 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2748 return hkey;
2751 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2753 HKEY hkey;
2754 DWORD count=0;
2755 DWORD type;
2756 DWORD sz = sizeof(count);
2757 DWORD rc;
2759 hkey = openSharedDLLsKey();
2760 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2761 if (rc != ERROR_SUCCESS)
2762 count = 0;
2763 RegCloseKey(hkey);
2764 return count;
2767 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2769 HKEY hkey;
2771 hkey = openSharedDLLsKey();
2772 if (count > 0)
2773 msi_reg_set_val_dword( hkey, path, count );
2774 else
2775 RegDeleteValueW(hkey,path);
2776 RegCloseKey(hkey);
2777 return count;
2781 * Return TRUE if the count should be written out and FALSE if not
2783 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2785 MSIFEATURE *feature;
2786 INT count = 0;
2787 BOOL write = FALSE;
2789 /* only refcount DLLs */
2790 if (comp->KeyPath == NULL ||
2791 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2792 comp->Attributes & msidbComponentAttributesODBCDataSource)
2793 write = FALSE;
2794 else
2796 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2797 write = (count > 0);
2799 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2800 write = TRUE;
2803 /* increment counts */
2804 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2806 ComponentList *cl;
2808 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2809 continue;
2811 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2813 if ( cl->component == comp )
2814 count++;
2818 /* decrement counts */
2819 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2821 ComponentList *cl;
2823 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2824 continue;
2826 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2828 if ( cl->component == comp )
2829 count--;
2833 /* ref count all the files in the component */
2834 if (write)
2836 MSIFILE *file;
2838 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2840 if (file->Component == comp)
2841 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2845 /* add a count for permenent */
2846 if (comp->Attributes & msidbComponentAttributesPermanent)
2847 count ++;
2849 comp->RefCount = count;
2851 if (write)
2852 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2856 * Ok further analysis makes me think that this work is
2857 * actually done in the PublishComponents and PublishFeatures
2858 * step, and not here. It appears like the keypath and all that is
2859 * resolved in this step, however actually written in the Publish steps.
2860 * But we will leave it here for now because it is unclear
2862 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2864 WCHAR squished_pc[GUID_SIZE];
2865 WCHAR squished_cc[GUID_SIZE];
2866 UINT rc;
2867 MSICOMPONENT *comp;
2868 HKEY hkey=0,hkey2=0;
2870 TRACE("\n");
2872 /* writes the Component and Features values to the registry */
2874 rc = MSIREG_OpenComponents(&hkey);
2875 if (rc != ERROR_SUCCESS)
2876 return rc;
2878 squash_guid(package->ProductCode,squished_pc);
2879 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2881 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2883 MSIRECORD * uirow;
2885 ui_progress(package,2,0,0,0);
2886 if (!comp->ComponentId)
2887 continue;
2889 squash_guid(comp->ComponentId,squished_cc);
2891 msi_free(comp->FullKeypath);
2892 comp->FullKeypath = resolve_keypath( package, comp );
2894 /* do the refcounting */
2895 ACTION_RefCountComponent( package, comp );
2897 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2898 debugstr_w(comp->Component),
2899 debugstr_w(squished_cc),
2900 debugstr_w(comp->FullKeypath),
2901 comp->RefCount);
2903 * Write the keypath out if the component is to be registered
2904 * and delete the key if the component is to be deregistered
2906 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2908 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2909 if (rc != ERROR_SUCCESS)
2910 continue;
2912 if (!comp->FullKeypath)
2913 continue;
2915 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2917 if (comp->Attributes & msidbComponentAttributesPermanent)
2919 static const WCHAR szPermKey[] =
2920 { '0','0','0','0','0','0','0','0','0','0','0','0',
2921 '0','0','0','0','0','0','0','0','0','0','0','0',
2922 '0','0','0','0','0','0','0','0',0 };
2924 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2927 RegCloseKey(hkey2);
2929 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey2, TRUE);
2930 if (rc != ERROR_SUCCESS)
2931 continue;
2933 msi_reg_set_val_str(hkey2, squished_pc, comp->FullKeypath);
2934 RegCloseKey(hkey2);
2936 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2938 DWORD res;
2940 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2941 if (rc != ERROR_SUCCESS)
2942 continue;
2944 RegDeleteValueW(hkey2,squished_pc);
2946 /* if the key is empty delete it */
2947 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2948 RegCloseKey(hkey2);
2949 if (res == ERROR_NO_MORE_ITEMS)
2950 RegDeleteKeyW(hkey,squished_cc);
2952 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2955 /* UI stuff */
2956 uirow = MSI_CreateRecord(3);
2957 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2958 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2959 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2960 ui_actiondata(package,szProcessComponents,uirow);
2961 msiobj_release( &uirow->hdr );
2963 RegCloseKey(hkey);
2964 return rc;
2967 typedef struct {
2968 CLSID clsid;
2969 LPWSTR source;
2971 LPWSTR path;
2972 ITypeLib *ptLib;
2973 } typelib_struct;
2975 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2976 LPWSTR lpszName, LONG_PTR lParam)
2978 TLIBATTR *attr;
2979 typelib_struct *tl_struct = (typelib_struct*) lParam;
2980 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2981 int sz;
2982 HRESULT res;
2984 if (!IS_INTRESOURCE(lpszName))
2986 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2987 return TRUE;
2990 sz = strlenW(tl_struct->source)+4;
2991 sz *= sizeof(WCHAR);
2993 if ((INT_PTR)lpszName == 1)
2994 tl_struct->path = strdupW(tl_struct->source);
2995 else
2997 tl_struct->path = msi_alloc(sz);
2998 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3001 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3002 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3003 if (!SUCCEEDED(res))
3005 msi_free(tl_struct->path);
3006 tl_struct->path = NULL;
3008 return TRUE;
3011 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3012 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3014 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3015 return FALSE;
3018 msi_free(tl_struct->path);
3019 tl_struct->path = NULL;
3021 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3022 ITypeLib_Release(tl_struct->ptLib);
3024 return TRUE;
3027 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3029 MSIPACKAGE* package = (MSIPACKAGE*)param;
3030 LPCWSTR component;
3031 MSICOMPONENT *comp;
3032 MSIFILE *file;
3033 typelib_struct tl_struct;
3034 HMODULE module;
3035 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3037 component = MSI_RecordGetString(row,3);
3038 comp = get_loaded_component(package,component);
3039 if (!comp)
3040 return ERROR_SUCCESS;
3042 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3044 TRACE("Skipping typelib reg due to disabled component\n");
3046 comp->Action = comp->Installed;
3048 return ERROR_SUCCESS;
3051 comp->Action = INSTALLSTATE_LOCAL;
3053 file = get_loaded_file( package, comp->KeyPath );
3054 if (!file)
3055 return ERROR_SUCCESS;
3057 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3058 if (module)
3060 LPCWSTR guid;
3061 guid = MSI_RecordGetString(row,1);
3062 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3063 tl_struct.source = strdupW( file->TargetPath );
3064 tl_struct.path = NULL;
3066 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3067 (LONG_PTR)&tl_struct);
3069 if (tl_struct.path)
3071 LPWSTR help = NULL;
3072 LPCWSTR helpid;
3073 HRESULT res;
3075 helpid = MSI_RecordGetString(row,6);
3077 if (helpid)
3078 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3079 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3080 msi_free(help);
3082 if (!SUCCEEDED(res))
3083 ERR("Failed to register type library %s\n",
3084 debugstr_w(tl_struct.path));
3085 else
3087 ui_actiondata(package,szRegisterTypeLibraries,row);
3089 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3092 ITypeLib_Release(tl_struct.ptLib);
3093 msi_free(tl_struct.path);
3095 else
3096 ERR("Failed to load type library %s\n",
3097 debugstr_w(tl_struct.source));
3099 FreeLibrary(module);
3100 msi_free(tl_struct.source);
3102 else
3103 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3105 return ERROR_SUCCESS;
3108 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3111 * OK this is a bit confusing.. I am given a _Component key and I believe
3112 * that the file that is being registered as a type library is the "key file
3113 * of that component" which I interpret to mean "The file in the KeyPath of
3114 * that component".
3116 UINT rc;
3117 MSIQUERY * view;
3118 static const WCHAR Query[] =
3119 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3120 '`','T','y','p','e','L','i','b','`',0};
3122 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3123 if (rc != ERROR_SUCCESS)
3124 return ERROR_SUCCESS;
3126 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3127 msiobj_release(&view->hdr);
3128 return rc;
3131 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3133 MSIPACKAGE *package = (MSIPACKAGE*)param;
3134 LPWSTR target_file, target_folder, filename;
3135 LPCWSTR buffer, extension;
3136 MSICOMPONENT *comp;
3137 static const WCHAR szlnk[]={'.','l','n','k',0};
3138 IShellLinkW *sl = NULL;
3139 IPersistFile *pf = NULL;
3140 HRESULT res;
3142 buffer = MSI_RecordGetString(row,4);
3143 comp = get_loaded_component(package,buffer);
3144 if (!comp)
3145 return ERROR_SUCCESS;
3147 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3149 TRACE("Skipping shortcut creation due to disabled component\n");
3151 comp->Action = comp->Installed;
3153 return ERROR_SUCCESS;
3156 comp->Action = INSTALLSTATE_LOCAL;
3158 ui_actiondata(package,szCreateShortcuts,row);
3160 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3161 &IID_IShellLinkW, (LPVOID *) &sl );
3163 if (FAILED( res ))
3165 ERR("CLSID_ShellLink not available\n");
3166 goto err;
3169 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3170 if (FAILED( res ))
3172 ERR("QueryInterface(IID_IPersistFile) failed\n");
3173 goto err;
3176 buffer = MSI_RecordGetString(row,2);
3177 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3179 /* may be needed because of a bug somehwere else */
3180 create_full_pathW(target_folder);
3182 filename = msi_dup_record_field( row, 3 );
3183 reduce_to_longfilename(filename);
3185 extension = strchrW(filename,'.');
3186 if (!extension || strcmpiW(extension,szlnk))
3188 int len = strlenW(filename);
3189 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3190 memcpy(filename + len, szlnk, sizeof(szlnk));
3192 target_file = build_directory_name(2, target_folder, filename);
3193 msi_free(target_folder);
3194 msi_free(filename);
3196 buffer = MSI_RecordGetString(row,5);
3197 if (strchrW(buffer,'['))
3199 LPWSTR deformated;
3200 deformat_string(package,buffer,&deformated);
3201 IShellLinkW_SetPath(sl,deformated);
3202 msi_free(deformated);
3204 else
3206 FIXME("poorly handled shortcut format, advertised shortcut\n");
3207 IShellLinkW_SetPath(sl,comp->FullKeypath);
3210 if (!MSI_RecordIsNull(row,6))
3212 LPWSTR deformated;
3213 buffer = MSI_RecordGetString(row,6);
3214 deformat_string(package,buffer,&deformated);
3215 IShellLinkW_SetArguments(sl,deformated);
3216 msi_free(deformated);
3219 if (!MSI_RecordIsNull(row,7))
3221 buffer = MSI_RecordGetString(row,7);
3222 IShellLinkW_SetDescription(sl,buffer);
3225 if (!MSI_RecordIsNull(row,8))
3226 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3228 if (!MSI_RecordIsNull(row,9))
3230 LPWSTR Path;
3231 INT index;
3233 buffer = MSI_RecordGetString(row,9);
3235 Path = build_icon_path(package,buffer);
3236 index = MSI_RecordGetInteger(row,10);
3238 /* no value means 0 */
3239 if (index == MSI_NULL_INTEGER)
3240 index = 0;
3242 IShellLinkW_SetIconLocation(sl,Path,index);
3243 msi_free(Path);
3246 if (!MSI_RecordIsNull(row,11))
3247 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3249 if (!MSI_RecordIsNull(row,12))
3251 LPWSTR Path;
3252 buffer = MSI_RecordGetString(row,12);
3253 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3254 if (Path)
3255 IShellLinkW_SetWorkingDirectory(sl,Path);
3256 msi_free(Path);
3259 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3260 IPersistFile_Save(pf,target_file,FALSE);
3262 msi_free(target_file);
3264 err:
3265 if (pf)
3266 IPersistFile_Release( pf );
3267 if (sl)
3268 IShellLinkW_Release( sl );
3270 return ERROR_SUCCESS;
3273 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3275 UINT rc;
3276 HRESULT res;
3277 MSIQUERY * view;
3278 static const WCHAR Query[] =
3279 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3280 '`','S','h','o','r','t','c','u','t','`',0};
3282 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3283 if (rc != ERROR_SUCCESS)
3284 return ERROR_SUCCESS;
3286 res = CoInitialize( NULL );
3287 if (FAILED (res))
3289 ERR("CoInitialize failed\n");
3290 return ERROR_FUNCTION_FAILED;
3293 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3294 msiobj_release(&view->hdr);
3296 CoUninitialize();
3298 return rc;
3301 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3303 MSIPACKAGE* package = (MSIPACKAGE*)param;
3304 HANDLE the_file;
3305 LPWSTR FilePath;
3306 LPCWSTR FileName;
3307 CHAR buffer[1024];
3308 DWORD sz;
3309 UINT rc;
3310 MSIRECORD *uirow;
3312 FileName = MSI_RecordGetString(row,1);
3313 if (!FileName)
3315 ERR("Unable to get FileName\n");
3316 return ERROR_SUCCESS;
3319 FilePath = build_icon_path(package,FileName);
3321 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3323 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3324 FILE_ATTRIBUTE_NORMAL, NULL);
3326 if (the_file == INVALID_HANDLE_VALUE)
3328 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3329 msi_free(FilePath);
3330 return ERROR_SUCCESS;
3335 DWORD write;
3336 sz = 1024;
3337 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3338 if (rc != ERROR_SUCCESS)
3340 ERR("Failed to get stream\n");
3341 CloseHandle(the_file);
3342 DeleteFileW(FilePath);
3343 break;
3345 WriteFile(the_file,buffer,sz,&write,NULL);
3346 } while (sz == 1024);
3348 msi_free(FilePath);
3350 CloseHandle(the_file);
3352 uirow = MSI_CreateRecord(1);
3353 MSI_RecordSetStringW(uirow,1,FileName);
3354 ui_actiondata(package,szPublishProduct,uirow);
3355 msiobj_release( &uirow->hdr );
3357 return ERROR_SUCCESS;
3360 static BOOL msi_check_publish(MSIPACKAGE *package)
3362 MSIFEATURE *feature;
3364 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3366 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3367 return TRUE;
3370 return FALSE;
3374 * 99% of the work done here is only done for
3375 * advertised installs. However this is where the
3376 * Icon table is processed and written out
3377 * so that is what I am going to do here.
3379 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3381 UINT rc;
3382 MSIQUERY * view;
3383 MSISOURCELISTINFO *info;
3384 MSIMEDIADISK *disk;
3385 static const WCHAR Query[]=
3386 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3387 '`','I','c','o','n','`',0};
3388 /* for registry stuff */
3389 HKEY hkey=0;
3390 HKEY hukey=0;
3391 HKEY hudkey=0, props=0;
3392 static const WCHAR szProductLanguage[] =
3393 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3394 static const WCHAR szARPProductIcon[] =
3395 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3396 static const WCHAR szProductVersion[] =
3397 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3398 DWORD langid;
3399 LPWSTR buffer;
3400 DWORD size;
3401 MSIHANDLE hDb, hSumInfo;
3403 /* FIXME: also need to publish if the product is in advertise mode */
3404 if (!msi_check_publish(package))
3405 return ERROR_SUCCESS;
3407 /* write out icon files */
3409 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3410 if (rc == ERROR_SUCCESS)
3412 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3413 msiobj_release(&view->hdr);
3416 /* ok there is a lot more done here but i need to figure out what */
3418 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3419 if (rc != ERROR_SUCCESS)
3420 goto end;
3422 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3423 if (rc != ERROR_SUCCESS)
3424 goto end;
3426 rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3427 if (rc != ERROR_SUCCESS)
3428 goto end;
3430 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode,&props,TRUE);
3431 if (rc != ERROR_SUCCESS)
3432 goto end;
3434 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3435 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3436 msi_free(buffer);
3438 langid = msi_get_property_int( package, szProductLanguage, 0 );
3439 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3441 buffer = msi_dup_property( package, szARPProductIcon );
3442 if (buffer)
3444 LPWSTR path = build_icon_path(package,buffer);
3445 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3446 msi_free( path );
3448 msi_free(buffer);
3450 buffer = msi_dup_property( package, szProductVersion );
3451 if (buffer)
3453 DWORD verdword = msi_version_str_to_dword(buffer);
3454 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3456 msi_free(buffer);
3458 /* FIXME: Need to write more keys to the user registry */
3460 hDb= alloc_msihandle( &package->db->hdr );
3461 if (!hDb) {
3462 rc = ERROR_NOT_ENOUGH_MEMORY;
3463 goto end;
3465 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3466 MsiCloseHandle(hDb);
3467 if (rc == ERROR_SUCCESS)
3469 WCHAR guidbuffer[0x200];
3470 size = 0x200;
3471 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3472 guidbuffer, &size);
3473 if (rc == ERROR_SUCCESS)
3475 WCHAR squashed[GUID_SIZE];
3476 /* for now we only care about the first guid */
3477 LPWSTR ptr = strchrW(guidbuffer,';');
3478 if (ptr) *ptr = 0;
3479 squash_guid(guidbuffer,squashed);
3480 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3482 else
3484 ERR("Unable to query Revision_Number...\n");
3485 rc = ERROR_SUCCESS;
3487 MsiCloseHandle(hSumInfo);
3489 else
3491 ERR("Unable to open Summary Information\n");
3492 rc = ERROR_SUCCESS;
3495 /* publish the SourceList info */
3496 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3498 MsiSourceListSetInfoW(package->ProductCode, NULL,
3499 info->context, info->options,
3500 info->property, info->value);
3503 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3505 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3506 disk->context, disk->options,
3507 disk->disk_id, disk->volume_label, disk->disk_prompt);
3510 end:
3511 RegCloseKey(hkey);
3512 RegCloseKey(hukey);
3513 RegCloseKey(hudkey);
3514 RegCloseKey(props);
3516 return rc;
3519 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3521 MSIPACKAGE *package = (MSIPACKAGE*)param;
3522 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3523 LPWSTR deformated_section, deformated_key, deformated_value;
3524 LPWSTR folder, fullname = NULL;
3525 MSIRECORD * uirow;
3526 INT action;
3527 MSICOMPONENT *comp;
3528 static const WCHAR szWindowsFolder[] =
3529 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3531 component = MSI_RecordGetString(row, 8);
3532 comp = get_loaded_component(package,component);
3534 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3536 TRACE("Skipping ini file due to disabled component %s\n",
3537 debugstr_w(component));
3539 comp->Action = comp->Installed;
3541 return ERROR_SUCCESS;
3544 comp->Action = INSTALLSTATE_LOCAL;
3546 identifier = MSI_RecordGetString(row,1);
3547 filename = MSI_RecordGetString(row,2);
3548 dirproperty = MSI_RecordGetString(row,3);
3549 section = MSI_RecordGetString(row,4);
3550 key = MSI_RecordGetString(row,5);
3551 value = MSI_RecordGetString(row,6);
3552 action = MSI_RecordGetInteger(row,7);
3554 deformat_string(package,section,&deformated_section);
3555 deformat_string(package,key,&deformated_key);
3556 deformat_string(package,value,&deformated_value);
3558 if (dirproperty)
3560 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3561 if (!folder)
3562 folder = msi_dup_property( package, dirproperty );
3564 else
3565 folder = msi_dup_property( package, szWindowsFolder );
3567 if (!folder)
3569 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3570 goto cleanup;
3573 fullname = build_directory_name(2, folder, filename);
3575 if (action == 0)
3577 TRACE("Adding value %s to section %s in %s\n",
3578 debugstr_w(deformated_key), debugstr_w(deformated_section),
3579 debugstr_w(fullname));
3580 WritePrivateProfileStringW(deformated_section, deformated_key,
3581 deformated_value, fullname);
3583 else if (action == 1)
3585 WCHAR returned[10];
3586 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3587 returned, 10, fullname);
3588 if (returned[0] == 0)
3590 TRACE("Adding value %s to section %s in %s\n",
3591 debugstr_w(deformated_key), debugstr_w(deformated_section),
3592 debugstr_w(fullname));
3594 WritePrivateProfileStringW(deformated_section, deformated_key,
3595 deformated_value, fullname);
3598 else if (action == 3)
3599 FIXME("Append to existing section not yet implemented\n");
3601 uirow = MSI_CreateRecord(4);
3602 MSI_RecordSetStringW(uirow,1,identifier);
3603 MSI_RecordSetStringW(uirow,2,deformated_section);
3604 MSI_RecordSetStringW(uirow,3,deformated_key);
3605 MSI_RecordSetStringW(uirow,4,deformated_value);
3606 ui_actiondata(package,szWriteIniValues,uirow);
3607 msiobj_release( &uirow->hdr );
3608 cleanup:
3609 msi_free(fullname);
3610 msi_free(folder);
3611 msi_free(deformated_key);
3612 msi_free(deformated_value);
3613 msi_free(deformated_section);
3614 return ERROR_SUCCESS;
3617 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3619 UINT rc;
3620 MSIQUERY * view;
3621 static const WCHAR ExecSeqQuery[] =
3622 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3623 '`','I','n','i','F','i','l','e','`',0};
3625 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3626 if (rc != ERROR_SUCCESS)
3628 TRACE("no IniFile table\n");
3629 return ERROR_SUCCESS;
3632 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3633 msiobj_release(&view->hdr);
3634 return rc;
3637 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3639 MSIPACKAGE *package = (MSIPACKAGE*)param;
3640 LPCWSTR filename;
3641 LPWSTR FullName;
3642 MSIFILE *file;
3643 DWORD len;
3644 static const WCHAR ExeStr[] =
3645 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3646 static const WCHAR close[] = {'\"',0};
3647 STARTUPINFOW si;
3648 PROCESS_INFORMATION info;
3649 BOOL brc;
3650 MSIRECORD *uirow;
3651 LPWSTR uipath, p;
3653 memset(&si,0,sizeof(STARTUPINFOW));
3655 filename = MSI_RecordGetString(row,1);
3656 file = get_loaded_file( package, filename );
3658 if (!file)
3660 ERR("Unable to find file id %s\n",debugstr_w(filename));
3661 return ERROR_SUCCESS;
3664 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3666 FullName = msi_alloc(len*sizeof(WCHAR));
3667 strcpyW(FullName,ExeStr);
3668 strcatW( FullName, file->TargetPath );
3669 strcatW(FullName,close);
3671 TRACE("Registering %s\n",debugstr_w(FullName));
3672 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3673 &si, &info);
3675 if (brc)
3676 msi_dialog_check_messages(info.hProcess);
3678 msi_free(FullName);
3680 /* the UI chunk */
3681 uirow = MSI_CreateRecord( 2 );
3682 uipath = strdupW( file->TargetPath );
3683 p = strrchrW(uipath,'\\');
3684 if (p)
3685 p[0]=0;
3686 MSI_RecordSetStringW( uirow, 1, &p[1] );
3687 MSI_RecordSetStringW( uirow, 2, uipath);
3688 ui_actiondata( package, szSelfRegModules, uirow);
3689 msiobj_release( &uirow->hdr );
3690 msi_free( uipath );
3691 /* FIXME: call ui_progress? */
3693 return ERROR_SUCCESS;
3696 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3698 UINT rc;
3699 MSIQUERY * view;
3700 static const WCHAR ExecSeqQuery[] =
3701 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3702 '`','S','e','l','f','R','e','g','`',0};
3704 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3705 if (rc != ERROR_SUCCESS)
3707 TRACE("no SelfReg table\n");
3708 return ERROR_SUCCESS;
3711 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3712 msiobj_release(&view->hdr);
3714 return ERROR_SUCCESS;
3717 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3719 MSIFEATURE *feature;
3720 UINT rc;
3721 HKEY hkey=0;
3722 HKEY hukey=0;
3723 HKEY userdata=0;
3725 if (!msi_check_publish(package))
3726 return ERROR_SUCCESS;
3728 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3729 if (rc != ERROR_SUCCESS)
3730 goto end;
3732 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3733 if (rc != ERROR_SUCCESS)
3734 goto end;
3736 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &userdata, TRUE);
3737 if (rc != ERROR_SUCCESS)
3738 goto end;
3740 /* here the guids are base 85 encoded */
3741 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3743 ComponentList *cl;
3744 LPWSTR data = NULL;
3745 GUID clsid;
3746 INT size;
3747 BOOL absent = FALSE;
3748 MSIRECORD *uirow;
3750 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3751 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3752 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3753 absent = TRUE;
3755 size = 1;
3756 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3758 size += 21;
3760 if (feature->Feature_Parent)
3761 size += strlenW( feature->Feature_Parent )+2;
3763 data = msi_alloc(size * sizeof(WCHAR));
3765 data[0] = 0;
3766 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3768 MSICOMPONENT* component = cl->component;
3769 WCHAR buf[21];
3771 buf[0] = 0;
3772 if (component->ComponentId)
3774 TRACE("From %s\n",debugstr_w(component->ComponentId));
3775 CLSIDFromString(component->ComponentId, &clsid);
3776 encode_base85_guid(&clsid,buf);
3777 TRACE("to %s\n",debugstr_w(buf));
3778 strcatW(data,buf);
3782 if (feature->Feature_Parent)
3784 static const WCHAR sep[] = {'\2',0};
3785 strcatW(data,sep);
3786 strcatW(data,feature->Feature_Parent);
3789 msi_reg_set_val_str( hkey, feature->Feature, data );
3790 msi_reg_set_val_str( userdata, feature->Feature, data );
3791 msi_free(data);
3793 size = 0;
3794 if (feature->Feature_Parent)
3795 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3796 if (!absent)
3798 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3799 (LPBYTE)feature->Feature_Parent,size);
3801 else
3803 size += 2*sizeof(WCHAR);
3804 data = msi_alloc(size);
3805 data[0] = 0x6;
3806 data[1] = 0;
3807 if (feature->Feature_Parent)
3808 strcpyW( &data[1], feature->Feature_Parent );
3809 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3810 (LPBYTE)data,size);
3811 msi_free(data);
3814 /* the UI chunk */
3815 uirow = MSI_CreateRecord( 1 );
3816 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3817 ui_actiondata( package, szPublishFeatures, uirow);
3818 msiobj_release( &uirow->hdr );
3819 /* FIXME: call ui_progress? */
3822 end:
3823 RegCloseKey(hkey);
3824 RegCloseKey(hukey);
3825 return rc;
3828 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3830 UINT r;
3831 HKEY hkey;
3833 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3835 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3836 if (r == ERROR_SUCCESS)
3838 RegDeleteValueW(hkey, feature->Feature);
3839 RegCloseKey(hkey);
3842 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3843 if (r == ERROR_SUCCESS)
3845 RegDeleteValueW(hkey, feature->Feature);
3846 RegCloseKey(hkey);
3849 return ERROR_SUCCESS;
3852 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3854 MSIFEATURE *feature;
3856 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3858 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3859 return FALSE;
3862 return TRUE;
3865 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3867 MSIFEATURE *feature;
3869 if (!msi_check_unpublish(package))
3870 return ERROR_SUCCESS;
3872 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3874 msi_unpublish_feature(package, feature);
3877 return ERROR_SUCCESS;
3880 static UINT msi_get_local_package_name( LPWSTR path )
3882 static const WCHAR szInstaller[] = {
3883 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3884 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3885 DWORD time, len, i;
3886 HANDLE handle;
3888 time = GetTickCount();
3889 GetWindowsDirectoryW( path, MAX_PATH );
3890 lstrcatW( path, szInstaller );
3891 CreateDirectoryW( path, NULL );
3893 len = lstrlenW(path);
3894 for (i=0; i<0x10000; i++)
3896 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3897 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3898 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3899 if (handle != INVALID_HANDLE_VALUE)
3901 CloseHandle(handle);
3902 break;
3904 if (GetLastError() != ERROR_FILE_EXISTS &&
3905 GetLastError() != ERROR_SHARING_VIOLATION)
3906 return ERROR_FUNCTION_FAILED;
3909 return ERROR_SUCCESS;
3912 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3914 WCHAR packagefile[MAX_PATH];
3915 HKEY props;
3916 UINT r;
3918 r = msi_get_local_package_name( packagefile );
3919 if (r != ERROR_SUCCESS)
3920 return r;
3922 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3924 r = CopyFileW( package->db->path, packagefile, FALSE);
3926 if (!r)
3928 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3929 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
3930 return ERROR_FUNCTION_FAILED;
3933 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3935 r = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3936 if (r != ERROR_SUCCESS)
3937 return r;
3939 msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3940 RegCloseKey(props);
3941 return ERROR_SUCCESS;
3944 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3946 LPWSTR prop, val, key;
3947 static const LPCSTR propval[] = {
3948 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3949 "ARPCONTACT", "Contact",
3950 "ARPCOMMENTS", "Comments",
3951 "ProductName", "DisplayName",
3952 "ProductVersion", "DisplayVersion",
3953 "ARPHELPLINK", "HelpLink",
3954 "ARPHELPTELEPHONE", "HelpTelephone",
3955 "ARPINSTALLLOCATION", "InstallLocation",
3956 "SourceDir", "InstallSource",
3957 "Manufacturer", "Publisher",
3958 "ARPREADME", "Readme",
3959 "ARPSIZE", "Size",
3960 "ARPURLINFOABOUT", "URLInfoAbout",
3961 "ARPURLUPDATEINFO", "URLUpdateInfo",
3962 NULL,
3964 const LPCSTR *p = propval;
3966 while( *p )
3968 prop = strdupAtoW( *p++ );
3969 key = strdupAtoW( *p++ );
3970 val = msi_dup_property( package, prop );
3971 msi_reg_set_val_str( hkey, key, val );
3972 msi_free(val);
3973 msi_free(key);
3974 msi_free(prop);
3976 return ERROR_SUCCESS;
3979 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3981 HKEY hkey=0;
3982 HKEY hudkey=0, props=0;
3983 LPWSTR buffer = NULL;
3984 UINT rc;
3985 DWORD size, langid;
3986 static const WCHAR szWindowsInstaller[] =
3987 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3988 static const WCHAR szUpgradeCode[] =
3989 {'U','p','g','r','a','d','e','C','o','d','e',0};
3990 static const WCHAR modpath_fmt[] =
3991 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3992 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3993 static const WCHAR szModifyPath[] =
3994 {'M','o','d','i','f','y','P','a','t','h',0};
3995 static const WCHAR szUninstallString[] =
3996 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3997 static const WCHAR szEstimatedSize[] =
3998 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3999 static const WCHAR szProductLanguage[] =
4000 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4001 static const WCHAR szProductVersion[] =
4002 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4004 SYSTEMTIME systime;
4005 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4006 LPWSTR upgrade_code;
4007 WCHAR szDate[9];
4009 /* FIXME: also need to publish if the product is in advertise mode */
4010 if (!msi_check_publish(package))
4011 return ERROR_SUCCESS;
4013 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4014 if (rc != ERROR_SUCCESS)
4015 return rc;
4017 /* dump all the info i can grab */
4018 /* FIXME: Flesh out more information */
4020 msi_write_uninstall_property_vals( package, hkey );
4022 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
4024 msi_make_package_local( package, hkey );
4026 /* do ModifyPath and UninstallString */
4027 size = deformat_string(package,modpath_fmt,&buffer);
4028 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4029 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4030 msi_free(buffer);
4032 /* FIXME: Write real Estimated Size when we have it */
4033 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
4035 GetLocalTime(&systime);
4036 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
4037 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
4039 langid = msi_get_property_int( package, szProductLanguage, 0 );
4040 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
4042 buffer = msi_dup_property( package, szProductVersion );
4043 if (buffer)
4045 DWORD verdword = msi_version_str_to_dword(buffer);
4047 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
4048 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
4049 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
4051 msi_free(buffer);
4053 /* Handle Upgrade Codes */
4054 upgrade_code = msi_dup_property( package, szUpgradeCode );
4055 if (upgrade_code)
4057 HKEY hkey2;
4058 WCHAR squashed[33];
4059 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4060 squash_guid(package->ProductCode,squashed);
4061 msi_reg_set_val_str( hkey2, squashed, NULL );
4062 RegCloseKey(hkey2);
4063 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4064 squash_guid(package->ProductCode,squashed);
4065 msi_reg_set_val_str( hkey2, squashed, NULL );
4066 RegCloseKey(hkey2);
4068 msi_free(upgrade_code);
4071 RegCloseKey(hkey);
4073 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
4074 if (rc != ERROR_SUCCESS)
4075 return rc;
4077 RegCloseKey(hudkey);
4079 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
4080 if (rc != ERROR_SUCCESS)
4081 return rc;
4083 msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
4084 RegCloseKey(props);
4086 return ERROR_SUCCESS;
4089 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4091 return execute_script(package,INSTALL_SCRIPT);
4094 static UINT msi_unpublish_product(MSIPACKAGE *package)
4096 LPWSTR remove = NULL;
4097 LPWSTR *features = NULL;
4098 BOOL full_uninstall = TRUE;
4099 MSIFEATURE *feature;
4101 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4102 static const WCHAR szAll[] = {'A','L','L',0};
4104 remove = msi_dup_property(package, szRemove);
4105 if (!remove)
4106 return ERROR_SUCCESS;
4108 features = msi_split_string(remove, ',');
4109 if (!features)
4111 msi_free(remove);
4112 ERR("REMOVE feature list is empty!\n");
4113 return ERROR_FUNCTION_FAILED;
4116 if (!lstrcmpW(features[0], szAll))
4117 full_uninstall = TRUE;
4118 else
4120 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4122 if (feature->Action != INSTALLSTATE_ABSENT)
4123 full_uninstall = FALSE;
4127 if (!full_uninstall)
4128 goto done;
4130 MSIREG_DeleteProductKey(package->ProductCode);
4131 MSIREG_DeleteUserProductKey(package->ProductCode);
4132 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4133 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4134 MSIREG_DeleteUninstallKey(package->ProductCode);
4136 done:
4137 msi_free(remove);
4138 msi_free(features);
4139 return ERROR_SUCCESS;
4142 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4144 UINT rc;
4146 rc = msi_unpublish_product(package);
4147 if (rc != ERROR_SUCCESS)
4148 return rc;
4150 /* turn off scheduling */
4151 package->script->CurrentlyScripting= FALSE;
4153 /* first do the same as an InstallExecute */
4154 rc = ACTION_InstallExecute(package);
4155 if (rc != ERROR_SUCCESS)
4156 return rc;
4158 /* then handle Commit Actions */
4159 rc = execute_script(package,COMMIT_SCRIPT);
4161 return rc;
4164 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4166 static const WCHAR RunOnce[] = {
4167 'S','o','f','t','w','a','r','e','\\',
4168 'M','i','c','r','o','s','o','f','t','\\',
4169 'W','i','n','d','o','w','s','\\',
4170 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4171 'R','u','n','O','n','c','e',0};
4172 static const WCHAR InstallRunOnce[] = {
4173 'S','o','f','t','w','a','r','e','\\',
4174 'M','i','c','r','o','s','o','f','t','\\',
4175 'W','i','n','d','o','w','s','\\',
4176 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4177 'I','n','s','t','a','l','l','e','r','\\',
4178 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4180 static const WCHAR msiexec_fmt[] = {
4181 '%','s',
4182 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4183 '\"','%','s','\"',0};
4184 static const WCHAR install_fmt[] = {
4185 '/','I',' ','\"','%','s','\"',' ',
4186 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4187 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4188 WCHAR buffer[256], sysdir[MAX_PATH];
4189 HKEY hkey;
4190 WCHAR squished_pc[100];
4192 squash_guid(package->ProductCode,squished_pc);
4194 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4195 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4196 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4197 squished_pc);
4199 msi_reg_set_val_str( hkey, squished_pc, buffer );
4200 RegCloseKey(hkey);
4202 TRACE("Reboot command %s\n",debugstr_w(buffer));
4204 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4205 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4207 msi_reg_set_val_str( hkey, squished_pc, buffer );
4208 RegCloseKey(hkey);
4210 return ERROR_INSTALL_SUSPEND;
4213 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4215 DWORD attrib;
4216 UINT rc;
4219 * We are currently doing what should be done here in the top level Install
4220 * however for Administrative and uninstalls this step will be needed
4222 if (!package->PackagePath)
4223 return ERROR_SUCCESS;
4225 msi_set_sourcedir_props(package, TRUE);
4227 attrib = GetFileAttributesW(package->db->path);
4228 if (attrib == INVALID_FILE_ATTRIBUTES)
4230 LPWSTR prompt;
4231 LPWSTR msg;
4232 DWORD size = 0;
4234 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4235 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4236 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4237 if (rc == ERROR_MORE_DATA)
4239 prompt = msi_alloc(size * sizeof(WCHAR));
4240 MsiSourceListGetInfoW(package->ProductCode, NULL,
4241 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4242 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4244 else
4245 prompt = strdupW(package->db->path);
4247 msg = generate_error_string(package,1302,1,prompt);
4248 while(attrib == INVALID_FILE_ATTRIBUTES)
4250 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4251 if (rc == IDCANCEL)
4253 rc = ERROR_INSTALL_USEREXIT;
4254 break;
4256 attrib = GetFileAttributesW(package->db->path);
4258 msi_free(prompt);
4259 rc = ERROR_SUCCESS;
4261 else
4262 return ERROR_SUCCESS;
4264 return rc;
4267 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4269 HKEY hkey=0;
4270 LPWSTR buffer;
4271 LPWSTR productid;
4272 UINT rc,i;
4274 static const WCHAR szPropKeys[][80] =
4276 {'P','r','o','d','u','c','t','I','D',0},
4277 {'U','S','E','R','N','A','M','E',0},
4278 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4279 {0},
4282 static const WCHAR szRegKeys[][80] =
4284 {'P','r','o','d','u','c','t','I','D',0},
4285 {'R','e','g','O','w','n','e','r',0},
4286 {'R','e','g','C','o','m','p','a','n','y',0},
4287 {0},
4290 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4291 if (!productid)
4292 return ERROR_SUCCESS;
4294 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4295 if (rc != ERROR_SUCCESS)
4296 goto end;
4298 for( i = 0; szPropKeys[i][0]; i++ )
4300 buffer = msi_dup_property( package, szPropKeys[i] );
4301 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4302 msi_free( buffer );
4305 end:
4306 msi_free(productid);
4307 RegCloseKey(hkey);
4309 /* FIXME: call ui_actiondata */
4311 return ERROR_SUCCESS;
4315 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4317 UINT rc;
4319 package->script->InWhatSequence |= SEQUENCE_EXEC;
4320 rc = ACTION_ProcessExecSequence(package,FALSE);
4321 return rc;
4325 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4327 MSIPACKAGE *package = (MSIPACKAGE*)param;
4328 LPCWSTR compgroupid=NULL;
4329 LPCWSTR feature=NULL;
4330 LPCWSTR text = NULL;
4331 LPCWSTR qualifier = NULL;
4332 LPCWSTR component = NULL;
4333 LPWSTR advertise = NULL;
4334 LPWSTR output = NULL;
4335 HKEY hkey;
4336 UINT rc = ERROR_SUCCESS;
4337 MSICOMPONENT *comp;
4338 DWORD sz = 0;
4339 MSIRECORD *uirow;
4341 component = MSI_RecordGetString(rec,3);
4342 comp = get_loaded_component(package,component);
4344 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4345 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4346 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4348 TRACE("Skipping: Component %s not scheduled for install\n",
4349 debugstr_w(component));
4351 return ERROR_SUCCESS;
4354 compgroupid = MSI_RecordGetString(rec,1);
4355 qualifier = MSI_RecordGetString(rec,2);
4357 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4358 if (rc != ERROR_SUCCESS)
4359 goto end;
4361 text = MSI_RecordGetString(rec,4);
4362 feature = MSI_RecordGetString(rec,5);
4364 advertise = create_component_advertise_string(package, comp, feature);
4366 sz = strlenW(advertise);
4368 if (text)
4369 sz += lstrlenW(text);
4371 sz+=3;
4372 sz *= sizeof(WCHAR);
4374 output = msi_alloc_zero(sz);
4375 strcpyW(output,advertise);
4376 msi_free(advertise);
4378 if (text)
4379 strcatW(output,text);
4381 msi_reg_set_val_multi_str( hkey, qualifier, output );
4383 end:
4384 RegCloseKey(hkey);
4385 msi_free(output);
4387 /* the UI chunk */
4388 uirow = MSI_CreateRecord( 2 );
4389 MSI_RecordSetStringW( uirow, 1, compgroupid );
4390 MSI_RecordSetStringW( uirow, 2, qualifier);
4391 ui_actiondata( package, szPublishComponents, uirow);
4392 msiobj_release( &uirow->hdr );
4393 /* FIXME: call ui_progress? */
4395 return rc;
4399 * At present I am ignorning the advertised components part of this and only
4400 * focusing on the qualified component sets
4402 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4404 UINT rc;
4405 MSIQUERY * view;
4406 static const WCHAR ExecSeqQuery[] =
4407 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4408 '`','P','u','b','l','i','s','h',
4409 'C','o','m','p','o','n','e','n','t','`',0};
4411 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4412 if (rc != ERROR_SUCCESS)
4413 return ERROR_SUCCESS;
4415 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4416 msiobj_release(&view->hdr);
4418 return rc;
4421 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4423 MSIPACKAGE *package = (MSIPACKAGE*)param;
4424 MSIRECORD *row;
4425 MSIFILE *file;
4426 SC_HANDLE hscm, service = NULL;
4427 LPCWSTR name, disp, comp, depends, pass;
4428 LPCWSTR load_order, serv_name, key;
4429 DWORD serv_type, start_type;
4430 DWORD err_control;
4432 static const WCHAR query[] =
4433 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4434 '`','C','o','m','p','o','n','e','n','t','`',' ',
4435 'W','H','E','R','E',' ',
4436 '`','C','o','m','p','o','n','e','n','t','`',' ',
4437 '=','\'','%','s','\'',0};
4439 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4440 if (!hscm)
4442 ERR("Failed to open the SC Manager!\n");
4443 goto done;
4446 start_type = MSI_RecordGetInteger(rec, 5);
4447 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4448 goto done;
4450 depends = MSI_RecordGetString(rec, 8);
4451 if (depends && *depends)
4452 FIXME("Dependency list unhandled!\n");
4454 name = MSI_RecordGetString(rec, 2);
4455 disp = MSI_RecordGetString(rec, 3);
4456 serv_type = MSI_RecordGetInteger(rec, 4);
4457 err_control = MSI_RecordGetInteger(rec, 6);
4458 load_order = MSI_RecordGetString(rec, 7);
4459 serv_name = MSI_RecordGetString(rec, 9);
4460 pass = MSI_RecordGetString(rec, 10);
4461 comp = MSI_RecordGetString(rec, 12);
4463 /* fetch the service path */
4464 row = MSI_QueryGetRecord(package->db, query, comp);
4465 if (!row)
4467 ERR("Control query failed!\n");
4468 goto done;
4471 key = MSI_RecordGetString(row, 6);
4473 file = get_loaded_file(package, key);
4474 msiobj_release(&row->hdr);
4475 if (!file)
4477 ERR("Failed to load the service file\n");
4478 goto done;
4481 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4482 start_type, err_control, file->TargetPath,
4483 load_order, NULL, NULL, serv_name, pass);
4484 if (!service)
4486 if (GetLastError() != ERROR_SERVICE_EXISTS)
4487 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4490 done:
4491 CloseServiceHandle(service);
4492 CloseServiceHandle(hscm);
4494 return ERROR_SUCCESS;
4497 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4499 UINT rc;
4500 MSIQUERY * view;
4501 static const WCHAR ExecSeqQuery[] =
4502 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4503 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4505 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4506 if (rc != ERROR_SUCCESS)
4507 return ERROR_SUCCESS;
4509 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4510 msiobj_release(&view->hdr);
4512 return rc;
4515 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4516 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4518 LPCWSTR *vector, *temp_vector;
4519 LPWSTR p, q;
4520 DWORD sep_len;
4522 static const WCHAR separator[] = {'[','~',']',0};
4524 *numargs = 0;
4525 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4527 if (!args)
4528 return NULL;
4530 vector = msi_alloc(sizeof(LPWSTR));
4531 if (!vector)
4532 return NULL;
4534 p = args;
4537 (*numargs)++;
4538 vector[*numargs - 1] = p;
4540 if ((q = strstrW(p, separator)))
4542 *q = '\0';
4544 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4545 if (!temp_vector)
4547 msi_free(vector);
4548 return NULL;
4550 vector = temp_vector;
4552 p = q + sep_len;
4554 } while (q);
4556 return vector;
4559 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4561 MSIPACKAGE *package = (MSIPACKAGE *)param;
4562 MSICOMPONENT *comp;
4563 SC_HANDLE scm, service = NULL;
4564 LPCWSTR name, *vector = NULL;
4565 LPWSTR args;
4566 DWORD event, numargs;
4567 UINT r = ERROR_FUNCTION_FAILED;
4569 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4570 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4571 return ERROR_SUCCESS;
4573 name = MSI_RecordGetString(rec, 2);
4574 event = MSI_RecordGetInteger(rec, 3);
4575 args = strdupW(MSI_RecordGetString(rec, 4));
4577 if (!(event & msidbServiceControlEventStart))
4578 return ERROR_SUCCESS;
4580 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4581 if (!scm)
4583 ERR("Failed to open the service control manager\n");
4584 goto done;
4587 service = OpenServiceW(scm, name, SERVICE_START);
4588 if (!service)
4590 ERR("Failed to open service %s\n", debugstr_w(name));
4591 goto done;
4594 vector = msi_service_args_to_vector(args, &numargs);
4596 if (!StartServiceW(service, numargs, vector))
4598 ERR("Failed to start service %s\n", debugstr_w(name));
4599 goto done;
4602 r = ERROR_SUCCESS;
4604 done:
4605 CloseServiceHandle(service);
4606 CloseServiceHandle(scm);
4608 msi_free(args);
4609 msi_free(vector);
4610 return r;
4613 static UINT ACTION_StartServices( MSIPACKAGE *package )
4615 UINT rc;
4616 MSIQUERY *view;
4618 static const WCHAR query[] = {
4619 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4620 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4622 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4623 if (rc != ERROR_SUCCESS)
4624 return ERROR_SUCCESS;
4626 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4627 msiobj_release(&view->hdr);
4629 return rc;
4632 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4634 MSIFILE *file;
4636 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4638 if (!lstrcmpW(file->File, filename))
4639 return file;
4642 return NULL;
4645 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4647 MSIPACKAGE *package = (MSIPACKAGE*)param;
4648 LPWSTR driver, driver_path, ptr;
4649 WCHAR outpath[MAX_PATH];
4650 MSIFILE *driver_file, *setup_file;
4651 LPCWSTR desc;
4652 DWORD len, usage;
4653 UINT r = ERROR_SUCCESS;
4655 static const WCHAR driver_fmt[] = {
4656 'D','r','i','v','e','r','=','%','s',0};
4657 static const WCHAR setup_fmt[] = {
4658 'S','e','t','u','p','=','%','s',0};
4659 static const WCHAR usage_fmt[] = {
4660 'F','i','l','e','U','s','a','g','e','=','1',0};
4662 desc = MSI_RecordGetString(rec, 3);
4664 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4665 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4667 if (!driver_file || !setup_file)
4669 ERR("ODBC Driver entry not found!\n");
4670 return ERROR_FUNCTION_FAILED;
4673 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4674 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4675 lstrlenW(usage_fmt) + 1;
4676 driver = msi_alloc(len * sizeof(WCHAR));
4677 if (!driver)
4678 return ERROR_OUTOFMEMORY;
4680 ptr = driver;
4681 lstrcpyW(ptr, desc);
4682 ptr += lstrlenW(ptr) + 1;
4684 sprintfW(ptr, driver_fmt, driver_file->FileName);
4685 ptr += lstrlenW(ptr) + 1;
4687 sprintfW(ptr, setup_fmt, setup_file->FileName);
4688 ptr += lstrlenW(ptr) + 1;
4690 lstrcpyW(ptr, usage_fmt);
4691 ptr += lstrlenW(ptr) + 1;
4692 *ptr = '\0';
4694 driver_path = strdupW(driver_file->TargetPath);
4695 ptr = strrchrW(driver_path, '\\');
4696 if (ptr) *ptr = '\0';
4698 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4699 NULL, ODBC_INSTALL_COMPLETE, &usage))
4701 ERR("Failed to install SQL driver!\n");
4702 r = ERROR_FUNCTION_FAILED;
4705 msi_free(driver);
4706 msi_free(driver_path);
4708 return r;
4711 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4713 MSIPACKAGE *package = (MSIPACKAGE*)param;
4714 LPWSTR translator, translator_path, ptr;
4715 WCHAR outpath[MAX_PATH];
4716 MSIFILE *translator_file, *setup_file;
4717 LPCWSTR desc;
4718 DWORD len, usage;
4719 UINT r = ERROR_SUCCESS;
4721 static const WCHAR translator_fmt[] = {
4722 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4723 static const WCHAR setup_fmt[] = {
4724 'S','e','t','u','p','=','%','s',0};
4726 desc = MSI_RecordGetString(rec, 3);
4728 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4729 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4731 if (!translator_file || !setup_file)
4733 ERR("ODBC Translator entry not found!\n");
4734 return ERROR_FUNCTION_FAILED;
4737 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4738 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4739 translator = msi_alloc(len * sizeof(WCHAR));
4740 if (!translator)
4741 return ERROR_OUTOFMEMORY;
4743 ptr = translator;
4744 lstrcpyW(ptr, desc);
4745 ptr += lstrlenW(ptr) + 1;
4747 sprintfW(ptr, translator_fmt, translator_file->FileName);
4748 ptr += lstrlenW(ptr) + 1;
4750 sprintfW(ptr, setup_fmt, setup_file->FileName);
4751 ptr += lstrlenW(ptr) + 1;
4752 *ptr = '\0';
4754 translator_path = strdupW(translator_file->TargetPath);
4755 ptr = strrchrW(translator_path, '\\');
4756 if (ptr) *ptr = '\0';
4758 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4759 NULL, ODBC_INSTALL_COMPLETE, &usage))
4761 ERR("Failed to install SQL translator!\n");
4762 r = ERROR_FUNCTION_FAILED;
4765 msi_free(translator);
4766 msi_free(translator_path);
4768 return r;
4771 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4773 LPWSTR attrs;
4774 LPCWSTR desc, driver;
4775 WORD request = ODBC_ADD_SYS_DSN;
4776 INT registration;
4777 DWORD len;
4778 UINT r = ERROR_SUCCESS;
4780 static const WCHAR attrs_fmt[] = {
4781 'D','S','N','=','%','s',0 };
4783 desc = MSI_RecordGetString(rec, 3);
4784 driver = MSI_RecordGetString(rec, 4);
4785 registration = MSI_RecordGetInteger(rec, 5);
4787 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4788 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4790 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4791 attrs = msi_alloc(len * sizeof(WCHAR));
4792 if (!attrs)
4793 return ERROR_OUTOFMEMORY;
4795 sprintfW(attrs, attrs_fmt, desc);
4796 attrs[len - 1] = '\0';
4798 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4800 ERR("Failed to install SQL data source!\n");
4801 r = ERROR_FUNCTION_FAILED;
4804 msi_free(attrs);
4806 return r;
4809 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4811 UINT rc;
4812 MSIQUERY *view;
4814 static const WCHAR driver_query[] = {
4815 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4816 'O','D','B','C','D','r','i','v','e','r',0 };
4818 static const WCHAR translator_query[] = {
4819 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4820 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4822 static const WCHAR source_query[] = {
4823 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4824 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4826 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4827 if (rc != ERROR_SUCCESS)
4828 return ERROR_SUCCESS;
4830 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4831 msiobj_release(&view->hdr);
4833 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4834 if (rc != ERROR_SUCCESS)
4835 return ERROR_SUCCESS;
4837 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4838 msiobj_release(&view->hdr);
4840 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4841 if (rc != ERROR_SUCCESS)
4842 return ERROR_SUCCESS;
4844 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4845 msiobj_release(&view->hdr);
4847 return rc;
4850 #define ENV_ACT_SETALWAYS 0x1
4851 #define ENV_ACT_SETABSENT 0x2
4852 #define ENV_ACT_REMOVE 0x4
4853 #define ENV_ACT_REMOVEMATCH 0x8
4855 #define ENV_MOD_MACHINE 0x20000000
4856 #define ENV_MOD_APPEND 0x40000000
4857 #define ENV_MOD_PREFIX 0x80000000
4858 #define ENV_MOD_MASK 0xC0000000
4860 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4862 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4864 LPCWSTR cptr = *name;
4865 LPCWSTR ptr = *value;
4867 static const WCHAR prefix[] = {'[','~',']',0};
4868 static const int prefix_len = 3;
4870 *flags = 0;
4871 while (*cptr)
4873 if (*cptr == '=')
4874 *flags |= ENV_ACT_SETALWAYS;
4875 else if (*cptr == '+')
4876 *flags |= ENV_ACT_SETABSENT;
4877 else if (*cptr == '-')
4878 *flags |= ENV_ACT_REMOVE;
4879 else if (*cptr == '!')
4880 *flags |= ENV_ACT_REMOVEMATCH;
4881 else if (*cptr == '*')
4882 *flags |= ENV_MOD_MACHINE;
4883 else
4884 break;
4886 cptr++;
4887 (*name)++;
4890 if (!*cptr)
4892 ERR("Missing environment variable\n");
4893 return ERROR_FUNCTION_FAILED;
4896 if (!strncmpW(ptr, prefix, prefix_len))
4898 *flags |= ENV_MOD_APPEND;
4899 *value += lstrlenW(prefix);
4901 else if (lstrlenW(*value) >= prefix_len)
4903 ptr += lstrlenW(ptr) - prefix_len;
4904 if (!lstrcmpW(ptr, prefix))
4906 *flags |= ENV_MOD_PREFIX;
4907 /* the "[~]" will be removed by deformat_string */;
4911 if (!*flags ||
4912 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4913 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4914 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4915 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4917 ERR("Invalid flags: %08x\n", *flags);
4918 return ERROR_FUNCTION_FAILED;
4921 return ERROR_SUCCESS;
4924 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4926 MSIPACKAGE *package = param;
4927 LPCWSTR name, value, comp;
4928 LPWSTR data = NULL, newval = NULL;
4929 LPWSTR deformatted = NULL, ptr;
4930 DWORD flags, type, size;
4931 LONG res;
4932 HKEY env = NULL, root;
4933 LPCWSTR environment;
4935 static const WCHAR user_env[] =
4936 {'E','n','v','i','r','o','n','m','e','n','t',0};
4937 static const WCHAR machine_env[] =
4938 {'S','y','s','t','e','m','\\',
4939 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4940 'C','o','n','t','r','o','l','\\',
4941 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4942 'E','n','v','i','r','o','n','m','e','n','t',0};
4943 static const WCHAR semicolon[] = {';',0};
4945 name = MSI_RecordGetString(rec, 2);
4946 value = MSI_RecordGetString(rec, 3);
4947 comp = MSI_RecordGetString(rec, 4);
4949 res = env_set_flags(&name, &value, &flags);
4950 if (res != ERROR_SUCCESS)
4951 goto done;
4953 deformat_string(package, value, &deformatted);
4954 if (!deformatted)
4956 res = ERROR_OUTOFMEMORY;
4957 goto done;
4960 value = deformatted;
4962 if (flags & ENV_MOD_MACHINE)
4964 environment = machine_env;
4965 root = HKEY_LOCAL_MACHINE;
4967 else
4969 environment = user_env;
4970 root = HKEY_CURRENT_USER;
4973 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
4974 KEY_ALL_ACCESS, NULL, &env, NULL);
4975 if (res != ERROR_SUCCESS)
4976 goto done;
4978 if (flags & ENV_ACT_REMOVE)
4979 FIXME("Not removing environment variable on uninstall!\n");
4981 size = 0;
4982 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
4983 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
4984 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
4985 goto done;
4987 if (res != ERROR_FILE_NOT_FOUND)
4989 if (flags & ENV_ACT_SETABSENT)
4991 res = ERROR_SUCCESS;
4992 goto done;
4995 data = msi_alloc(size);
4996 if (!data)
4998 RegCloseKey(env);
4999 return ERROR_OUTOFMEMORY;
5002 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5003 if (res != ERROR_SUCCESS)
5004 goto done;
5006 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5008 res = RegDeleteKeyW(env, name);
5009 goto done;
5012 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5013 newval = msi_alloc(size);
5014 ptr = newval;
5015 if (!newval)
5017 res = ERROR_OUTOFMEMORY;
5018 goto done;
5021 if (!(flags & ENV_MOD_MASK))
5022 lstrcpyW(newval, value);
5023 else
5025 if (flags & ENV_MOD_PREFIX)
5027 lstrcpyW(newval, value);
5028 lstrcatW(newval, semicolon);
5029 ptr = newval + lstrlenW(value) + 1;
5032 lstrcpyW(ptr, data);
5034 if (flags & ENV_MOD_APPEND)
5036 lstrcatW(newval, semicolon);
5037 lstrcatW(newval, value);
5041 else
5043 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5044 newval = msi_alloc(size);
5045 if (!newval)
5047 res = ERROR_OUTOFMEMORY;
5048 goto done;
5051 lstrcpyW(newval, value);
5054 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5055 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5057 done:
5058 if (env) RegCloseKey(env);
5059 msi_free(deformatted);
5060 msi_free(data);
5061 msi_free(newval);
5062 return res;
5065 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5067 UINT rc;
5068 MSIQUERY * view;
5069 static const WCHAR ExecSeqQuery[] =
5070 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5071 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5072 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5073 if (rc != ERROR_SUCCESS)
5074 return ERROR_SUCCESS;
5076 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5077 msiobj_release(&view->hdr);
5079 return rc;
5082 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5084 typedef struct
5086 struct list entry;
5087 LPWSTR sourcename;
5088 LPWSTR destname;
5089 LPWSTR source;
5090 LPWSTR dest;
5091 } FILE_LIST;
5093 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5095 BOOL ret;
5097 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5098 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5100 WARN("Source or dest is directory, not moving\n");
5101 return FALSE;
5104 if (options == msidbMoveFileOptionsMove)
5106 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5107 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5108 if (!ret)
5110 WARN("MoveFile failed: %d\n", GetLastError());
5111 return FALSE;
5114 else
5116 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5117 ret = CopyFileW(source, dest, FALSE);
5118 if (!ret)
5120 WARN("CopyFile failed: %d\n", GetLastError());
5121 return FALSE;
5125 return TRUE;
5128 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5130 LPWSTR path, ptr;
5131 DWORD dirlen, pathlen;
5133 ptr = strrchrW(wildcard, '\\');
5134 dirlen = ptr - wildcard + 1;
5136 pathlen = dirlen + lstrlenW(filename) + 1;
5137 path = msi_alloc(pathlen * sizeof(WCHAR));
5139 lstrcpynW(path, wildcard, dirlen + 1);
5140 lstrcatW(path, filename);
5142 return path;
5145 static void free_file_entry(FILE_LIST *file)
5147 msi_free(file->source);
5148 msi_free(file->dest);
5149 msi_free(file);
5152 static void free_list(FILE_LIST *list)
5154 while (!list_empty(&list->entry))
5156 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5158 list_remove(&file->entry);
5159 free_file_entry(file);
5163 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5165 FILE_LIST *new, *file;
5166 LPWSTR ptr, filename;
5167 DWORD size;
5169 new = msi_alloc_zero(sizeof(FILE_LIST));
5170 if (!new)
5171 return FALSE;
5173 new->source = strdupW(source);
5174 ptr = strrchrW(dest, '\\') + 1;
5175 filename = strrchrW(new->source, '\\') + 1;
5177 new->sourcename = filename;
5179 if (*ptr)
5180 new->destname = ptr;
5181 else
5182 new->destname = new->sourcename;
5184 size = (ptr - dest) + lstrlenW(filename) + 1;
5185 new->dest = msi_alloc(size * sizeof(WCHAR));
5186 if (!new->dest)
5188 free_file_entry(new);
5189 return FALSE;
5192 lstrcpynW(new->dest, dest, ptr - dest + 1);
5193 lstrcatW(new->dest, filename);
5195 if (list_empty(&files->entry))
5197 list_add_head(&files->entry, &new->entry);
5198 return TRUE;
5201 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5203 if (lstrcmpW(source, file->source) < 0)
5205 list_add_before(&file->entry, &new->entry);
5206 return TRUE;
5210 list_add_after(&file->entry, &new->entry);
5211 return TRUE;
5214 BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5216 WIN32_FIND_DATAW wfd;
5217 HANDLE hfile;
5218 LPWSTR path;
5219 BOOL res;
5220 FILE_LIST files, *file;
5221 DWORD size;
5223 hfile = FindFirstFileW(source, &wfd);
5224 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5226 list_init(&files.entry);
5228 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5230 if (is_dot_dir(wfd.cFileName)) continue;
5232 path = wildcard_to_file(source, wfd.cFileName);
5233 if (!path)
5235 res = FALSE;
5236 goto done;
5239 add_wildcard(&files, path, dest);
5240 msi_free(path);
5243 /* only the first wildcard match gets renamed to dest */
5244 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5245 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5246 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5247 if (!file->dest)
5249 res = FALSE;
5250 goto done;
5253 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5255 while (!list_empty(&files.entry))
5257 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5259 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5261 list_remove(&file->entry);
5262 free_file_entry(file);
5265 res = TRUE;
5267 done:
5268 free_list(&files);
5269 FindClose(hfile);
5270 return res;
5273 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5275 MSIPACKAGE *package = param;
5276 MSICOMPONENT *comp;
5277 LPCWSTR sourcename, destname;
5278 LPWSTR sourcedir = NULL, destdir = NULL;
5279 LPWSTR source = NULL, dest = NULL;
5280 int options;
5281 DWORD size;
5282 BOOL ret, wildcards;
5284 static const WCHAR backslash[] = {'\\',0};
5286 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5287 if (!comp || !comp->Enabled ||
5288 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5290 TRACE("Component not set for install, not moving file\n");
5291 return ERROR_SUCCESS;
5294 sourcename = MSI_RecordGetString(rec, 3);
5295 destname = MSI_RecordGetString(rec, 4);
5296 options = MSI_RecordGetInteger(rec, 7);
5298 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5299 if (!sourcedir)
5300 goto done;
5302 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5303 if (!destdir)
5304 goto done;
5306 if (!sourcename)
5308 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5309 goto done;
5311 source = strdupW(sourcedir);
5312 if (!source)
5313 goto done;
5315 else
5317 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5318 source = msi_alloc(size * sizeof(WCHAR));
5319 if (!source)
5320 goto done;
5322 lstrcpyW(source, sourcedir);
5323 if (source[lstrlenW(source) - 1] != '\\')
5324 lstrcatW(source, backslash);
5325 lstrcatW(source, sourcename);
5328 wildcards = strchrW(source, '*') || strchrW(source, '?');
5330 if (!destname && !wildcards)
5332 destname = strdupW(sourcename);
5333 if (!destname)
5334 goto done;
5337 size = 0;
5338 if (destname)
5339 size = lstrlenW(destname);
5341 size += lstrlenW(destdir) + 2;
5342 dest = msi_alloc(size * sizeof(WCHAR));
5343 if (!dest)
5344 goto done;
5346 lstrcpyW(dest, destdir);
5347 if (dest[lstrlenW(dest) - 1] != '\\')
5348 lstrcatW(dest, backslash);
5350 if (destname)
5351 lstrcatW(dest, destname);
5353 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5355 ret = CreateDirectoryW(destdir, NULL);
5356 if (!ret)
5358 WARN("CreateDirectory failed: %d\n", GetLastError());
5359 return ERROR_SUCCESS;
5363 if (!wildcards)
5364 msi_move_file(source, dest, options);
5365 else
5366 move_files_wildcard(source, dest, options);
5368 done:
5369 msi_free(sourcedir);
5370 msi_free(destdir);
5371 msi_free(source);
5372 msi_free(dest);
5374 return ERROR_SUCCESS;
5377 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5379 UINT rc;
5380 MSIQUERY *view;
5382 static const WCHAR ExecSeqQuery[] =
5383 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5384 '`','M','o','v','e','F','i','l','e','`',0};
5386 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5387 if (rc != ERROR_SUCCESS)
5388 return ERROR_SUCCESS;
5390 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5391 msiobj_release(&view->hdr);
5393 return rc;
5396 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5397 LPCSTR action, LPCWSTR table )
5399 static const WCHAR query[] = {
5400 'S','E','L','E','C','T',' ','*',' ',
5401 'F','R','O','M',' ','`','%','s','`',0 };
5402 MSIQUERY *view = NULL;
5403 DWORD count = 0;
5404 UINT r;
5406 r = MSI_OpenQuery( package->db, &view, query, table );
5407 if (r == ERROR_SUCCESS)
5409 r = MSI_IterateRecords(view, &count, NULL, package);
5410 msiobj_release(&view->hdr);
5413 if (count)
5414 FIXME("%s -> %u ignored %s table values\n",
5415 action, count, debugstr_w(table));
5417 return ERROR_SUCCESS;
5420 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5422 TRACE("%p\n", package);
5423 return ERROR_SUCCESS;
5426 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5428 static const WCHAR table[] =
5429 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5430 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5433 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5435 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5436 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5439 static UINT ACTION_BindImage( MSIPACKAGE *package )
5441 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5442 return msi_unimplemented_action_stub( package, "BindImage", table );
5445 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5447 static const WCHAR table[] = {
5448 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5449 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5452 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5454 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5455 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5458 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5460 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5461 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5464 static UINT ACTION_StopServices( MSIPACKAGE *package )
5466 static const WCHAR table[] = {
5467 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5468 return msi_unimplemented_action_stub( package, "StopServices", table );
5471 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5473 static const WCHAR table[] = {
5474 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5475 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5477 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5479 static const WCHAR table[] = {
5480 'P','r','o','d','u','c','t','I','D',0 };
5481 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5484 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5486 static const WCHAR table[] = {
5487 'E','n','v','i','r','o','n','m','e','n','t',0 };
5488 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5491 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5493 static const WCHAR table[] = {
5494 'M','s','i','A','s','s','e','m','b','l','y',0 };
5495 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5498 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5500 static const WCHAR table[] = {
5501 'M','s','i','A','s','s','e','m','b','l','y',0 };
5502 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5505 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5507 static const WCHAR table[] = { 'F','o','n','t',0 };
5508 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5511 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5513 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5514 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5517 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5519 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5520 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5523 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5525 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5526 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5529 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5531 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5532 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5535 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5537 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5538 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5541 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5543 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5544 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5547 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5549 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5550 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5553 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5555 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5556 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5559 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5561 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5562 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5565 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5567 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5568 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5571 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5573 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5574 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5577 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5579 static const WCHAR table[] = { 'A','p','p','I','d',0 };
5580 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5583 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5585 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5586 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5589 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5591 static const WCHAR table[] = { 'M','I','M','E',0 };
5592 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5595 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5597 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5598 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5601 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5603 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5604 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5607 static const struct _actions StandardActions[] = {
5608 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5609 { szAppSearch, ACTION_AppSearch },
5610 { szBindImage, ACTION_BindImage },
5611 { szCCPSearch, ACTION_CCPSearch },
5612 { szCostFinalize, ACTION_CostFinalize },
5613 { szCostInitialize, ACTION_CostInitialize },
5614 { szCreateFolders, ACTION_CreateFolders },
5615 { szCreateShortcuts, ACTION_CreateShortcuts },
5616 { szDeleteServices, ACTION_DeleteServices },
5617 { szDisableRollback, NULL },
5618 { szDuplicateFiles, ACTION_DuplicateFiles },
5619 { szExecuteAction, ACTION_ExecuteAction },
5620 { szFileCost, ACTION_FileCost },
5621 { szFindRelatedProducts, ACTION_FindRelatedProducts },
5622 { szForceReboot, ACTION_ForceReboot },
5623 { szInstallAdminPackage, NULL },
5624 { szInstallExecute, ACTION_InstallExecute },
5625 { szInstallExecuteAgain, ACTION_InstallExecute },
5626 { szInstallFiles, ACTION_InstallFiles},
5627 { szInstallFinalize, ACTION_InstallFinalize },
5628 { szInstallInitialize, ACTION_InstallInitialize },
5629 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5630 { szInstallValidate, ACTION_InstallValidate },
5631 { szIsolateComponents, ACTION_IsolateComponents },
5632 { szLaunchConditions, ACTION_LaunchConditions },
5633 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5634 { szMoveFiles, ACTION_MoveFiles },
5635 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5636 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5637 { szInstallODBC, ACTION_InstallODBC },
5638 { szInstallServices, ACTION_InstallServices },
5639 { szPatchFiles, ACTION_PatchFiles },
5640 { szProcessComponents, ACTION_ProcessComponents },
5641 { szPublishComponents, ACTION_PublishComponents },
5642 { szPublishFeatures, ACTION_PublishFeatures },
5643 { szPublishProduct, ACTION_PublishProduct },
5644 { szRegisterClassInfo, ACTION_RegisterClassInfo },
5645 { szRegisterComPlus, ACTION_RegisterComPlus},
5646 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5647 { szRegisterFonts, ACTION_RegisterFonts },
5648 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5649 { szRegisterProduct, ACTION_RegisterProduct },
5650 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5651 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5652 { szRegisterUser, ACTION_RegisterUser },
5653 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5654 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5655 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5656 { szRemoveFiles, ACTION_RemoveFiles },
5657 { szRemoveFolders, ACTION_RemoveFolders },
5658 { szRemoveIniValues, ACTION_RemoveIniValues },
5659 { szRemoveODBC, ACTION_RemoveODBC },
5660 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5661 { szRemoveShortcuts, ACTION_RemoveShortcuts },
5662 { szResolveSource, ACTION_ResolveSource },
5663 { szRMCCPSearch, ACTION_RMCCPSearch },
5664 { szScheduleReboot, NULL },
5665 { szSelfRegModules, ACTION_SelfRegModules },
5666 { szSelfUnregModules, ACTION_SelfUnregModules },
5667 { szSetODBCFolders, NULL },
5668 { szStartServices, ACTION_StartServices },
5669 { szStopServices, ACTION_StopServices },
5670 { szUnpublishComponents, ACTION_UnpublishComponents },
5671 { szUnpublishFeatures, ACTION_UnpublishFeatures },
5672 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5673 { szUnregisterComPlus, ACTION_UnregisterComPlus },
5674 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5675 { szUnregisterFonts, ACTION_UnregisterFonts },
5676 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5677 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5678 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5679 { szValidateProductID, ACTION_ValidateProductID },
5680 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5681 { szWriteIniValues, ACTION_WriteIniValues },
5682 { szWriteRegistryValues, ACTION_WriteRegistryValues },
5683 { NULL, NULL },