msi: Properly register features.
[wine/wine64.git] / dlls / msi / action.c
blobca46448698c767386366d65d1cecc48d2b3ab7df
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 /****************************************************
612 * TOP level entry points
613 *****************************************************/
615 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
616 LPCWSTR szCommandLine )
618 UINT rc;
619 BOOL ui = FALSE, ui_exists;
620 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
621 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
622 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
624 MSI_SetPropertyW(package, szAction, szInstall);
626 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
628 package->script->InWhatSequence = SEQUENCE_INSTALL;
630 if (szPackagePath)
632 LPWSTR p, check, dir;
633 LPCWSTR file;
635 dir = strdupW(szPackagePath);
636 p = strrchrW(dir, '\\');
637 if (p)
639 *(++p) = 0;
640 file = szPackagePath + (p - dir);
642 else
644 msi_free(dir);
645 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
646 GetCurrentDirectoryW(MAX_PATH, dir);
647 lstrcatW(dir, cszbs);
648 file = szPackagePath;
651 msi_free( package->PackagePath );
652 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
653 if (!package->PackagePath)
655 msi_free(dir);
656 return ERROR_OUTOFMEMORY;
659 lstrcpyW(package->PackagePath, dir);
660 lstrcatW(package->PackagePath, file);
662 check = msi_dup_property( package, cszSourceDir );
663 if (!check)
664 MSI_SetPropertyW(package, cszSourceDir, dir);
665 msi_free(check);
667 check = msi_dup_property( package, cszSOURCEDIR );
668 if (!check)
669 MSI_SetPropertyW(package, cszSOURCEDIR, dir);
671 msi_free(dir);
672 msi_free(check);
675 msi_parse_command_line( package, szCommandLine );
677 msi_apply_transforms( package );
678 msi_apply_patches( package );
680 /* properties may have been added by a transform */
681 msi_clone_properties( package );
683 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
685 package->script->InWhatSequence |= SEQUENCE_UI;
686 rc = ACTION_ProcessUISequence(package);
687 ui = TRUE;
688 ui_exists = ui_sequence_exists(package);
689 if (rc == ERROR_SUCCESS || !ui_exists)
691 package->script->InWhatSequence |= SEQUENCE_EXEC;
692 rc = ACTION_ProcessExecSequence(package,ui_exists);
695 else
696 rc = ACTION_ProcessExecSequence(package,FALSE);
698 if (rc == -1)
700 /* install was halted but should be considered a success */
701 rc = ERROR_SUCCESS;
704 package->script->CurrentlyScripting= FALSE;
706 /* process the ending type action */
707 if (rc == ERROR_SUCCESS)
708 ACTION_PerformActionSequence(package,-1,ui);
709 else if (rc == ERROR_INSTALL_USEREXIT)
710 ACTION_PerformActionSequence(package,-2,ui);
711 else if (rc == ERROR_INSTALL_SUSPEND)
712 ACTION_PerformActionSequence(package,-4,ui);
713 else /* failed */
714 ACTION_PerformActionSequence(package,-3,ui);
716 /* finish up running custom actions */
717 ACTION_FinishCustomActions(package);
719 return rc;
722 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
724 UINT rc = ERROR_SUCCESS;
725 MSIRECORD * row = 0;
726 static const WCHAR ExecSeqQuery[] =
727 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
728 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
729 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
730 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
732 static const WCHAR UISeqQuery[] =
733 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
734 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
735 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
736 ' ', '=',' ','%','i',0};
738 if (UI)
739 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
740 else
741 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
743 if (row)
745 LPCWSTR action, cond;
747 TRACE("Running the actions\n");
749 /* check conditions */
750 cond = MSI_RecordGetString(row,2);
752 /* this is a hack to skip errors in the condition code */
753 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
754 goto end;
756 action = MSI_RecordGetString(row,1);
757 if (!action)
759 ERR("failed to fetch action\n");
760 rc = ERROR_FUNCTION_FAILED;
761 goto end;
764 if (UI)
765 rc = ACTION_PerformUIAction(package,action,-1);
766 else
767 rc = ACTION_PerformAction(package,action,-1,FALSE);
768 end:
769 msiobj_release(&row->hdr);
771 else
772 rc = ERROR_SUCCESS;
774 return rc;
777 typedef struct {
778 MSIPACKAGE* package;
779 BOOL UI;
780 } iterate_action_param;
782 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
784 iterate_action_param *iap= (iterate_action_param*)param;
785 UINT rc;
786 LPCWSTR cond, action;
788 action = MSI_RecordGetString(row,1);
789 if (!action)
791 ERR("Error is retrieving action name\n");
792 return ERROR_FUNCTION_FAILED;
795 /* check conditions */
796 cond = MSI_RecordGetString(row,2);
798 /* this is a hack to skip errors in the condition code */
799 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
801 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
802 return ERROR_SUCCESS;
805 if (iap->UI)
806 rc = ACTION_PerformUIAction(iap->package,action,-1);
807 else
808 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
810 msi_dialog_check_messages( NULL );
812 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
813 rc = iap->package->CurrentInstallState;
815 if (rc == ERROR_FUNCTION_NOT_CALLED)
816 rc = ERROR_SUCCESS;
818 if (rc != ERROR_SUCCESS)
819 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
821 return rc;
824 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
826 MSIQUERY * view;
827 UINT r;
828 static const WCHAR query[] =
829 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
830 '`','%','s','`',
831 ' ','W','H','E','R','E',' ',
832 '`','S','e','q','u','e','n','c','e','`',' ',
833 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
834 '`','S','e','q','u','e','n','c','e','`',0};
835 iterate_action_param iap;
838 * FIXME: probably should be checking UILevel in the
839 * ACTION_PerformUIAction/ACTION_PerformAction
840 * rather than saving the UI level here. Those
841 * two functions can be merged too.
843 iap.package = package;
844 iap.UI = TRUE;
846 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
848 r = MSI_OpenQuery( package->db, &view, query, szTable );
849 if (r == ERROR_SUCCESS)
851 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
852 msiobj_release(&view->hdr);
855 return r;
858 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
860 MSIQUERY * view;
861 UINT rc;
862 static const WCHAR ExecSeqQuery[] =
863 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
864 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
865 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
866 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
867 'O','R','D','E','R',' ', 'B','Y',' ',
868 '`','S','e','q','u','e','n','c','e','`',0 };
869 MSIRECORD * row = 0;
870 static const WCHAR IVQuery[] =
871 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
872 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
873 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
874 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
875 ' ','\'', 'I','n','s','t','a','l','l',
876 'V','a','l','i','d','a','t','e','\'', 0};
877 INT seq = 0;
878 iterate_action_param iap;
880 iap.package = package;
881 iap.UI = FALSE;
883 if (package->script->ExecuteSequenceRun)
885 TRACE("Execute Sequence already Run\n");
886 return ERROR_SUCCESS;
889 package->script->ExecuteSequenceRun = TRUE;
891 /* get the sequence number */
892 if (UIran)
894 row = MSI_QueryGetRecord(package->db, IVQuery);
895 if( !row )
896 return ERROR_FUNCTION_FAILED;
897 seq = MSI_RecordGetInteger(row,1);
898 msiobj_release(&row->hdr);
901 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
902 if (rc == ERROR_SUCCESS)
904 TRACE("Running the actions\n");
906 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
907 msiobj_release(&view->hdr);
910 return rc;
913 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
915 MSIQUERY * view;
916 UINT rc;
917 static const WCHAR ExecSeqQuery [] =
918 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
919 '`','I','n','s','t','a','l','l',
920 'U','I','S','e','q','u','e','n','c','e','`',
921 ' ','W','H','E','R','E',' ',
922 '`','S','e','q','u','e','n','c','e','`',' ',
923 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
924 '`','S','e','q','u','e','n','c','e','`',0};
925 iterate_action_param iap;
927 iap.package = package;
928 iap.UI = TRUE;
930 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
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 /********************************************************
944 * ACTION helper functions and functions that perform the actions
945 *******************************************************/
946 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
947 UINT* rc, BOOL force )
949 BOOL ret = FALSE;
950 BOOL run = force;
951 int i;
953 if (!run && !package->script->CurrentlyScripting)
954 run = TRUE;
956 if (!run)
958 if (strcmpW(action,szInstallFinalize) == 0 ||
959 strcmpW(action,szInstallExecute) == 0 ||
960 strcmpW(action,szInstallExecuteAgain) == 0)
961 run = TRUE;
964 i = 0;
965 while (StandardActions[i].action != NULL)
967 if (strcmpW(StandardActions[i].action, action)==0)
969 if (!run)
971 ui_actioninfo(package, action, TRUE, 0);
972 *rc = schedule_action(package,INSTALL_SCRIPT,action);
973 ui_actioninfo(package, action, FALSE, *rc);
975 else
977 ui_actionstart(package, action);
978 if (StandardActions[i].handler)
980 *rc = StandardActions[i].handler(package);
982 else
984 FIXME("unhandled standard action %s\n",debugstr_w(action));
985 *rc = ERROR_SUCCESS;
988 ret = TRUE;
989 break;
991 i++;
993 return ret;
996 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
997 UINT* rc, UINT script, BOOL force )
999 BOOL ret=FALSE;
1000 UINT arc;
1002 arc = ACTION_CustomAction(package, action, script, force);
1004 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1006 *rc = arc;
1007 ret = TRUE;
1009 return ret;
1013 * A lot of actions are really important even if they don't do anything
1014 * explicit... Lots of properties are set at the beginning of the installation
1015 * CostFinalize does a bunch of work to translate the directories and such
1017 * But until I get write access to the database that is hard, so I am going to
1018 * hack it to see if I can get something to run.
1020 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1022 UINT rc = ERROR_SUCCESS;
1023 BOOL handled;
1025 TRACE("Performing action (%s)\n",debugstr_w(action));
1027 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1029 if (!handled)
1030 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1032 if (!handled)
1034 FIXME("unhandled msi action %s\n",debugstr_w(action));
1035 rc = ERROR_FUNCTION_NOT_CALLED;
1038 return rc;
1041 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1043 UINT rc = ERROR_SUCCESS;
1044 BOOL handled = FALSE;
1046 TRACE("Performing action (%s)\n",debugstr_w(action));
1048 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1050 if (!handled)
1051 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1053 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1054 handled = TRUE;
1056 if (!handled)
1058 FIXME("unhandled msi action %s\n",debugstr_w(action));
1059 rc = ERROR_FUNCTION_NOT_CALLED;
1062 return rc;
1067 * Actual Action Handlers
1070 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1072 MSIPACKAGE *package = (MSIPACKAGE*)param;
1073 LPCWSTR dir;
1074 LPWSTR full_path;
1075 MSIRECORD *uirow;
1076 MSIFOLDER *folder;
1078 dir = MSI_RecordGetString(row,1);
1079 if (!dir)
1081 ERR("Unable to get folder id\n");
1082 return ERROR_SUCCESS;
1085 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1086 if (!full_path)
1088 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1089 return ERROR_SUCCESS;
1092 TRACE("Folder is %s\n",debugstr_w(full_path));
1094 /* UI stuff */
1095 uirow = MSI_CreateRecord(1);
1096 MSI_RecordSetStringW(uirow,1,full_path);
1097 ui_actiondata(package,szCreateFolders,uirow);
1098 msiobj_release( &uirow->hdr );
1100 if (folder->State == 0)
1101 create_full_pathW(full_path);
1103 folder->State = 3;
1105 msi_free(full_path);
1106 return ERROR_SUCCESS;
1109 /* FIXME: probably should merge this with the above function */
1110 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1112 UINT rc = ERROR_SUCCESS;
1113 MSIFOLDER *folder;
1114 LPWSTR install_path;
1116 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1117 if (!install_path)
1118 return ERROR_FUNCTION_FAILED;
1120 /* create the path */
1121 if (folder->State == 0)
1123 create_full_pathW(install_path);
1124 folder->State = 2;
1126 msi_free(install_path);
1128 return rc;
1131 UINT msi_create_component_directories( MSIPACKAGE *package )
1133 MSICOMPONENT *comp;
1135 /* create all the folders required by the components are going to install */
1136 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1138 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1139 continue;
1140 msi_create_directory( package, comp->Directory );
1143 return ERROR_SUCCESS;
1147 * Also we cannot enable/disable components either, so for now I am just going
1148 * to do all the directories for all the components.
1150 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1152 static const WCHAR ExecSeqQuery[] =
1153 {'S','E','L','E','C','T',' ',
1154 '`','D','i','r','e','c','t','o','r','y','_','`',
1155 ' ','F','R','O','M',' ',
1156 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1157 UINT rc;
1158 MSIQUERY *view;
1160 /* create all the empty folders specified in the CreateFolder table */
1161 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1162 if (rc != ERROR_SUCCESS)
1163 return ERROR_SUCCESS;
1165 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1166 msiobj_release(&view->hdr);
1168 msi_create_component_directories( package );
1170 return rc;
1173 static UINT load_component( MSIRECORD *row, LPVOID param )
1175 MSIPACKAGE *package = param;
1176 MSICOMPONENT *comp;
1178 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1179 if (!comp)
1180 return ERROR_FUNCTION_FAILED;
1182 list_add_tail( &package->components, &comp->entry );
1184 /* fill in the data */
1185 comp->Component = msi_dup_record_field( row, 1 );
1187 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1189 comp->ComponentId = msi_dup_record_field( row, 2 );
1190 comp->Directory = msi_dup_record_field( row, 3 );
1191 comp->Attributes = MSI_RecordGetInteger(row,4);
1192 comp->Condition = msi_dup_record_field( row, 5 );
1193 comp->KeyPath = msi_dup_record_field( row, 6 );
1195 comp->Installed = INSTALLSTATE_UNKNOWN;
1196 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1198 return ERROR_SUCCESS;
1201 static UINT load_all_components( MSIPACKAGE *package )
1203 static const WCHAR query[] = {
1204 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1205 '`','C','o','m','p','o','n','e','n','t','`',0 };
1206 MSIQUERY *view;
1207 UINT r;
1209 if (!list_empty(&package->components))
1210 return ERROR_SUCCESS;
1212 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1213 if (r != ERROR_SUCCESS)
1214 return r;
1216 r = MSI_IterateRecords(view, NULL, load_component, package);
1217 msiobj_release(&view->hdr);
1218 return r;
1221 typedef struct {
1222 MSIPACKAGE *package;
1223 MSIFEATURE *feature;
1224 } _ilfs;
1226 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1228 ComponentList *cl;
1230 cl = msi_alloc( sizeof (*cl) );
1231 if ( !cl )
1232 return ERROR_NOT_ENOUGH_MEMORY;
1233 cl->component = comp;
1234 list_add_tail( &feature->Components, &cl->entry );
1236 return ERROR_SUCCESS;
1239 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1241 FeatureList *fl;
1243 fl = msi_alloc( sizeof(*fl) );
1244 if ( !fl )
1245 return ERROR_NOT_ENOUGH_MEMORY;
1246 fl->feature = child;
1247 list_add_tail( &parent->Children, &fl->entry );
1249 return ERROR_SUCCESS;
1252 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1254 _ilfs* ilfs= (_ilfs*)param;
1255 LPCWSTR component;
1256 MSICOMPONENT *comp;
1258 component = MSI_RecordGetString(row,1);
1260 /* check to see if the component is already loaded */
1261 comp = get_loaded_component( ilfs->package, component );
1262 if (!comp)
1264 ERR("unknown component %s\n", debugstr_w(component));
1265 return ERROR_FUNCTION_FAILED;
1268 add_feature_component( ilfs->feature, comp );
1269 comp->Enabled = TRUE;
1271 return ERROR_SUCCESS;
1274 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1276 MSIFEATURE *feature;
1278 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1280 if ( !lstrcmpW( feature->Feature, name ) )
1281 return feature;
1284 return NULL;
1287 static UINT load_feature(MSIRECORD * row, LPVOID param)
1289 MSIPACKAGE* package = (MSIPACKAGE*)param;
1290 MSIFEATURE* feature;
1291 static const WCHAR Query1[] =
1292 {'S','E','L','E','C','T',' ',
1293 '`','C','o','m','p','o','n','e','n','t','_','`',
1294 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1295 'C','o','m','p','o','n','e','n','t','s','`',' ',
1296 'W','H','E','R','E',' ',
1297 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1298 MSIQUERY * view;
1299 UINT rc;
1300 _ilfs ilfs;
1302 /* fill in the data */
1304 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1305 if (!feature)
1306 return ERROR_NOT_ENOUGH_MEMORY;
1308 list_init( &feature->Children );
1309 list_init( &feature->Components );
1311 feature->Feature = msi_dup_record_field( row, 1 );
1313 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1315 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1316 feature->Title = msi_dup_record_field( row, 3 );
1317 feature->Description = msi_dup_record_field( row, 4 );
1319 if (!MSI_RecordIsNull(row,5))
1320 feature->Display = MSI_RecordGetInteger(row,5);
1322 feature->Level= MSI_RecordGetInteger(row,6);
1323 feature->Directory = msi_dup_record_field( row, 7 );
1324 feature->Attributes = MSI_RecordGetInteger(row,8);
1326 feature->Installed = INSTALLSTATE_UNKNOWN;
1327 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1329 list_add_tail( &package->features, &feature->entry );
1331 /* load feature components */
1333 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1334 if (rc != ERROR_SUCCESS)
1335 return ERROR_SUCCESS;
1337 ilfs.package = package;
1338 ilfs.feature = feature;
1340 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1341 msiobj_release(&view->hdr);
1343 return ERROR_SUCCESS;
1346 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1348 MSIPACKAGE* package = (MSIPACKAGE*)param;
1349 MSIFEATURE *parent, *child;
1351 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1352 if (!child)
1353 return ERROR_FUNCTION_FAILED;
1355 if (!child->Feature_Parent)
1356 return ERROR_SUCCESS;
1358 parent = find_feature_by_name( package, child->Feature_Parent );
1359 if (!parent)
1360 return ERROR_FUNCTION_FAILED;
1362 add_feature_child( parent, child );
1363 return ERROR_SUCCESS;
1366 static UINT load_all_features( MSIPACKAGE *package )
1368 static const WCHAR query[] = {
1369 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1370 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1371 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1372 MSIQUERY *view;
1373 UINT r;
1375 if (!list_empty(&package->features))
1376 return ERROR_SUCCESS;
1378 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1379 if (r != ERROR_SUCCESS)
1380 return r;
1382 r = MSI_IterateRecords( view, NULL, load_feature, package );
1383 if (r != ERROR_SUCCESS)
1384 return r;
1386 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1387 msiobj_release( &view->hdr );
1389 return r;
1392 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1394 if (!p)
1395 return p;
1396 p = strchrW(p, ch);
1397 if (!p)
1398 return p;
1399 *p = 0;
1400 return p+1;
1403 static UINT load_file(MSIRECORD *row, LPVOID param)
1405 MSIPACKAGE* package = (MSIPACKAGE*)param;
1406 LPCWSTR component;
1407 MSIFILE *file;
1409 /* fill in the data */
1411 file = msi_alloc_zero( sizeof (MSIFILE) );
1412 if (!file)
1413 return ERROR_NOT_ENOUGH_MEMORY;
1415 file->File = msi_dup_record_field( row, 1 );
1417 component = MSI_RecordGetString( row, 2 );
1418 file->Component = get_loaded_component( package, component );
1420 if (!file->Component)
1421 ERR("Unfound Component %s\n",debugstr_w(component));
1423 file->FileName = msi_dup_record_field( row, 3 );
1424 reduce_to_longfilename( file->FileName );
1426 file->ShortName = msi_dup_record_field( row, 3 );
1427 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1429 file->FileSize = MSI_RecordGetInteger( row, 4 );
1430 file->Version = msi_dup_record_field( row, 5 );
1431 file->Language = msi_dup_record_field( row, 6 );
1432 file->Attributes = MSI_RecordGetInteger( row, 7 );
1433 file->Sequence = MSI_RecordGetInteger( row, 8 );
1435 file->state = msifs_invalid;
1437 /* if the compressed bits are not set in the file attributes,
1438 * then read the information from the package word count property
1440 if (file->Attributes & msidbFileAttributesCompressed)
1442 file->IsCompressed = TRUE;
1444 else if (file->Attributes & msidbFileAttributesNoncompressed)
1446 file->IsCompressed = FALSE;
1448 else
1450 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1453 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1455 list_add_tail( &package->files, &file->entry );
1457 return ERROR_SUCCESS;
1460 static UINT load_all_files(MSIPACKAGE *package)
1462 MSIQUERY * view;
1463 UINT rc;
1464 static const WCHAR Query[] =
1465 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1466 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1467 '`','S','e','q','u','e','n','c','e','`', 0};
1469 if (!list_empty(&package->files))
1470 return ERROR_SUCCESS;
1472 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1473 if (rc != ERROR_SUCCESS)
1474 return ERROR_SUCCESS;
1476 rc = MSI_IterateRecords(view, NULL, load_file, package);
1477 msiobj_release(&view->hdr);
1479 return ERROR_SUCCESS;
1482 static UINT load_folder( MSIRECORD *row, LPVOID param )
1484 MSIPACKAGE *package = param;
1485 static const WCHAR szDot[] = { '.',0 };
1486 static WCHAR szEmpty[] = { 0 };
1487 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1488 MSIFOLDER *folder;
1490 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1491 if (!folder)
1492 return ERROR_NOT_ENOUGH_MEMORY;
1494 folder->Directory = msi_dup_record_field( row, 1 );
1496 TRACE("%s\n", debugstr_w(folder->Directory));
1498 p = msi_dup_record_field(row, 3);
1500 /* split src and target dir */
1501 tgt_short = p;
1502 src_short = folder_split_path( p, ':' );
1504 /* split the long and short paths */
1505 tgt_long = folder_split_path( tgt_short, '|' );
1506 src_long = folder_split_path( src_short, '|' );
1508 /* check for no-op dirs */
1509 if (!lstrcmpW(szDot, tgt_short))
1510 tgt_short = szEmpty;
1511 if (!lstrcmpW(szDot, src_short))
1512 src_short = szEmpty;
1514 if (!tgt_long)
1515 tgt_long = tgt_short;
1517 if (!src_short) {
1518 src_short = tgt_short;
1519 src_long = tgt_long;
1522 if (!src_long)
1523 src_long = src_short;
1525 /* FIXME: use the target short path too */
1526 folder->TargetDefault = strdupW(tgt_long);
1527 folder->SourceShortPath = strdupW(src_short);
1528 folder->SourceLongPath = strdupW(src_long);
1529 msi_free(p);
1531 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1532 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1533 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1535 folder->Parent = msi_dup_record_field( row, 2 );
1537 folder->Property = msi_dup_property( package, folder->Directory );
1539 list_add_tail( &package->folders, &folder->entry );
1541 TRACE("returning %p\n", folder);
1543 return ERROR_SUCCESS;
1546 static UINT load_all_folders( MSIPACKAGE *package )
1548 static const WCHAR query[] = {
1549 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1550 '`','D','i','r','e','c','t','o','r','y','`',0 };
1551 MSIQUERY *view;
1552 UINT r;
1554 if (!list_empty(&package->folders))
1555 return ERROR_SUCCESS;
1557 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1558 if (r != ERROR_SUCCESS)
1559 return r;
1561 r = MSI_IterateRecords(view, NULL, load_folder, package);
1562 msiobj_release(&view->hdr);
1563 return r;
1567 * I am not doing any of the costing functionality yet.
1568 * Mostly looking at doing the Component and Feature loading
1570 * The native MSI does A LOT of modification to tables here. Mostly adding
1571 * a lot of temporary columns to the Feature and Component tables.
1573 * note: Native msi also tracks the short filename. But I am only going to
1574 * track the long ones. Also looking at this directory table
1575 * it appears that the directory table does not get the parents
1576 * resolved base on property only based on their entries in the
1577 * directory table.
1579 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1581 static const WCHAR szCosting[] =
1582 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1583 static const WCHAR szZero[] = { '0', 0 };
1585 MSI_SetPropertyW(package, szCosting, szZero);
1586 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1588 load_all_components( package );
1589 load_all_features( package );
1590 load_all_files( package );
1591 load_all_folders( package );
1593 return ERROR_SUCCESS;
1596 static UINT execute_script(MSIPACKAGE *package, UINT script )
1598 int i;
1599 UINT rc = ERROR_SUCCESS;
1601 TRACE("Executing Script %i\n",script);
1603 if (!package->script)
1605 ERR("no script!\n");
1606 return ERROR_FUNCTION_FAILED;
1609 for (i = 0; i < package->script->ActionCount[script]; i++)
1611 LPWSTR action;
1612 action = package->script->Actions[script][i];
1613 ui_actionstart(package, action);
1614 TRACE("Executing Action (%s)\n",debugstr_w(action));
1615 rc = ACTION_PerformAction(package, action, script, TRUE);
1616 if (rc != ERROR_SUCCESS)
1617 break;
1619 msi_free_action_script(package, script);
1620 return rc;
1623 static UINT ACTION_FileCost(MSIPACKAGE *package)
1625 return ERROR_SUCCESS;
1628 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1630 MSICOMPONENT *comp;
1632 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1634 INSTALLSTATE res;
1636 if (!comp->ComponentId)
1637 continue;
1639 res = MsiGetComponentPathW( package->ProductCode,
1640 comp->ComponentId, NULL, NULL);
1641 if (res < 0)
1642 res = INSTALLSTATE_ABSENT;
1643 comp->Installed = res;
1647 /* scan for and update current install states */
1648 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1650 MSICOMPONENT *comp;
1651 MSIFEATURE *feature;
1653 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1655 ComponentList *cl;
1656 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1658 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1660 comp= cl->component;
1662 if (!comp->ComponentId)
1664 res = INSTALLSTATE_ABSENT;
1665 break;
1668 if (res == INSTALLSTATE_ABSENT)
1669 res = comp->Installed;
1670 else
1672 if (res == comp->Installed)
1673 continue;
1675 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1676 res != INSTALLSTATE_SOURCE)
1678 res = INSTALLSTATE_INCOMPLETE;
1682 feature->Installed = res;
1686 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1687 INSTALLSTATE state)
1689 static const WCHAR all[]={'A','L','L',0};
1690 LPWSTR override;
1691 MSIFEATURE *feature;
1693 override = msi_dup_property( package, property );
1694 if (!override)
1695 return FALSE;
1697 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1699 if (strcmpiW(override,all)==0)
1700 msi_feature_set_state( feature, state );
1701 else
1703 LPWSTR ptr = override;
1704 LPWSTR ptr2 = strchrW(override,',');
1706 while (ptr)
1708 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1709 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1711 msi_feature_set_state( feature, state );
1712 break;
1714 if (ptr2)
1716 ptr=ptr2+1;
1717 ptr2 = strchrW(ptr,',');
1719 else
1720 break;
1724 msi_free(override);
1726 return TRUE;
1729 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1731 int install_level;
1732 static const WCHAR szlevel[] =
1733 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1734 static const WCHAR szAddLocal[] =
1735 {'A','D','D','L','O','C','A','L',0};
1736 static const WCHAR szRemove[] =
1737 {'R','E','M','O','V','E',0};
1738 static const WCHAR szReinstall[] =
1739 {'R','E','I','N','S','T','A','L','L',0};
1740 BOOL override = FALSE;
1741 MSICOMPONENT* component;
1742 MSIFEATURE *feature;
1745 /* I do not know if this is where it should happen.. but */
1747 TRACE("Checking Install Level\n");
1749 install_level = msi_get_property_int( package, szlevel, 1 );
1751 /* ok here is the _real_ rub
1752 * all these activation/deactivation things happen in order and things
1753 * later on the list override things earlier on the list.
1754 * 1) INSTALLLEVEL processing
1755 * 2) ADDLOCAL
1756 * 3) REMOVE
1757 * 4) ADDSOURCE
1758 * 5) ADDDEFAULT
1759 * 6) REINSTALL
1760 * 7) COMPADDLOCAL
1761 * 8) COMPADDSOURCE
1762 * 9) FILEADDLOCAL
1763 * 10) FILEADDSOURCE
1764 * 11) FILEADDDEFAULT
1765 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1766 * ignored for all the features. seems strange, especially since it is not
1767 * documented anywhere, but it is how it works.
1769 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1770 * REMOVE are the big ones, since we don't handle administrative installs
1771 * yet anyway.
1773 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1774 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1775 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1777 if (!override)
1779 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1781 BOOL feature_state = ((feature->Level > 0) &&
1782 (feature->Level <= install_level));
1784 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1786 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1787 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1788 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1789 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1790 else
1791 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1795 /* disable child features of unselected parent features */
1796 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1798 FeatureList *fl;
1800 if (feature->Level > 0 && feature->Level <= install_level)
1801 continue;
1803 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1804 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1807 else
1809 /* set the Preselected Property */
1810 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1811 static const WCHAR szOne[] = { '1', 0 };
1813 MSI_SetPropertyW(package,szPreselected,szOne);
1817 * now we want to enable or disable components base on feature
1820 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1822 ComponentList *cl;
1824 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1825 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1827 /* features with components that have compressed files are made local */
1828 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1830 if (cl->component->Enabled &&
1831 cl->component->ForceLocalState &&
1832 feature->Action == INSTALLSTATE_SOURCE)
1834 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1835 break;
1839 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1841 component = cl->component;
1843 if (!component->Enabled)
1844 continue;
1846 switch (feature->Action)
1848 case INSTALLSTATE_ABSENT:
1849 component->anyAbsent = 1;
1850 break;
1851 case INSTALLSTATE_ADVERTISED:
1852 component->hasAdvertiseFeature = 1;
1853 break;
1854 case INSTALLSTATE_SOURCE:
1855 component->hasSourceFeature = 1;
1856 break;
1857 case INSTALLSTATE_LOCAL:
1858 component->hasLocalFeature = 1;
1859 break;
1860 case INSTALLSTATE_DEFAULT:
1861 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1862 component->hasAdvertiseFeature = 1;
1863 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1864 component->hasSourceFeature = 1;
1865 else
1866 component->hasLocalFeature = 1;
1867 break;
1868 default:
1869 break;
1874 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1876 /* if the component isn't enabled, leave it alone */
1877 if (!component->Enabled)
1878 continue;
1880 /* check if it's local or source */
1881 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1882 (component->hasLocalFeature || component->hasSourceFeature))
1884 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1885 !component->ForceLocalState)
1886 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1887 else
1888 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1889 continue;
1892 /* if any feature is local, the component must be local too */
1893 if (component->hasLocalFeature)
1895 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1896 continue;
1899 if (component->hasSourceFeature)
1901 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1902 continue;
1905 if (component->hasAdvertiseFeature)
1907 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1908 continue;
1911 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1912 if (component->anyAbsent)
1913 msi_component_set_state(component, INSTALLSTATE_ABSENT);
1916 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1918 if (component->Action == INSTALLSTATE_DEFAULT)
1920 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1921 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1924 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1925 debugstr_w(component->Component), component->Installed, component->Action);
1929 return ERROR_SUCCESS;
1932 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1934 MSIPACKAGE *package = (MSIPACKAGE*)param;
1935 LPCWSTR name;
1936 LPWSTR path;
1937 MSIFOLDER *f;
1939 name = MSI_RecordGetString(row,1);
1941 f = get_loaded_folder(package, name);
1942 if (!f) return ERROR_SUCCESS;
1944 /* reset the ResolvedTarget */
1945 msi_free(f->ResolvedTarget);
1946 f->ResolvedTarget = NULL;
1948 /* This helper function now does ALL the work */
1949 TRACE("Dir %s ...\n",debugstr_w(name));
1950 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1951 TRACE("resolves to %s\n",debugstr_w(path));
1952 msi_free(path);
1954 return ERROR_SUCCESS;
1957 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1959 MSIPACKAGE *package = (MSIPACKAGE*)param;
1960 LPCWSTR name;
1961 MSIFEATURE *feature;
1963 name = MSI_RecordGetString( row, 1 );
1965 feature = get_loaded_feature( package, name );
1966 if (!feature)
1967 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1968 else
1970 LPCWSTR Condition;
1971 Condition = MSI_RecordGetString(row,3);
1973 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1975 int level = MSI_RecordGetInteger(row,2);
1976 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
1977 feature->Level = level;
1980 return ERROR_SUCCESS;
1983 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1985 static const WCHAR name_fmt[] =
1986 {'%','u','.','%','u','.','%','u','.','%','u',0};
1987 static WCHAR name[] = {'\\',0};
1988 VS_FIXEDFILEINFO *lpVer;
1989 WCHAR filever[0x100];
1990 LPVOID version;
1991 DWORD versize;
1992 DWORD handle;
1993 UINT sz;
1995 TRACE("%s\n", debugstr_w(filename));
1997 versize = GetFileVersionInfoSizeW( filename, &handle );
1998 if (!versize)
1999 return NULL;
2001 version = msi_alloc( versize );
2002 GetFileVersionInfoW( filename, 0, versize, version );
2004 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2006 msi_free( version );
2007 return NULL;
2010 sprintfW( filever, name_fmt,
2011 HIWORD(lpVer->dwFileVersionMS),
2012 LOWORD(lpVer->dwFileVersionMS),
2013 HIWORD(lpVer->dwFileVersionLS),
2014 LOWORD(lpVer->dwFileVersionLS));
2016 msi_free( version );
2018 return strdupW( filever );
2021 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2023 LPWSTR file_version;
2024 MSIFILE *file;
2026 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2028 MSICOMPONENT* comp = file->Component;
2029 LPWSTR p;
2031 if (!comp)
2032 continue;
2034 if (file->IsCompressed)
2035 comp->ForceLocalState = TRUE;
2037 /* calculate target */
2038 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2040 msi_free(file->TargetPath);
2042 TRACE("file %s is named %s\n",
2043 debugstr_w(file->File), debugstr_w(file->FileName));
2045 file->TargetPath = build_directory_name(2, p, file->FileName);
2047 msi_free(p);
2049 TRACE("file %s resolves to %s\n",
2050 debugstr_w(file->File), debugstr_w(file->TargetPath));
2052 /* don't check files of components that aren't installed */
2053 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2054 comp->Installed == INSTALLSTATE_ABSENT)
2056 file->state = msifs_missing; /* assume files are missing */
2057 continue;
2060 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2062 file->state = msifs_missing;
2063 comp->Cost += file->FileSize;
2064 comp->Installed = INSTALLSTATE_INCOMPLETE;
2065 continue;
2068 if (file->Version &&
2069 (file_version = msi_get_disk_file_version( file->TargetPath )))
2071 TRACE("new %s old %s\n", debugstr_w(file->Version),
2072 debugstr_w(file_version));
2073 /* FIXME: seems like a bad way to compare version numbers */
2074 if (lstrcmpiW(file_version, file->Version)<0)
2076 file->state = msifs_overwrite;
2077 comp->Cost += file->FileSize;
2078 comp->Installed = INSTALLSTATE_INCOMPLETE;
2080 else
2081 file->state = msifs_present;
2082 msi_free( file_version );
2084 else
2085 file->state = msifs_present;
2088 return ERROR_SUCCESS;
2092 * A lot is done in this function aside from just the costing.
2093 * The costing needs to be implemented at some point but for now I am going
2094 * to focus on the directory building
2097 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2099 static const WCHAR ExecSeqQuery[] =
2100 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2101 '`','D','i','r','e','c','t','o','r','y','`',0};
2102 static const WCHAR ConditionQuery[] =
2103 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2104 '`','C','o','n','d','i','t','i','o','n','`',0};
2105 static const WCHAR szCosting[] =
2106 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2107 static const WCHAR szlevel[] =
2108 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2109 static const WCHAR szOne[] = { '1', 0 };
2110 MSICOMPONENT *comp;
2111 UINT rc;
2112 MSIQUERY * view;
2113 LPWSTR level;
2115 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2116 return ERROR_SUCCESS;
2118 TRACE("Building Directory properties\n");
2120 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2121 if (rc == ERROR_SUCCESS)
2123 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2124 package);
2125 msiobj_release(&view->hdr);
2128 /* read components states from the registry */
2129 ACTION_GetComponentInstallStates(package);
2131 TRACE("File calculations\n");
2132 msi_check_file_install_states( package );
2134 TRACE("Evaluating Condition Table\n");
2136 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2137 if (rc == ERROR_SUCCESS)
2139 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2140 package);
2141 msiobj_release(&view->hdr);
2144 TRACE("Enabling or Disabling Components\n");
2145 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2147 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2149 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2150 comp->Enabled = FALSE;
2154 MSI_SetPropertyW(package,szCosting,szOne);
2155 /* set default run level if not set */
2156 level = msi_dup_property( package, szlevel );
2157 if (!level)
2158 MSI_SetPropertyW(package,szlevel, szOne);
2159 msi_free(level);
2161 ACTION_UpdateFeatureInstallStates(package);
2163 return MSI_SetFeatureStates(package);
2166 /* OK this value is "interpreted" and then formatted based on the
2167 first few characters */
2168 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2169 DWORD *size)
2171 LPSTR data = NULL;
2172 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2174 if (value[1]=='x')
2176 LPWSTR ptr;
2177 CHAR byte[5];
2178 LPWSTR deformated = NULL;
2179 int count;
2181 deformat_string(package, &value[2], &deformated);
2183 /* binary value type */
2184 ptr = deformated;
2185 *type = REG_BINARY;
2186 if (strlenW(ptr)%2)
2187 *size = (strlenW(ptr)/2)+1;
2188 else
2189 *size = strlenW(ptr)/2;
2191 data = msi_alloc(*size);
2193 byte[0] = '0';
2194 byte[1] = 'x';
2195 byte[4] = 0;
2196 count = 0;
2197 /* if uneven pad with a zero in front */
2198 if (strlenW(ptr)%2)
2200 byte[2]= '0';
2201 byte[3]= *ptr;
2202 ptr++;
2203 data[count] = (BYTE)strtol(byte,NULL,0);
2204 count ++;
2205 TRACE("Uneven byte count\n");
2207 while (*ptr)
2209 byte[2]= *ptr;
2210 ptr++;
2211 byte[3]= *ptr;
2212 ptr++;
2213 data[count] = (BYTE)strtol(byte,NULL,0);
2214 count ++;
2216 msi_free(deformated);
2218 TRACE("Data %i bytes(%i)\n",*size,count);
2220 else
2222 LPWSTR deformated;
2223 LPWSTR p;
2224 DWORD d = 0;
2225 deformat_string(package, &value[1], &deformated);
2227 *type=REG_DWORD;
2228 *size = sizeof(DWORD);
2229 data = msi_alloc(*size);
2230 p = deformated;
2231 if (*p == '-')
2232 p++;
2233 while (*p)
2235 if ( (*p < '0') || (*p > '9') )
2236 break;
2237 d *= 10;
2238 d += (*p - '0');
2239 p++;
2241 if (deformated[0] == '-')
2242 d = -d;
2243 *(LPDWORD)data = d;
2244 TRACE("DWORD %i\n",*(LPDWORD)data);
2246 msi_free(deformated);
2249 else
2251 static const WCHAR szMulti[] = {'[','~',']',0};
2252 LPCWSTR ptr;
2253 *type=REG_SZ;
2255 if (value[0]=='#')
2257 if (value[1]=='%')
2259 ptr = &value[2];
2260 *type=REG_EXPAND_SZ;
2262 else
2263 ptr = &value[1];
2265 else
2266 ptr=value;
2268 if (strstrW(value,szMulti))
2269 *type = REG_MULTI_SZ;
2271 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2273 return data;
2276 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2278 MSIPACKAGE *package = (MSIPACKAGE*)param;
2279 static const WCHAR szHCR[] =
2280 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2281 'R','O','O','T','\\',0};
2282 static const WCHAR szHCU[] =
2283 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2284 'U','S','E','R','\\',0};
2285 static const WCHAR szHLM[] =
2286 {'H','K','E','Y','_','L','O','C','A','L','_',
2287 'M','A','C','H','I','N','E','\\',0};
2288 static const WCHAR szHU[] =
2289 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2291 LPSTR value_data = NULL;
2292 HKEY root_key, hkey;
2293 DWORD type,size;
2294 LPWSTR deformated;
2295 LPCWSTR szRoot, component, name, key, value;
2296 MSICOMPONENT *comp;
2297 MSIRECORD * uirow;
2298 LPWSTR uikey;
2299 INT root;
2300 BOOL check_first = FALSE;
2301 UINT rc;
2303 ui_progress(package,2,0,0,0);
2305 value = NULL;
2306 key = NULL;
2307 uikey = NULL;
2308 name = NULL;
2310 component = MSI_RecordGetString(row, 6);
2311 comp = get_loaded_component(package,component);
2312 if (!comp)
2313 return ERROR_SUCCESS;
2315 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2317 TRACE("Skipping write due to disabled component %s\n",
2318 debugstr_w(component));
2320 comp->Action = comp->Installed;
2322 return ERROR_SUCCESS;
2325 comp->Action = INSTALLSTATE_LOCAL;
2327 name = MSI_RecordGetString(row, 4);
2328 if( MSI_RecordIsNull(row,5) && name )
2330 /* null values can have special meanings */
2331 if (name[0]=='-' && name[1] == 0)
2332 return ERROR_SUCCESS;
2333 else if ((name[0]=='+' && name[1] == 0) ||
2334 (name[0] == '*' && name[1] == 0))
2335 name = NULL;
2336 check_first = TRUE;
2339 root = MSI_RecordGetInteger(row,2);
2340 key = MSI_RecordGetString(row, 3);
2342 /* get the root key */
2343 switch (root)
2345 case -1:
2347 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2348 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2349 if (all_users && all_users[0] == '1')
2351 root_key = HKEY_LOCAL_MACHINE;
2352 szRoot = szHLM;
2354 else
2356 root_key = HKEY_CURRENT_USER;
2357 szRoot = szHCU;
2359 msi_free(all_users);
2361 break;
2362 case 0: root_key = HKEY_CLASSES_ROOT;
2363 szRoot = szHCR;
2364 break;
2365 case 1: root_key = HKEY_CURRENT_USER;
2366 szRoot = szHCU;
2367 break;
2368 case 2: root_key = HKEY_LOCAL_MACHINE;
2369 szRoot = szHLM;
2370 break;
2371 case 3: root_key = HKEY_USERS;
2372 szRoot = szHU;
2373 break;
2374 default:
2375 ERR("Unknown root %i\n",root);
2376 root_key=NULL;
2377 szRoot = NULL;
2378 break;
2380 if (!root_key)
2381 return ERROR_SUCCESS;
2383 deformat_string(package, key , &deformated);
2384 size = strlenW(deformated) + strlenW(szRoot) + 1;
2385 uikey = msi_alloc(size*sizeof(WCHAR));
2386 strcpyW(uikey,szRoot);
2387 strcatW(uikey,deformated);
2389 if (RegCreateKeyW( root_key, deformated, &hkey))
2391 ERR("Could not create key %s\n",debugstr_w(deformated));
2392 msi_free(deformated);
2393 msi_free(uikey);
2394 return ERROR_SUCCESS;
2396 msi_free(deformated);
2398 value = MSI_RecordGetString(row,5);
2399 if (value)
2400 value_data = parse_value(package, value, &type, &size);
2401 else
2403 static const WCHAR szEmpty[] = {0};
2404 value_data = (LPSTR)strdupW(szEmpty);
2405 size = 0;
2406 type = REG_SZ;
2409 deformat_string(package, name, &deformated);
2411 /* get the double nulls to terminate SZ_MULTI */
2412 if (type == REG_MULTI_SZ)
2413 size +=sizeof(WCHAR);
2415 if (!check_first)
2417 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2418 debugstr_w(uikey));
2419 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2421 else
2423 DWORD sz = 0;
2424 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2425 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2427 TRACE("value %s of %s checked already exists\n",
2428 debugstr_w(deformated), debugstr_w(uikey));
2430 else
2432 TRACE("Checked and setting value %s of %s\n",
2433 debugstr_w(deformated), debugstr_w(uikey));
2434 if (deformated || size)
2435 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2438 RegCloseKey(hkey);
2440 uirow = MSI_CreateRecord(3);
2441 MSI_RecordSetStringW(uirow,2,deformated);
2442 MSI_RecordSetStringW(uirow,1,uikey);
2444 if (type == REG_SZ)
2445 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2446 else
2447 MSI_RecordSetStringW(uirow,3,value);
2449 ui_actiondata(package,szWriteRegistryValues,uirow);
2450 msiobj_release( &uirow->hdr );
2452 msi_free(value_data);
2453 msi_free(deformated);
2454 msi_free(uikey);
2456 return ERROR_SUCCESS;
2459 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2461 UINT rc;
2462 MSIQUERY * view;
2463 static const WCHAR ExecSeqQuery[] =
2464 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2465 '`','R','e','g','i','s','t','r','y','`',0 };
2467 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2468 if (rc != ERROR_SUCCESS)
2469 return ERROR_SUCCESS;
2471 /* increment progress bar each time action data is sent */
2472 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2474 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2476 msiobj_release(&view->hdr);
2477 return rc;
2480 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2482 package->script->CurrentlyScripting = TRUE;
2484 return ERROR_SUCCESS;
2488 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2490 MSICOMPONENT *comp;
2491 DWORD progress = 0;
2492 DWORD total = 0;
2493 static const WCHAR q1[]=
2494 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2495 '`','R','e','g','i','s','t','r','y','`',0};
2496 UINT rc;
2497 MSIQUERY * view;
2498 MSIFEATURE *feature;
2499 MSIFILE *file;
2501 TRACE("InstallValidate\n");
2503 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2504 if (rc == ERROR_SUCCESS)
2506 MSI_IterateRecords( view, &progress, NULL, package );
2507 msiobj_release( &view->hdr );
2508 total += progress * REG_PROGRESS_VALUE;
2511 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2512 total += COMPONENT_PROGRESS_VALUE;
2514 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2515 total += file->FileSize;
2517 ui_progress(package,0,total,0,0);
2519 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2521 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2522 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2523 feature->ActionRequest);
2526 return ERROR_SUCCESS;
2529 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2531 MSIPACKAGE* package = (MSIPACKAGE*)param;
2532 LPCWSTR cond = NULL;
2533 LPCWSTR message = NULL;
2534 UINT r;
2536 static const WCHAR title[]=
2537 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2539 cond = MSI_RecordGetString(row,1);
2541 r = MSI_EvaluateConditionW(package,cond);
2542 if (r == MSICONDITION_FALSE)
2544 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2546 LPWSTR deformated;
2547 message = MSI_RecordGetString(row,2);
2548 deformat_string(package,message,&deformated);
2549 MessageBoxW(NULL,deformated,title,MB_OK);
2550 msi_free(deformated);
2553 return ERROR_INSTALL_FAILURE;
2556 return ERROR_SUCCESS;
2559 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2561 UINT rc;
2562 MSIQUERY * view = NULL;
2563 static const WCHAR ExecSeqQuery[] =
2564 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2565 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2567 TRACE("Checking launch conditions\n");
2569 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2570 if (rc != ERROR_SUCCESS)
2571 return ERROR_SUCCESS;
2573 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2574 msiobj_release(&view->hdr);
2576 return rc;
2579 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2582 if (!cmp->KeyPath)
2583 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2585 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2587 MSIRECORD * row = 0;
2588 UINT root,len;
2589 LPWSTR deformated,buffer,deformated_name;
2590 LPCWSTR key,name;
2591 static const WCHAR ExecSeqQuery[] =
2592 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2593 '`','R','e','g','i','s','t','r','y','`',' ',
2594 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2595 ' ','=',' ' ,'\'','%','s','\'',0 };
2596 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2597 static const WCHAR fmt2[]=
2598 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2600 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2601 if (!row)
2602 return NULL;
2604 root = MSI_RecordGetInteger(row,2);
2605 key = MSI_RecordGetString(row, 3);
2606 name = MSI_RecordGetString(row, 4);
2607 deformat_string(package, key , &deformated);
2608 deformat_string(package, name, &deformated_name);
2610 len = strlenW(deformated) + 6;
2611 if (deformated_name)
2612 len+=strlenW(deformated_name);
2614 buffer = msi_alloc( len *sizeof(WCHAR));
2616 if (deformated_name)
2617 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2618 else
2619 sprintfW(buffer,fmt,root,deformated);
2621 msi_free(deformated);
2622 msi_free(deformated_name);
2623 msiobj_release(&row->hdr);
2625 return buffer;
2627 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2629 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2630 return NULL;
2632 else
2634 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2636 if (file)
2637 return strdupW( file->TargetPath );
2639 return NULL;
2642 static HKEY openSharedDLLsKey(void)
2644 HKEY hkey=0;
2645 static const WCHAR path[] =
2646 {'S','o','f','t','w','a','r','e','\\',
2647 'M','i','c','r','o','s','o','f','t','\\',
2648 'W','i','n','d','o','w','s','\\',
2649 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2650 'S','h','a','r','e','d','D','L','L','s',0};
2652 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2653 return hkey;
2656 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2658 HKEY hkey;
2659 DWORD count=0;
2660 DWORD type;
2661 DWORD sz = sizeof(count);
2662 DWORD rc;
2664 hkey = openSharedDLLsKey();
2665 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2666 if (rc != ERROR_SUCCESS)
2667 count = 0;
2668 RegCloseKey(hkey);
2669 return count;
2672 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2674 HKEY hkey;
2676 hkey = openSharedDLLsKey();
2677 if (count > 0)
2678 msi_reg_set_val_dword( hkey, path, count );
2679 else
2680 RegDeleteValueW(hkey,path);
2681 RegCloseKey(hkey);
2682 return count;
2686 * Return TRUE if the count should be written out and FALSE if not
2688 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2690 MSIFEATURE *feature;
2691 INT count = 0;
2692 BOOL write = FALSE;
2694 /* only refcount DLLs */
2695 if (comp->KeyPath == NULL ||
2696 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2697 comp->Attributes & msidbComponentAttributesODBCDataSource)
2698 write = FALSE;
2699 else
2701 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2702 write = (count > 0);
2704 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2705 write = TRUE;
2708 /* increment counts */
2709 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2711 ComponentList *cl;
2713 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2714 continue;
2716 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2718 if ( cl->component == comp )
2719 count++;
2723 /* decrement counts */
2724 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2726 ComponentList *cl;
2728 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2729 continue;
2731 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2733 if ( cl->component == comp )
2734 count--;
2738 /* ref count all the files in the component */
2739 if (write)
2741 MSIFILE *file;
2743 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2745 if (file->Component == comp)
2746 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2750 /* add a count for permenent */
2751 if (comp->Attributes & msidbComponentAttributesPermanent)
2752 count ++;
2754 comp->RefCount = count;
2756 if (write)
2757 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2761 * Ok further analysis makes me think that this work is
2762 * actually done in the PublishComponents and PublishFeatures
2763 * step, and not here. It appears like the keypath and all that is
2764 * resolved in this step, however actually written in the Publish steps.
2765 * But we will leave it here for now because it is unclear
2767 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2769 WCHAR squished_pc[GUID_SIZE];
2770 WCHAR squished_cc[GUID_SIZE];
2771 UINT rc;
2772 MSICOMPONENT *comp;
2773 HKEY hkey=0,hkey2=0;
2775 TRACE("\n");
2777 /* writes the Component and Features values to the registry */
2779 rc = MSIREG_OpenComponents(&hkey);
2780 if (rc != ERROR_SUCCESS)
2781 return rc;
2783 squash_guid(package->ProductCode,squished_pc);
2784 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2786 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2788 MSIRECORD * uirow;
2790 ui_progress(package,2,0,0,0);
2791 if (!comp->ComponentId)
2792 continue;
2794 squash_guid(comp->ComponentId,squished_cc);
2796 msi_free(comp->FullKeypath);
2797 comp->FullKeypath = resolve_keypath( package, comp );
2799 /* do the refcounting */
2800 ACTION_RefCountComponent( package, comp );
2802 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2803 debugstr_w(comp->Component),
2804 debugstr_w(squished_cc),
2805 debugstr_w(comp->FullKeypath),
2806 comp->RefCount);
2808 * Write the keypath out if the component is to be registered
2809 * and delete the key if the component is to be deregistered
2811 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2813 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2814 if (rc != ERROR_SUCCESS)
2815 continue;
2817 if (!comp->FullKeypath)
2818 continue;
2820 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2822 if (comp->Attributes & msidbComponentAttributesPermanent)
2824 static const WCHAR szPermKey[] =
2825 { '0','0','0','0','0','0','0','0','0','0','0','0',
2826 '0','0','0','0','0','0','0','0','0','0','0','0',
2827 '0','0','0','0','0','0','0','0',0 };
2829 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2832 RegCloseKey(hkey2);
2834 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey2, TRUE);
2835 if (rc != ERROR_SUCCESS)
2836 continue;
2838 msi_reg_set_val_str(hkey2, squished_pc, comp->FullKeypath);
2839 RegCloseKey(hkey2);
2841 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2843 DWORD res;
2845 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2846 if (rc != ERROR_SUCCESS)
2847 continue;
2849 RegDeleteValueW(hkey2,squished_pc);
2851 /* if the key is empty delete it */
2852 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2853 RegCloseKey(hkey2);
2854 if (res == ERROR_NO_MORE_ITEMS)
2855 RegDeleteKeyW(hkey,squished_cc);
2857 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2860 /* UI stuff */
2861 uirow = MSI_CreateRecord(3);
2862 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2863 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2864 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2865 ui_actiondata(package,szProcessComponents,uirow);
2866 msiobj_release( &uirow->hdr );
2868 RegCloseKey(hkey);
2869 return rc;
2872 typedef struct {
2873 CLSID clsid;
2874 LPWSTR source;
2876 LPWSTR path;
2877 ITypeLib *ptLib;
2878 } typelib_struct;
2880 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2881 LPWSTR lpszName, LONG_PTR lParam)
2883 TLIBATTR *attr;
2884 typelib_struct *tl_struct = (typelib_struct*) lParam;
2885 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2886 int sz;
2887 HRESULT res;
2889 if (!IS_INTRESOURCE(lpszName))
2891 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2892 return TRUE;
2895 sz = strlenW(tl_struct->source)+4;
2896 sz *= sizeof(WCHAR);
2898 if ((INT_PTR)lpszName == 1)
2899 tl_struct->path = strdupW(tl_struct->source);
2900 else
2902 tl_struct->path = msi_alloc(sz);
2903 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2906 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2907 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2908 if (!SUCCEEDED(res))
2910 msi_free(tl_struct->path);
2911 tl_struct->path = NULL;
2913 return TRUE;
2916 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2917 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2919 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2920 return FALSE;
2923 msi_free(tl_struct->path);
2924 tl_struct->path = NULL;
2926 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2927 ITypeLib_Release(tl_struct->ptLib);
2929 return TRUE;
2932 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2934 MSIPACKAGE* package = (MSIPACKAGE*)param;
2935 LPCWSTR component;
2936 MSICOMPONENT *comp;
2937 MSIFILE *file;
2938 typelib_struct tl_struct;
2939 HMODULE module;
2940 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2942 component = MSI_RecordGetString(row,3);
2943 comp = get_loaded_component(package,component);
2944 if (!comp)
2945 return ERROR_SUCCESS;
2947 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2949 TRACE("Skipping typelib reg due to disabled component\n");
2951 comp->Action = comp->Installed;
2953 return ERROR_SUCCESS;
2956 comp->Action = INSTALLSTATE_LOCAL;
2958 file = get_loaded_file( package, comp->KeyPath );
2959 if (!file)
2960 return ERROR_SUCCESS;
2962 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2963 if (module)
2965 LPCWSTR guid;
2966 guid = MSI_RecordGetString(row,1);
2967 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2968 tl_struct.source = strdupW( file->TargetPath );
2969 tl_struct.path = NULL;
2971 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2972 (LONG_PTR)&tl_struct);
2974 if (tl_struct.path)
2976 LPWSTR help = NULL;
2977 LPCWSTR helpid;
2978 HRESULT res;
2980 helpid = MSI_RecordGetString(row,6);
2982 if (helpid)
2983 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2984 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2985 msi_free(help);
2987 if (!SUCCEEDED(res))
2988 ERR("Failed to register type library %s\n",
2989 debugstr_w(tl_struct.path));
2990 else
2992 ui_actiondata(package,szRegisterTypeLibraries,row);
2994 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2997 ITypeLib_Release(tl_struct.ptLib);
2998 msi_free(tl_struct.path);
3000 else
3001 ERR("Failed to load type library %s\n",
3002 debugstr_w(tl_struct.source));
3004 FreeLibrary(module);
3005 msi_free(tl_struct.source);
3007 else
3008 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3010 return ERROR_SUCCESS;
3013 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3016 * OK this is a bit confusing.. I am given a _Component key and I believe
3017 * that the file that is being registered as a type library is the "key file
3018 * of that component" which I interpret to mean "The file in the KeyPath of
3019 * that component".
3021 UINT rc;
3022 MSIQUERY * view;
3023 static const WCHAR Query[] =
3024 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3025 '`','T','y','p','e','L','i','b','`',0};
3027 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3028 if (rc != ERROR_SUCCESS)
3029 return ERROR_SUCCESS;
3031 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3032 msiobj_release(&view->hdr);
3033 return rc;
3036 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3038 MSIPACKAGE *package = (MSIPACKAGE*)param;
3039 LPWSTR target_file, target_folder, filename;
3040 LPCWSTR buffer, extension;
3041 MSICOMPONENT *comp;
3042 static const WCHAR szlnk[]={'.','l','n','k',0};
3043 IShellLinkW *sl = NULL;
3044 IPersistFile *pf = NULL;
3045 HRESULT res;
3047 buffer = MSI_RecordGetString(row,4);
3048 comp = get_loaded_component(package,buffer);
3049 if (!comp)
3050 return ERROR_SUCCESS;
3052 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3054 TRACE("Skipping shortcut creation due to disabled component\n");
3056 comp->Action = comp->Installed;
3058 return ERROR_SUCCESS;
3061 comp->Action = INSTALLSTATE_LOCAL;
3063 ui_actiondata(package,szCreateShortcuts,row);
3065 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3066 &IID_IShellLinkW, (LPVOID *) &sl );
3068 if (FAILED( res ))
3070 ERR("CLSID_ShellLink not available\n");
3071 goto err;
3074 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3075 if (FAILED( res ))
3077 ERR("QueryInterface(IID_IPersistFile) failed\n");
3078 goto err;
3081 buffer = MSI_RecordGetString(row,2);
3082 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3084 /* may be needed because of a bug somehwere else */
3085 create_full_pathW(target_folder);
3087 filename = msi_dup_record_field( row, 3 );
3088 reduce_to_longfilename(filename);
3090 extension = strchrW(filename,'.');
3091 if (!extension || strcmpiW(extension,szlnk))
3093 int len = strlenW(filename);
3094 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3095 memcpy(filename + len, szlnk, sizeof(szlnk));
3097 target_file = build_directory_name(2, target_folder, filename);
3098 msi_free(target_folder);
3099 msi_free(filename);
3101 buffer = MSI_RecordGetString(row,5);
3102 if (strchrW(buffer,'['))
3104 LPWSTR deformated;
3105 deformat_string(package,buffer,&deformated);
3106 IShellLinkW_SetPath(sl,deformated);
3107 msi_free(deformated);
3109 else
3111 FIXME("poorly handled shortcut format, advertised shortcut\n");
3112 IShellLinkW_SetPath(sl,comp->FullKeypath);
3115 if (!MSI_RecordIsNull(row,6))
3117 LPWSTR deformated;
3118 buffer = MSI_RecordGetString(row,6);
3119 deformat_string(package,buffer,&deformated);
3120 IShellLinkW_SetArguments(sl,deformated);
3121 msi_free(deformated);
3124 if (!MSI_RecordIsNull(row,7))
3126 buffer = MSI_RecordGetString(row,7);
3127 IShellLinkW_SetDescription(sl,buffer);
3130 if (!MSI_RecordIsNull(row,8))
3131 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3133 if (!MSI_RecordIsNull(row,9))
3135 LPWSTR Path;
3136 INT index;
3138 buffer = MSI_RecordGetString(row,9);
3140 Path = build_icon_path(package,buffer);
3141 index = MSI_RecordGetInteger(row,10);
3143 /* no value means 0 */
3144 if (index == MSI_NULL_INTEGER)
3145 index = 0;
3147 IShellLinkW_SetIconLocation(sl,Path,index);
3148 msi_free(Path);
3151 if (!MSI_RecordIsNull(row,11))
3152 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3154 if (!MSI_RecordIsNull(row,12))
3156 LPWSTR Path;
3157 buffer = MSI_RecordGetString(row,12);
3158 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3159 if (Path)
3160 IShellLinkW_SetWorkingDirectory(sl,Path);
3161 msi_free(Path);
3164 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3165 IPersistFile_Save(pf,target_file,FALSE);
3167 msi_free(target_file);
3169 err:
3170 if (pf)
3171 IPersistFile_Release( pf );
3172 if (sl)
3173 IShellLinkW_Release( sl );
3175 return ERROR_SUCCESS;
3178 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3180 UINT rc;
3181 HRESULT res;
3182 MSIQUERY * view;
3183 static const WCHAR Query[] =
3184 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3185 '`','S','h','o','r','t','c','u','t','`',0};
3187 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3188 if (rc != ERROR_SUCCESS)
3189 return ERROR_SUCCESS;
3191 res = CoInitialize( NULL );
3192 if (FAILED (res))
3194 ERR("CoInitialize failed\n");
3195 return ERROR_FUNCTION_FAILED;
3198 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3199 msiobj_release(&view->hdr);
3201 CoUninitialize();
3203 return rc;
3206 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3208 MSIPACKAGE* package = (MSIPACKAGE*)param;
3209 HANDLE the_file;
3210 LPWSTR FilePath;
3211 LPCWSTR FileName;
3212 CHAR buffer[1024];
3213 DWORD sz;
3214 UINT rc;
3215 MSIRECORD *uirow;
3217 FileName = MSI_RecordGetString(row,1);
3218 if (!FileName)
3220 ERR("Unable to get FileName\n");
3221 return ERROR_SUCCESS;
3224 FilePath = build_icon_path(package,FileName);
3226 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3228 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3229 FILE_ATTRIBUTE_NORMAL, NULL);
3231 if (the_file == INVALID_HANDLE_VALUE)
3233 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3234 msi_free(FilePath);
3235 return ERROR_SUCCESS;
3240 DWORD write;
3241 sz = 1024;
3242 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3243 if (rc != ERROR_SUCCESS)
3245 ERR("Failed to get stream\n");
3246 CloseHandle(the_file);
3247 DeleteFileW(FilePath);
3248 break;
3250 WriteFile(the_file,buffer,sz,&write,NULL);
3251 } while (sz == 1024);
3253 msi_free(FilePath);
3255 CloseHandle(the_file);
3257 uirow = MSI_CreateRecord(1);
3258 MSI_RecordSetStringW(uirow,1,FileName);
3259 ui_actiondata(package,szPublishProduct,uirow);
3260 msiobj_release( &uirow->hdr );
3262 return ERROR_SUCCESS;
3265 static BOOL msi_check_publish(MSIPACKAGE *package)
3267 MSIFEATURE *feature;
3269 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3271 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3272 return TRUE;
3275 return FALSE;
3279 * 99% of the work done here is only done for
3280 * advertised installs. However this is where the
3281 * Icon table is processed and written out
3282 * so that is what I am going to do here.
3284 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3286 UINT rc;
3287 MSIQUERY * view;
3288 MSISOURCELISTINFO *info;
3289 MSIMEDIADISK *disk;
3290 static const WCHAR Query[]=
3291 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3292 '`','I','c','o','n','`',0};
3293 /* for registry stuff */
3294 HKEY hkey=0;
3295 HKEY hukey=0;
3296 HKEY hudkey=0, props=0;
3297 static const WCHAR szProductLanguage[] =
3298 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3299 static const WCHAR szARPProductIcon[] =
3300 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3301 static const WCHAR szProductVersion[] =
3302 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3303 DWORD langid;
3304 LPWSTR buffer;
3305 DWORD size;
3306 MSIHANDLE hDb, hSumInfo;
3308 /* FIXME: also need to publish if the product is in advertise mode */
3309 if (!msi_check_publish(package))
3310 return ERROR_SUCCESS;
3312 /* write out icon files */
3314 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3315 if (rc == ERROR_SUCCESS)
3317 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3318 msiobj_release(&view->hdr);
3321 /* ok there is a lot more done here but i need to figure out what */
3323 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3324 if (rc != ERROR_SUCCESS)
3325 goto end;
3327 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3328 if (rc != ERROR_SUCCESS)
3329 goto end;
3331 rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3332 if (rc != ERROR_SUCCESS)
3333 goto end;
3335 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode,&props,TRUE);
3336 if (rc != ERROR_SUCCESS)
3337 goto end;
3339 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3340 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3341 msi_free(buffer);
3343 langid = msi_get_property_int( package, szProductLanguage, 0 );
3344 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3346 buffer = msi_dup_property( package, szARPProductIcon );
3347 if (buffer)
3349 LPWSTR path = build_icon_path(package,buffer);
3350 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3351 msi_free( path );
3353 msi_free(buffer);
3355 buffer = msi_dup_property( package, szProductVersion );
3356 if (buffer)
3358 DWORD verdword = msi_version_str_to_dword(buffer);
3359 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3361 msi_free(buffer);
3363 /* FIXME: Need to write more keys to the user registry */
3365 hDb= alloc_msihandle( &package->db->hdr );
3366 if (!hDb) {
3367 rc = ERROR_NOT_ENOUGH_MEMORY;
3368 goto end;
3370 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3371 MsiCloseHandle(hDb);
3372 if (rc == ERROR_SUCCESS)
3374 WCHAR guidbuffer[0x200];
3375 size = 0x200;
3376 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3377 guidbuffer, &size);
3378 if (rc == ERROR_SUCCESS)
3380 WCHAR squashed[GUID_SIZE];
3381 /* for now we only care about the first guid */
3382 LPWSTR ptr = strchrW(guidbuffer,';');
3383 if (ptr) *ptr = 0;
3384 squash_guid(guidbuffer,squashed);
3385 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3387 else
3389 ERR("Unable to query Revision_Number...\n");
3390 rc = ERROR_SUCCESS;
3392 MsiCloseHandle(hSumInfo);
3394 else
3396 ERR("Unable to open Summary Information\n");
3397 rc = ERROR_SUCCESS;
3400 /* publish the SourceList info */
3401 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3403 MsiSourceListSetInfoW(package->ProductCode, NULL,
3404 info->context, info->options,
3405 info->property, info->value);
3408 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3410 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3411 disk->context, disk->options,
3412 disk->disk_id, disk->volume_label, disk->disk_prompt);
3415 end:
3416 RegCloseKey(hkey);
3417 RegCloseKey(hukey);
3418 RegCloseKey(hudkey);
3419 RegCloseKey(props);
3421 return rc;
3424 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3426 MSIPACKAGE *package = (MSIPACKAGE*)param;
3427 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3428 LPWSTR deformated_section, deformated_key, deformated_value;
3429 LPWSTR folder, fullname = NULL;
3430 MSIRECORD * uirow;
3431 INT action;
3432 MSICOMPONENT *comp;
3433 static const WCHAR szWindowsFolder[] =
3434 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3436 component = MSI_RecordGetString(row, 8);
3437 comp = get_loaded_component(package,component);
3439 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3441 TRACE("Skipping ini file due to disabled component %s\n",
3442 debugstr_w(component));
3444 comp->Action = comp->Installed;
3446 return ERROR_SUCCESS;
3449 comp->Action = INSTALLSTATE_LOCAL;
3451 identifier = MSI_RecordGetString(row,1);
3452 filename = MSI_RecordGetString(row,2);
3453 dirproperty = MSI_RecordGetString(row,3);
3454 section = MSI_RecordGetString(row,4);
3455 key = MSI_RecordGetString(row,5);
3456 value = MSI_RecordGetString(row,6);
3457 action = MSI_RecordGetInteger(row,7);
3459 deformat_string(package,section,&deformated_section);
3460 deformat_string(package,key,&deformated_key);
3461 deformat_string(package,value,&deformated_value);
3463 if (dirproperty)
3465 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3466 if (!folder)
3467 folder = msi_dup_property( package, dirproperty );
3469 else
3470 folder = msi_dup_property( package, szWindowsFolder );
3472 if (!folder)
3474 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3475 goto cleanup;
3478 fullname = build_directory_name(2, folder, filename);
3480 if (action == 0)
3482 TRACE("Adding value %s to section %s in %s\n",
3483 debugstr_w(deformated_key), debugstr_w(deformated_section),
3484 debugstr_w(fullname));
3485 WritePrivateProfileStringW(deformated_section, deformated_key,
3486 deformated_value, fullname);
3488 else if (action == 1)
3490 WCHAR returned[10];
3491 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3492 returned, 10, fullname);
3493 if (returned[0] == 0)
3495 TRACE("Adding value %s to section %s in %s\n",
3496 debugstr_w(deformated_key), debugstr_w(deformated_section),
3497 debugstr_w(fullname));
3499 WritePrivateProfileStringW(deformated_section, deformated_key,
3500 deformated_value, fullname);
3503 else if (action == 3)
3504 FIXME("Append to existing section not yet implemented\n");
3506 uirow = MSI_CreateRecord(4);
3507 MSI_RecordSetStringW(uirow,1,identifier);
3508 MSI_RecordSetStringW(uirow,2,deformated_section);
3509 MSI_RecordSetStringW(uirow,3,deformated_key);
3510 MSI_RecordSetStringW(uirow,4,deformated_value);
3511 ui_actiondata(package,szWriteIniValues,uirow);
3512 msiobj_release( &uirow->hdr );
3513 cleanup:
3514 msi_free(fullname);
3515 msi_free(folder);
3516 msi_free(deformated_key);
3517 msi_free(deformated_value);
3518 msi_free(deformated_section);
3519 return ERROR_SUCCESS;
3522 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3524 UINT rc;
3525 MSIQUERY * view;
3526 static const WCHAR ExecSeqQuery[] =
3527 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3528 '`','I','n','i','F','i','l','e','`',0};
3530 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3531 if (rc != ERROR_SUCCESS)
3533 TRACE("no IniFile table\n");
3534 return ERROR_SUCCESS;
3537 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3538 msiobj_release(&view->hdr);
3539 return rc;
3542 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3544 MSIPACKAGE *package = (MSIPACKAGE*)param;
3545 LPCWSTR filename;
3546 LPWSTR FullName;
3547 MSIFILE *file;
3548 DWORD len;
3549 static const WCHAR ExeStr[] =
3550 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3551 static const WCHAR close[] = {'\"',0};
3552 STARTUPINFOW si;
3553 PROCESS_INFORMATION info;
3554 BOOL brc;
3555 MSIRECORD *uirow;
3556 LPWSTR uipath, p;
3558 memset(&si,0,sizeof(STARTUPINFOW));
3560 filename = MSI_RecordGetString(row,1);
3561 file = get_loaded_file( package, filename );
3563 if (!file)
3565 ERR("Unable to find file id %s\n",debugstr_w(filename));
3566 return ERROR_SUCCESS;
3569 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3571 FullName = msi_alloc(len*sizeof(WCHAR));
3572 strcpyW(FullName,ExeStr);
3573 strcatW( FullName, file->TargetPath );
3574 strcatW(FullName,close);
3576 TRACE("Registering %s\n",debugstr_w(FullName));
3577 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3578 &si, &info);
3580 if (brc)
3581 msi_dialog_check_messages(info.hProcess);
3583 msi_free(FullName);
3585 /* the UI chunk */
3586 uirow = MSI_CreateRecord( 2 );
3587 uipath = strdupW( file->TargetPath );
3588 p = strrchrW(uipath,'\\');
3589 if (p)
3590 p[0]=0;
3591 MSI_RecordSetStringW( uirow, 1, &p[1] );
3592 MSI_RecordSetStringW( uirow, 2, uipath);
3593 ui_actiondata( package, szSelfRegModules, uirow);
3594 msiobj_release( &uirow->hdr );
3595 msi_free( uipath );
3596 /* FIXME: call ui_progress? */
3598 return ERROR_SUCCESS;
3601 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3603 UINT rc;
3604 MSIQUERY * view;
3605 static const WCHAR ExecSeqQuery[] =
3606 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3607 '`','S','e','l','f','R','e','g','`',0};
3609 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3610 if (rc != ERROR_SUCCESS)
3612 TRACE("no SelfReg table\n");
3613 return ERROR_SUCCESS;
3616 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3617 msiobj_release(&view->hdr);
3619 return ERROR_SUCCESS;
3622 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3624 MSIFEATURE *feature;
3625 UINT rc;
3626 HKEY hkey=0;
3627 HKEY hukey=0;
3628 HKEY userdata=0;
3630 if (!msi_check_publish(package))
3631 return ERROR_SUCCESS;
3633 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3634 if (rc != ERROR_SUCCESS)
3635 goto end;
3637 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3638 if (rc != ERROR_SUCCESS)
3639 goto end;
3641 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &userdata, TRUE);
3642 if (rc != ERROR_SUCCESS)
3643 goto end;
3645 /* here the guids are base 85 encoded */
3646 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3648 ComponentList *cl;
3649 LPWSTR data = NULL;
3650 GUID clsid;
3651 INT size;
3652 BOOL absent = FALSE;
3653 MSIRECORD *uirow;
3655 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3656 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3657 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3658 absent = TRUE;
3660 size = 1;
3661 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3663 size += 21;
3665 if (feature->Feature_Parent)
3666 size += strlenW( feature->Feature_Parent )+2;
3668 data = msi_alloc(size * sizeof(WCHAR));
3670 data[0] = 0;
3671 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3673 MSICOMPONENT* component = cl->component;
3674 WCHAR buf[21];
3676 buf[0] = 0;
3677 if (component->ComponentId)
3679 TRACE("From %s\n",debugstr_w(component->ComponentId));
3680 CLSIDFromString(component->ComponentId, &clsid);
3681 encode_base85_guid(&clsid,buf);
3682 TRACE("to %s\n",debugstr_w(buf));
3683 strcatW(data,buf);
3687 if (feature->Feature_Parent)
3689 static const WCHAR sep[] = {'\2',0};
3690 strcatW(data,sep);
3691 strcatW(data,feature->Feature_Parent);
3694 msi_reg_set_val_str( hkey, feature->Feature, data );
3695 msi_reg_set_val_str( userdata, feature->Feature, data );
3696 msi_free(data);
3698 size = 0;
3699 if (feature->Feature_Parent)
3700 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3701 if (!absent)
3703 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3704 (LPBYTE)feature->Feature_Parent,size);
3706 else
3708 size += 2*sizeof(WCHAR);
3709 data = msi_alloc(size);
3710 data[0] = 0x6;
3711 data[1] = 0;
3712 if (feature->Feature_Parent)
3713 strcpyW( &data[1], feature->Feature_Parent );
3714 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3715 (LPBYTE)data,size);
3716 msi_free(data);
3719 /* the UI chunk */
3720 uirow = MSI_CreateRecord( 1 );
3721 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3722 ui_actiondata( package, szPublishFeatures, uirow);
3723 msiobj_release( &uirow->hdr );
3724 /* FIXME: call ui_progress? */
3727 end:
3728 RegCloseKey(hkey);
3729 RegCloseKey(hukey);
3730 return rc;
3733 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3735 UINT r;
3736 HKEY hkey;
3738 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3740 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3741 if (r == ERROR_SUCCESS)
3743 RegDeleteValueW(hkey, feature->Feature);
3744 RegCloseKey(hkey);
3747 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3748 if (r == ERROR_SUCCESS)
3750 RegDeleteValueW(hkey, feature->Feature);
3751 RegCloseKey(hkey);
3754 return ERROR_SUCCESS;
3757 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3759 MSIFEATURE *feature;
3761 if (msi_check_publish(package))
3762 return ERROR_SUCCESS;
3764 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3766 msi_unpublish_feature(package, feature);
3769 return ERROR_SUCCESS;
3772 static UINT msi_get_local_package_name( LPWSTR path )
3774 static const WCHAR szInstaller[] = {
3775 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3776 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3777 DWORD time, len, i;
3778 HANDLE handle;
3780 time = GetTickCount();
3781 GetWindowsDirectoryW( path, MAX_PATH );
3782 lstrcatW( path, szInstaller );
3783 CreateDirectoryW( path, NULL );
3785 len = lstrlenW(path);
3786 for (i=0; i<0x10000; i++)
3788 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3789 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3790 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3791 if (handle != INVALID_HANDLE_VALUE)
3793 CloseHandle(handle);
3794 break;
3796 if (GetLastError() != ERROR_FILE_EXISTS &&
3797 GetLastError() != ERROR_SHARING_VIOLATION)
3798 return ERROR_FUNCTION_FAILED;
3801 return ERROR_SUCCESS;
3804 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3806 static const WCHAR szOriginalDatabase[] =
3807 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3808 WCHAR packagefile[MAX_PATH];
3809 LPWSTR msiFilePath;
3810 HKEY props;
3811 UINT r;
3813 r = msi_get_local_package_name( packagefile );
3814 if (r != ERROR_SUCCESS)
3815 return r;
3817 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3819 msiFilePath = msi_dup_property( package, szOriginalDatabase );
3820 r = CopyFileW( msiFilePath, packagefile, FALSE);
3822 if (!r)
3824 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3825 debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3826 msi_free( msiFilePath );
3827 return ERROR_FUNCTION_FAILED;
3829 msi_free( msiFilePath );
3831 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3833 r = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3834 if (r != ERROR_SUCCESS)
3835 return r;
3837 msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3838 RegCloseKey(props);
3839 return ERROR_SUCCESS;
3842 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3844 LPWSTR prop, val, key;
3845 static const LPCSTR propval[] = {
3846 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3847 "ARPCONTACT", "Contact",
3848 "ARPCOMMENTS", "Comments",
3849 "ProductName", "DisplayName",
3850 "ProductVersion", "DisplayVersion",
3851 "ARPHELPLINK", "HelpLink",
3852 "ARPHELPTELEPHONE", "HelpTelephone",
3853 "ARPINSTALLLOCATION", "InstallLocation",
3854 "SourceDir", "InstallSource",
3855 "Manufacturer", "Publisher",
3856 "ARPREADME", "Readme",
3857 "ARPSIZE", "Size",
3858 "ARPURLINFOABOUT", "URLInfoAbout",
3859 "ARPURLUPDATEINFO", "URLUpdateInfo",
3860 NULL,
3862 const LPCSTR *p = propval;
3864 while( *p )
3866 prop = strdupAtoW( *p++ );
3867 key = strdupAtoW( *p++ );
3868 val = msi_dup_property( package, prop );
3869 msi_reg_set_val_str( hkey, key, val );
3870 msi_free(val);
3871 msi_free(key);
3872 msi_free(prop);
3874 return ERROR_SUCCESS;
3877 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3879 HKEY hkey=0;
3880 HKEY hudkey=0, props=0;
3881 LPWSTR buffer = NULL;
3882 UINT rc;
3883 DWORD size, langid;
3884 static const WCHAR szWindowsInstaller[] =
3885 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3886 static const WCHAR szUpgradeCode[] =
3887 {'U','p','g','r','a','d','e','C','o','d','e',0};
3888 static const WCHAR modpath_fmt[] =
3889 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3890 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3891 static const WCHAR szModifyPath[] =
3892 {'M','o','d','i','f','y','P','a','t','h',0};
3893 static const WCHAR szUninstallString[] =
3894 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3895 static const WCHAR szEstimatedSize[] =
3896 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3897 static const WCHAR szProductLanguage[] =
3898 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3899 static const WCHAR szProductVersion[] =
3900 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3902 SYSTEMTIME systime;
3903 static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3904 LPWSTR upgrade_code;
3905 WCHAR szDate[9];
3907 /* FIXME: also need to publish if the product is in advertise mode */
3908 if (!msi_check_publish(package))
3909 return ERROR_SUCCESS;
3911 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3912 if (rc != ERROR_SUCCESS)
3913 return rc;
3915 /* dump all the info i can grab */
3916 /* FIXME: Flesh out more information */
3918 msi_write_uninstall_property_vals( package, hkey );
3920 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3922 msi_make_package_local( package, hkey );
3924 /* do ModifyPath and UninstallString */
3925 size = deformat_string(package,modpath_fmt,&buffer);
3926 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3927 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3928 msi_free(buffer);
3930 /* FIXME: Write real Estimated Size when we have it */
3931 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3933 GetLocalTime(&systime);
3934 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3935 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3937 langid = msi_get_property_int( package, szProductLanguage, 0 );
3938 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3940 buffer = msi_dup_property( package, szProductVersion );
3941 if (buffer)
3943 DWORD verdword = msi_version_str_to_dword(buffer);
3945 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3946 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3947 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3949 msi_free(buffer);
3951 /* Handle Upgrade Codes */
3952 upgrade_code = msi_dup_property( package, szUpgradeCode );
3953 if (upgrade_code)
3955 HKEY hkey2;
3956 WCHAR squashed[33];
3957 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3958 squash_guid(package->ProductCode,squashed);
3959 msi_reg_set_val_str( hkey2, squashed, NULL );
3960 RegCloseKey(hkey2);
3961 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3962 squash_guid(package->ProductCode,squashed);
3963 msi_reg_set_val_str( hkey2, squashed, NULL );
3964 RegCloseKey(hkey2);
3966 msi_free(upgrade_code);
3969 RegCloseKey(hkey);
3971 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3972 if (rc != ERROR_SUCCESS)
3973 return rc;
3975 RegCloseKey(hudkey);
3977 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3978 if (rc != ERROR_SUCCESS)
3979 return rc;
3981 msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
3982 RegCloseKey(props);
3984 return ERROR_SUCCESS;
3987 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
3989 return execute_script(package,INSTALL_SCRIPT);
3992 static UINT msi_unpublish_product(MSIPACKAGE *package)
3994 LPWSTR remove = NULL;
3995 LPWSTR *features = NULL;
3996 BOOL full_uninstall = TRUE;
3997 MSIFEATURE *feature;
3999 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4000 static const WCHAR szAll[] = {'A','L','L',0};
4002 remove = msi_dup_property(package, szRemove);
4003 if (!remove)
4004 return ERROR_SUCCESS;
4006 features = msi_split_string(remove, ',');
4007 if (!features)
4009 msi_free(remove);
4010 ERR("REMOVE feature list is empty!\n");
4011 return ERROR_FUNCTION_FAILED;
4014 if (!lstrcmpW(features[0], szAll))
4015 full_uninstall = TRUE;
4016 else
4018 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4020 if (feature->Action != INSTALLSTATE_ABSENT)
4021 full_uninstall = FALSE;
4025 if (!full_uninstall)
4026 goto done;
4028 MSIREG_DeleteProductKey(package->ProductCode);
4029 MSIREG_DeleteUserProductKey(package->ProductCode);
4030 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4031 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4033 done:
4034 msi_free(remove);
4035 msi_free(features);
4036 return ERROR_SUCCESS;
4039 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4041 UINT rc;
4043 rc = msi_unpublish_product(package);
4044 if (rc != ERROR_SUCCESS)
4045 return rc;
4047 /* turn off scheduling */
4048 package->script->CurrentlyScripting= FALSE;
4050 /* first do the same as an InstallExecute */
4051 rc = ACTION_InstallExecute(package);
4052 if (rc != ERROR_SUCCESS)
4053 return rc;
4055 /* then handle Commit Actions */
4056 rc = execute_script(package,COMMIT_SCRIPT);
4058 return rc;
4061 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4063 static const WCHAR RunOnce[] = {
4064 'S','o','f','t','w','a','r','e','\\',
4065 'M','i','c','r','o','s','o','f','t','\\',
4066 'W','i','n','d','o','w','s','\\',
4067 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4068 'R','u','n','O','n','c','e',0};
4069 static const WCHAR InstallRunOnce[] = {
4070 'S','o','f','t','w','a','r','e','\\',
4071 'M','i','c','r','o','s','o','f','t','\\',
4072 'W','i','n','d','o','w','s','\\',
4073 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4074 'I','n','s','t','a','l','l','e','r','\\',
4075 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4077 static const WCHAR msiexec_fmt[] = {
4078 '%','s',
4079 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4080 '\"','%','s','\"',0};
4081 static const WCHAR install_fmt[] = {
4082 '/','I',' ','\"','%','s','\"',' ',
4083 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4084 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4085 WCHAR buffer[256], sysdir[MAX_PATH];
4086 HKEY hkey;
4087 WCHAR squished_pc[100];
4089 squash_guid(package->ProductCode,squished_pc);
4091 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4092 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4093 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4094 squished_pc);
4096 msi_reg_set_val_str( hkey, squished_pc, buffer );
4097 RegCloseKey(hkey);
4099 TRACE("Reboot command %s\n",debugstr_w(buffer));
4101 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4102 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4104 msi_reg_set_val_str( hkey, squished_pc, buffer );
4105 RegCloseKey(hkey);
4107 return ERROR_INSTALL_SUSPEND;
4110 static UINT msi_set_sourcedir_props(MSIPACKAGE *package)
4112 LPWSTR p, source;
4113 DWORD len;
4115 p = strrchrW( package->PackagePath, '\\' );
4116 if (!p)
4117 return ERROR_SUCCESS;
4119 len = p - package->PackagePath + 2;
4120 source = msi_alloc( len * sizeof(WCHAR) );
4121 lstrcpynW( source, package->PackagePath, len );
4123 MSI_SetPropertyW( package, cszSourceDir, source );
4124 MSI_SetPropertyW( package, cszSOURCEDIR, source );
4126 msi_free( source );
4128 return ERROR_SUCCESS;
4131 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4133 DWORD attrib;
4134 UINT rc;
4137 * We are currently doing what should be done here in the top level Install
4138 * however for Administrative and uninstalls this step will be needed
4140 if (!package->PackagePath)
4141 return ERROR_SUCCESS;
4143 msi_set_sourcedir_props(package);
4145 attrib = GetFileAttributesW(package->PackagePath);
4146 if (attrib == INVALID_FILE_ATTRIBUTES)
4148 LPWSTR prompt;
4149 LPWSTR msg;
4150 DWORD size = 0;
4152 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4153 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4154 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4155 if (rc == ERROR_MORE_DATA)
4157 prompt = msi_alloc(size * sizeof(WCHAR));
4158 MsiSourceListGetInfoW(package->ProductCode, NULL,
4159 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4160 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4162 else
4163 prompt = strdupW(package->PackagePath);
4165 msg = generate_error_string(package,1302,1,prompt);
4166 while(attrib == INVALID_FILE_ATTRIBUTES)
4168 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4169 if (rc == IDCANCEL)
4171 rc = ERROR_INSTALL_USEREXIT;
4172 break;
4174 attrib = GetFileAttributesW(package->PackagePath);
4176 msi_free(prompt);
4177 rc = ERROR_SUCCESS;
4179 else
4180 return ERROR_SUCCESS;
4182 return rc;
4185 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4187 HKEY hkey=0;
4188 LPWSTR buffer;
4189 LPWSTR productid;
4190 UINT rc,i;
4192 static const WCHAR szPropKeys[][80] =
4194 {'P','r','o','d','u','c','t','I','D',0},
4195 {'U','S','E','R','N','A','M','E',0},
4196 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4197 {0},
4200 static const WCHAR szRegKeys[][80] =
4202 {'P','r','o','d','u','c','t','I','D',0},
4203 {'R','e','g','O','w','n','e','r',0},
4204 {'R','e','g','C','o','m','p','a','n','y',0},
4205 {0},
4208 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4209 if (!productid)
4210 return ERROR_SUCCESS;
4212 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4213 if (rc != ERROR_SUCCESS)
4214 goto end;
4216 for( i = 0; szPropKeys[i][0]; i++ )
4218 buffer = msi_dup_property( package, szPropKeys[i] );
4219 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4220 msi_free( buffer );
4223 end:
4224 msi_free(productid);
4225 RegCloseKey(hkey);
4227 /* FIXME: call ui_actiondata */
4229 return ERROR_SUCCESS;
4233 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4235 UINT rc;
4237 package->script->InWhatSequence |= SEQUENCE_EXEC;
4238 rc = ACTION_ProcessExecSequence(package,FALSE);
4239 return rc;
4243 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4245 MSIPACKAGE *package = (MSIPACKAGE*)param;
4246 LPCWSTR compgroupid=NULL;
4247 LPCWSTR feature=NULL;
4248 LPCWSTR text = NULL;
4249 LPCWSTR qualifier = NULL;
4250 LPCWSTR component = NULL;
4251 LPWSTR advertise = NULL;
4252 LPWSTR output = NULL;
4253 HKEY hkey;
4254 UINT rc = ERROR_SUCCESS;
4255 MSICOMPONENT *comp;
4256 DWORD sz = 0;
4257 MSIRECORD *uirow;
4259 component = MSI_RecordGetString(rec,3);
4260 comp = get_loaded_component(package,component);
4262 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4263 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4264 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4266 TRACE("Skipping: Component %s not scheduled for install\n",
4267 debugstr_w(component));
4269 return ERROR_SUCCESS;
4272 compgroupid = MSI_RecordGetString(rec,1);
4273 qualifier = MSI_RecordGetString(rec,2);
4275 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4276 if (rc != ERROR_SUCCESS)
4277 goto end;
4279 text = MSI_RecordGetString(rec,4);
4280 feature = MSI_RecordGetString(rec,5);
4282 advertise = create_component_advertise_string(package, comp, feature);
4284 sz = strlenW(advertise);
4286 if (text)
4287 sz += lstrlenW(text);
4289 sz+=3;
4290 sz *= sizeof(WCHAR);
4292 output = msi_alloc_zero(sz);
4293 strcpyW(output,advertise);
4294 msi_free(advertise);
4296 if (text)
4297 strcatW(output,text);
4299 msi_reg_set_val_multi_str( hkey, qualifier, output );
4301 end:
4302 RegCloseKey(hkey);
4303 msi_free(output);
4305 /* the UI chunk */
4306 uirow = MSI_CreateRecord( 2 );
4307 MSI_RecordSetStringW( uirow, 1, compgroupid );
4308 MSI_RecordSetStringW( uirow, 2, qualifier);
4309 ui_actiondata( package, szPublishComponents, uirow);
4310 msiobj_release( &uirow->hdr );
4311 /* FIXME: call ui_progress? */
4313 return rc;
4317 * At present I am ignorning the advertised components part of this and only
4318 * focusing on the qualified component sets
4320 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4322 UINT rc;
4323 MSIQUERY * view;
4324 static const WCHAR ExecSeqQuery[] =
4325 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4326 '`','P','u','b','l','i','s','h',
4327 'C','o','m','p','o','n','e','n','t','`',0};
4329 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4330 if (rc != ERROR_SUCCESS)
4331 return ERROR_SUCCESS;
4333 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4334 msiobj_release(&view->hdr);
4336 return rc;
4339 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4341 MSIPACKAGE *package = (MSIPACKAGE*)param;
4342 MSIRECORD *row;
4343 MSIFILE *file;
4344 SC_HANDLE hscm, service = NULL;
4345 LPCWSTR name, disp, comp, depends, pass;
4346 LPCWSTR load_order, serv_name, key;
4347 DWORD serv_type, start_type;
4348 DWORD err_control;
4350 static const WCHAR query[] =
4351 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4352 '`','C','o','m','p','o','n','e','n','t','`',' ',
4353 'W','H','E','R','E',' ',
4354 '`','C','o','m','p','o','n','e','n','t','`',' ',
4355 '=','\'','%','s','\'',0};
4357 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4358 if (!hscm)
4360 ERR("Failed to open the SC Manager!\n");
4361 goto done;
4364 start_type = MSI_RecordGetInteger(rec, 5);
4365 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4366 goto done;
4368 depends = MSI_RecordGetString(rec, 8);
4369 if (depends && *depends)
4370 FIXME("Dependency list unhandled!\n");
4372 name = MSI_RecordGetString(rec, 2);
4373 disp = MSI_RecordGetString(rec, 3);
4374 serv_type = MSI_RecordGetInteger(rec, 4);
4375 err_control = MSI_RecordGetInteger(rec, 6);
4376 load_order = MSI_RecordGetString(rec, 7);
4377 serv_name = MSI_RecordGetString(rec, 9);
4378 pass = MSI_RecordGetString(rec, 10);
4379 comp = MSI_RecordGetString(rec, 12);
4381 /* fetch the service path */
4382 row = MSI_QueryGetRecord(package->db, query, comp);
4383 if (!row)
4385 ERR("Control query failed!\n");
4386 goto done;
4389 key = MSI_RecordGetString(row, 6);
4391 file = get_loaded_file(package, key);
4392 msiobj_release(&row->hdr);
4393 if (!file)
4395 ERR("Failed to load the service file\n");
4396 goto done;
4399 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4400 start_type, err_control, file->TargetPath,
4401 load_order, NULL, NULL, serv_name, pass);
4402 if (!service)
4404 if (GetLastError() != ERROR_SERVICE_EXISTS)
4405 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4408 done:
4409 CloseServiceHandle(service);
4410 CloseServiceHandle(hscm);
4412 return ERROR_SUCCESS;
4415 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4417 UINT rc;
4418 MSIQUERY * view;
4419 static const WCHAR ExecSeqQuery[] =
4420 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4421 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4423 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4424 if (rc != ERROR_SUCCESS)
4425 return ERROR_SUCCESS;
4427 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4428 msiobj_release(&view->hdr);
4430 return rc;
4433 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4434 static LPCWSTR *msi_service_args_to_vector(LPCWSTR name, LPWSTR args, DWORD *numargs)
4436 LPCWSTR *vector;
4437 LPWSTR p, q;
4438 DWORD sep_len;
4440 static const WCHAR separator[] = {'[','~',']',0};
4442 *numargs = 0;
4443 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4445 if (!args)
4446 return NULL;
4448 vector = msi_alloc(sizeof(LPWSTR));
4449 if (!vector)
4450 return NULL;
4452 p = args;
4455 (*numargs)++;
4456 vector[*numargs - 1] = p;
4458 if ((q = strstrW(p, separator)))
4460 *q = '\0';
4462 vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4463 if (!vector)
4464 return NULL;
4466 p = q + sep_len;
4468 } while (q);
4470 return vector;
4473 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4475 MSIPACKAGE *package = (MSIPACKAGE *)param;
4476 MSICOMPONENT *comp;
4477 SC_HANDLE scm, service = NULL;
4478 LPCWSTR name, *vector = NULL;
4479 LPWSTR args;
4480 DWORD event, numargs;
4481 UINT r = ERROR_FUNCTION_FAILED;
4483 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4484 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4485 return ERROR_SUCCESS;
4487 name = MSI_RecordGetString(rec, 2);
4488 event = MSI_RecordGetInteger(rec, 3);
4489 args = strdupW(MSI_RecordGetString(rec, 4));
4491 if (!(event & msidbServiceControlEventStart))
4492 return ERROR_SUCCESS;
4494 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4495 if (!scm)
4497 ERR("Failed to open the service control manager\n");
4498 goto done;
4501 service = OpenServiceW(scm, name, SERVICE_START);
4502 if (!service)
4504 ERR("Failed to open service %s\n", debugstr_w(name));
4505 goto done;
4508 vector = msi_service_args_to_vector(name, args, &numargs);
4510 if (!StartServiceW(service, numargs, vector))
4512 ERR("Failed to start service %s\n", debugstr_w(name));
4513 goto done;
4516 r = ERROR_SUCCESS;
4518 done:
4519 CloseServiceHandle(service);
4520 CloseServiceHandle(scm);
4522 msi_free(args);
4523 msi_free(vector);
4524 return r;
4527 static UINT ACTION_StartServices( MSIPACKAGE *package )
4529 UINT rc;
4530 MSIQUERY *view;
4532 static const WCHAR query[] = {
4533 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4534 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4536 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4537 if (rc != ERROR_SUCCESS)
4538 return ERROR_SUCCESS;
4540 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4541 msiobj_release(&view->hdr);
4543 return rc;
4546 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4548 MSIFILE *file;
4550 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4552 if (!lstrcmpW(file->File, filename))
4553 return file;
4556 return NULL;
4559 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4561 MSIPACKAGE *package = (MSIPACKAGE*)param;
4562 LPWSTR driver, driver_path, ptr;
4563 WCHAR outpath[MAX_PATH];
4564 MSIFILE *driver_file, *setup_file;
4565 LPCWSTR desc;
4566 DWORD len, usage;
4567 UINT r = ERROR_SUCCESS;
4569 static const WCHAR driver_fmt[] = {
4570 'D','r','i','v','e','r','=','%','s',0};
4571 static const WCHAR setup_fmt[] = {
4572 'S','e','t','u','p','=','%','s',0};
4573 static const WCHAR usage_fmt[] = {
4574 'F','i','l','e','U','s','a','g','e','=','1',0};
4576 desc = MSI_RecordGetString(rec, 3);
4578 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4579 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4581 if (!driver_file || !setup_file)
4583 ERR("ODBC Driver entry not found!\n");
4584 return ERROR_FUNCTION_FAILED;
4587 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4588 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4589 lstrlenW(usage_fmt) + 1;
4590 driver = msi_alloc(len * sizeof(WCHAR));
4591 if (!driver)
4592 return ERROR_OUTOFMEMORY;
4594 ptr = driver;
4595 lstrcpyW(ptr, desc);
4596 ptr += lstrlenW(ptr) + 1;
4598 sprintfW(ptr, driver_fmt, driver_file->FileName);
4599 ptr += lstrlenW(ptr) + 1;
4601 sprintfW(ptr, setup_fmt, setup_file->FileName);
4602 ptr += lstrlenW(ptr) + 1;
4604 lstrcpyW(ptr, usage_fmt);
4605 ptr += lstrlenW(ptr) + 1;
4606 *ptr = '\0';
4608 driver_path = strdupW(driver_file->TargetPath);
4609 ptr = strrchrW(driver_path, '\\');
4610 if (ptr) *ptr = '\0';
4612 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4613 NULL, ODBC_INSTALL_COMPLETE, &usage))
4615 ERR("Failed to install SQL driver!\n");
4616 r = ERROR_FUNCTION_FAILED;
4619 msi_free(driver);
4620 msi_free(driver_path);
4622 return r;
4625 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4627 MSIPACKAGE *package = (MSIPACKAGE*)param;
4628 LPWSTR translator, translator_path, ptr;
4629 WCHAR outpath[MAX_PATH];
4630 MSIFILE *translator_file, *setup_file;
4631 LPCWSTR desc;
4632 DWORD len, usage;
4633 UINT r = ERROR_SUCCESS;
4635 static const WCHAR translator_fmt[] = {
4636 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4637 static const WCHAR setup_fmt[] = {
4638 'S','e','t','u','p','=','%','s',0};
4640 desc = MSI_RecordGetString(rec, 3);
4642 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4643 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4645 if (!translator_file || !setup_file)
4647 ERR("ODBC Translator entry not found!\n");
4648 return ERROR_FUNCTION_FAILED;
4651 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4652 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4653 translator = msi_alloc(len * sizeof(WCHAR));
4654 if (!translator)
4655 return ERROR_OUTOFMEMORY;
4657 ptr = translator;
4658 lstrcpyW(ptr, desc);
4659 ptr += lstrlenW(ptr) + 1;
4661 sprintfW(ptr, translator_fmt, translator_file->FileName);
4662 ptr += lstrlenW(ptr) + 1;
4664 sprintfW(ptr, setup_fmt, setup_file->FileName);
4665 ptr += lstrlenW(ptr) + 1;
4666 *ptr = '\0';
4668 translator_path = strdupW(translator_file->TargetPath);
4669 ptr = strrchrW(translator_path, '\\');
4670 if (ptr) *ptr = '\0';
4672 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4673 NULL, ODBC_INSTALL_COMPLETE, &usage))
4675 ERR("Failed to install SQL translator!\n");
4676 r = ERROR_FUNCTION_FAILED;
4679 msi_free(translator);
4680 msi_free(translator_path);
4682 return r;
4685 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4687 LPWSTR attrs;
4688 LPCWSTR desc, driver;
4689 WORD request = ODBC_ADD_SYS_DSN;
4690 INT registration;
4691 DWORD len;
4692 UINT r = ERROR_SUCCESS;
4694 static const WCHAR attrs_fmt[] = {
4695 'D','S','N','=','%','s',0 };
4697 desc = MSI_RecordGetString(rec, 3);
4698 driver = MSI_RecordGetString(rec, 4);
4699 registration = MSI_RecordGetInteger(rec, 5);
4701 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4702 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4704 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4705 attrs = msi_alloc(len * sizeof(WCHAR));
4706 if (!attrs)
4707 return ERROR_OUTOFMEMORY;
4709 sprintfW(attrs, attrs_fmt, desc);
4710 attrs[len - 1] = '\0';
4712 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4714 ERR("Failed to install SQL data source!\n");
4715 r = ERROR_FUNCTION_FAILED;
4718 msi_free(attrs);
4720 return r;
4723 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4725 UINT rc;
4726 MSIQUERY *view;
4728 static const WCHAR driver_query[] = {
4729 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4730 'O','D','B','C','D','r','i','v','e','r',0 };
4732 static const WCHAR translator_query[] = {
4733 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4734 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4736 static const WCHAR source_query[] = {
4737 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4738 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4740 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4741 if (rc != ERROR_SUCCESS)
4742 return ERROR_SUCCESS;
4744 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4745 msiobj_release(&view->hdr);
4747 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4748 if (rc != ERROR_SUCCESS)
4749 return ERROR_SUCCESS;
4751 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4752 msiobj_release(&view->hdr);
4754 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4755 if (rc != ERROR_SUCCESS)
4756 return ERROR_SUCCESS;
4758 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4759 msiobj_release(&view->hdr);
4761 return rc;
4764 #define ENV_ACT_SETALWAYS 0x1
4765 #define ENV_ACT_SETABSENT 0x2
4766 #define ENV_ACT_REMOVE 0x4
4767 #define ENV_ACT_REMOVEMATCH 0x8
4769 #define ENV_MOD_MACHINE 0x20000000
4770 #define ENV_MOD_APPEND 0x40000000
4771 #define ENV_MOD_PREFIX 0x80000000
4772 #define ENV_MOD_MASK 0xC0000000
4774 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4776 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4778 LPCWSTR cptr = *name;
4779 LPCWSTR ptr = *value;
4781 static const WCHAR prefix[] = {'[','~',']',0};
4782 static const int prefix_len = 3;
4784 *flags = 0;
4785 while (*cptr)
4787 if (*cptr == '=')
4788 *flags |= ENV_ACT_SETALWAYS;
4789 else if (*cptr == '+')
4790 *flags |= ENV_ACT_SETABSENT;
4791 else if (*cptr == '-')
4792 *flags |= ENV_ACT_REMOVE;
4793 else if (*cptr == '!')
4794 *flags |= ENV_ACT_REMOVEMATCH;
4795 else if (*cptr == '*')
4796 *flags |= ENV_MOD_MACHINE;
4797 else
4798 break;
4800 cptr++;
4801 (*name)++;
4804 if (!*cptr)
4806 ERR("Missing environment variable\n");
4807 return ERROR_FUNCTION_FAILED;
4810 if (!strncmpW(ptr, prefix, prefix_len))
4812 *flags |= ENV_MOD_APPEND;
4813 *value += lstrlenW(prefix);
4815 else if (lstrlenW(*value) >= prefix_len)
4817 ptr += lstrlenW(ptr) - prefix_len;
4818 if (!lstrcmpW(ptr, prefix))
4820 *flags |= ENV_MOD_PREFIX;
4821 /* the "[~]" will be removed by deformat_string */;
4825 if (!*flags ||
4826 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4827 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4828 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4829 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4831 ERR("Invalid flags: %08x\n", *flags);
4832 return ERROR_FUNCTION_FAILED;
4835 return ERROR_SUCCESS;
4838 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4840 MSIPACKAGE *package = param;
4841 LPCWSTR name, value, comp;
4842 LPWSTR data = NULL, newval = NULL;
4843 LPWSTR deformatted = NULL, ptr;
4844 DWORD flags, type, size;
4845 LONG res;
4846 HKEY env = NULL, root = HKEY_CURRENT_USER;
4848 static const WCHAR environment[] =
4849 {'S','y','s','t','e','m','\\',
4850 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4851 'C','o','n','t','r','o','l','\\',
4852 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4853 'E','n','v','i','r','o','n','m','e','n','t',0};
4854 static const WCHAR semicolon[] = {';',0};
4856 name = MSI_RecordGetString(rec, 2);
4857 value = MSI_RecordGetString(rec, 3);
4858 comp = MSI_RecordGetString(rec, 4);
4860 res = env_set_flags(&name, &value, &flags);
4861 if (res != ERROR_SUCCESS)
4862 goto done;
4864 deformat_string(package, value, &deformatted);
4865 if (!deformatted)
4867 res = ERROR_OUTOFMEMORY;
4868 goto done;
4871 value = deformatted;
4873 if (flags & ENV_MOD_MACHINE)
4874 root = HKEY_LOCAL_MACHINE;
4876 res = RegOpenKeyExW(root, environment, 0, KEY_ALL_ACCESS, &env);
4877 if (res != ERROR_SUCCESS)
4878 goto done;
4880 if (flags & ENV_ACT_REMOVE)
4881 FIXME("Not removing environment variable on uninstall!\n");
4883 size = 0;
4884 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
4885 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
4886 (res == ERROR_SUCCESS && type != REG_SZ))
4887 goto done;
4889 if (res != ERROR_FILE_NOT_FOUND)
4891 if (flags & ENV_ACT_SETABSENT)
4893 res = ERROR_SUCCESS;
4894 goto done;
4897 data = msi_alloc(size);
4898 if (!data)
4900 RegCloseKey(env);
4901 return ERROR_OUTOFMEMORY;
4904 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
4905 if (res != ERROR_SUCCESS)
4906 goto done;
4908 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
4910 res = RegDeleteKeyW(env, name);
4911 goto done;
4914 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
4915 newval = msi_alloc(size);
4916 ptr = newval;
4917 if (!newval)
4919 res = ERROR_OUTOFMEMORY;
4920 goto done;
4923 if (!(flags & ENV_MOD_MASK))
4924 lstrcpyW(newval, value);
4925 else
4927 if (flags & ENV_MOD_PREFIX)
4929 lstrcpyW(newval, value);
4930 lstrcatW(newval, semicolon);
4931 ptr = newval + lstrlenW(value) + 1;
4934 lstrcpyW(ptr, data);
4936 if (flags & ENV_MOD_APPEND)
4938 lstrcatW(newval, semicolon);
4939 lstrcatW(newval, value);
4943 else
4945 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
4946 newval = msi_alloc(size);
4947 if (!newval)
4949 res = ERROR_OUTOFMEMORY;
4950 goto done;
4953 lstrcpyW(newval, value);
4956 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
4957 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
4959 done:
4960 if (env) RegCloseKey(env);
4961 msi_free(deformatted);
4962 msi_free(data);
4963 msi_free(newval);
4964 return res;
4967 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
4969 UINT rc;
4970 MSIQUERY * view;
4971 static const WCHAR ExecSeqQuery[] =
4972 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4973 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
4974 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4975 if (rc != ERROR_SUCCESS)
4976 return ERROR_SUCCESS;
4978 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
4979 msiobj_release(&view->hdr);
4981 return rc;
4984 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
4986 typedef struct
4988 struct list entry;
4989 LPWSTR sourcename;
4990 LPWSTR destname;
4991 LPWSTR source;
4992 LPWSTR dest;
4993 } FILE_LIST;
4995 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
4997 BOOL ret;
4999 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5000 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5002 WARN("Source or dest is directory, not moving\n");
5003 return FALSE;
5006 if (options == msidbMoveFileOptionsMove)
5008 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5009 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5010 if (!ret)
5012 WARN("MoveFile failed: %d\n", GetLastError());
5013 return FALSE;
5016 else
5018 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5019 ret = CopyFileW(source, dest, FALSE);
5020 if (!ret)
5022 WARN("CopyFile failed: %d\n", GetLastError());
5023 return FALSE;
5027 return TRUE;
5030 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5032 LPWSTR path, ptr;
5033 DWORD dirlen, pathlen;
5035 ptr = strrchrW(wildcard, '\\');
5036 dirlen = ptr - wildcard + 1;
5038 pathlen = dirlen + lstrlenW(filename) + 1;
5039 path = msi_alloc(pathlen * sizeof(WCHAR));
5041 lstrcpynW(path, wildcard, dirlen + 1);
5042 lstrcatW(path, filename);
5044 return path;
5047 static void free_file_entry(FILE_LIST *file)
5049 msi_free(file->source);
5050 msi_free(file->dest);
5051 msi_free(file);
5054 static void free_list(FILE_LIST *list)
5056 while (!list_empty(&list->entry))
5058 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5060 list_remove(&file->entry);
5061 free_file_entry(file);
5065 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5067 FILE_LIST *new, *file;
5068 LPWSTR ptr, filename;
5069 DWORD size;
5071 new = msi_alloc_zero(sizeof(FILE_LIST));
5072 if (!new)
5073 return FALSE;
5075 new->source = strdupW(source);
5076 ptr = strrchrW(dest, '\\') + 1;
5077 filename = strrchrW(new->source, '\\') + 1;
5079 new->sourcename = filename;
5081 if (*ptr)
5082 new->destname = ptr;
5083 else
5084 new->destname = new->sourcename;
5086 size = (ptr - dest) + lstrlenW(filename) + 1;
5087 new->dest = msi_alloc(size * sizeof(WCHAR));
5088 if (!new->dest)
5090 free_file_entry(new);
5091 return FALSE;
5094 lstrcpynW(new->dest, dest, ptr - dest + 1);
5095 lstrcatW(new->dest, filename);
5097 if (list_empty(&files->entry))
5099 list_add_head(&files->entry, &new->entry);
5100 return TRUE;
5103 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5105 if (lstrcmpW(source, file->source) < 0)
5107 list_add_before(&file->entry, &new->entry);
5108 return TRUE;
5112 list_add_after(&file->entry, &new->entry);
5113 return TRUE;
5116 BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5118 WIN32_FIND_DATAW wfd;
5119 HANDLE hfile;
5120 LPWSTR path;
5121 BOOL res;
5122 FILE_LIST files, *file;
5123 DWORD size;
5125 hfile = FindFirstFileW(source, &wfd);
5126 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5128 list_init(&files.entry);
5130 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5132 if (is_dot_dir(wfd.cFileName)) continue;
5134 path = wildcard_to_file(source, wfd.cFileName);
5135 if (!path)
5137 free_list(&files);
5138 return FALSE;
5141 add_wildcard(&files, path, dest);
5142 msi_free(path);
5145 /* only the first wildcard match gets renamed to dest */
5146 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5147 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5148 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5149 if (!file->dest)
5151 free_list(&files);
5152 return FALSE;
5155 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5157 while (!list_empty(&files.entry))
5159 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5161 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5163 list_remove(&file->entry);
5164 free_file_entry(file);
5167 return TRUE;
5170 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5172 MSIPACKAGE *package = param;
5173 MSICOMPONENT *comp;
5174 LPCWSTR sourcename, destname;
5175 LPWSTR sourcedir = NULL, destdir = NULL;
5176 LPWSTR source = NULL, dest = NULL;
5177 int options;
5178 DWORD size;
5179 BOOL ret, wildcards;
5181 static const WCHAR backslash[] = {'\\',0};
5183 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5184 if (!comp || !comp->Enabled ||
5185 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5187 TRACE("Component not set for install, not moving file\n");
5188 return ERROR_SUCCESS;
5191 sourcename = MSI_RecordGetString(rec, 3);
5192 destname = MSI_RecordGetString(rec, 4);
5193 options = MSI_RecordGetInteger(rec, 7);
5195 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5196 if (!sourcedir)
5197 goto done;
5199 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5200 if (!destdir)
5201 goto done;
5203 if (!sourcename)
5205 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5206 goto done;
5208 source = strdupW(sourcedir);
5209 if (!source)
5210 goto done;
5212 else
5214 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5215 source = msi_alloc(size * sizeof(WCHAR));
5216 if (!source)
5217 goto done;
5219 lstrcpyW(source, sourcedir);
5220 if (source[lstrlenW(source) - 1] != '\\')
5221 lstrcatW(source, backslash);
5222 lstrcatW(source, sourcename);
5225 wildcards = strchrW(source, '*') || strchrW(source, '?');
5227 if (!destname && !wildcards)
5229 destname = strdupW(sourcename);
5230 if (!destname)
5231 goto done;
5234 size = 0;
5235 if (destname)
5236 size = lstrlenW(destname);
5238 size += lstrlenW(destdir) + 2;
5239 dest = msi_alloc(size * sizeof(WCHAR));
5240 if (!dest)
5241 goto done;
5243 lstrcpyW(dest, destdir);
5244 if (dest[lstrlenW(dest) - 1] != '\\')
5245 lstrcatW(dest, backslash);
5247 if (destname)
5248 lstrcatW(dest, destname);
5250 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5252 ret = CreateDirectoryW(destdir, NULL);
5253 if (!ret)
5255 WARN("CreateDirectory failed: %d\n", GetLastError());
5256 return ERROR_SUCCESS;
5260 if (!wildcards)
5261 msi_move_file(source, dest, options);
5262 else
5263 move_files_wildcard(source, dest, options);
5265 done:
5266 msi_free(sourcedir);
5267 msi_free(destdir);
5268 msi_free(source);
5269 msi_free(dest);
5271 return ERROR_SUCCESS;
5274 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5276 UINT rc;
5277 MSIQUERY *view;
5279 static const WCHAR ExecSeqQuery[] =
5280 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5281 '`','M','o','v','e','F','i','l','e','`',0};
5283 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5284 if (rc != ERROR_SUCCESS)
5285 return ERROR_SUCCESS;
5287 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5288 msiobj_release(&view->hdr);
5290 return rc;
5293 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5294 LPCSTR action, LPCWSTR table )
5296 static const WCHAR query[] = {
5297 'S','E','L','E','C','T',' ','*',' ',
5298 'F','R','O','M',' ','`','%','s','`',0 };
5299 MSIQUERY *view = NULL;
5300 DWORD count = 0;
5301 UINT r;
5303 r = MSI_OpenQuery( package->db, &view, query, table );
5304 if (r == ERROR_SUCCESS)
5306 r = MSI_IterateRecords(view, &count, NULL, package);
5307 msiobj_release(&view->hdr);
5310 if (count)
5311 FIXME("%s -> %u ignored %s table values\n",
5312 action, count, debugstr_w(table));
5314 return ERROR_SUCCESS;
5317 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5319 TRACE("%p\n", package);
5320 return ERROR_SUCCESS;
5323 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5325 static const WCHAR table[] =
5326 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5327 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5330 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5332 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5333 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5336 static UINT ACTION_BindImage( MSIPACKAGE *package )
5338 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5339 return msi_unimplemented_action_stub( package, "BindImage", table );
5342 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5344 static const WCHAR table[] = {
5345 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5346 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5349 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5351 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5352 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5355 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5357 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5358 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5361 static UINT ACTION_StopServices( MSIPACKAGE *package )
5363 static const WCHAR table[] = {
5364 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5365 return msi_unimplemented_action_stub( package, "StopServices", table );
5368 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5370 static const WCHAR table[] = {
5371 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5372 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5374 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5376 static const WCHAR table[] = {
5377 'P','r','o','d','u','c','t','I','D',0 };
5378 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5381 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5383 static const WCHAR table[] = {
5384 'E','n','v','i','r','o','n','m','e','n','t',0 };
5385 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5388 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5390 static const WCHAR table[] = {
5391 'M','s','i','A','s','s','e','m','b','l','y',0 };
5392 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5395 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5397 static const WCHAR table[] = {
5398 'M','s','i','A','s','s','e','m','b','l','y',0 };
5399 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5402 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5404 static const WCHAR table[] = { 'F','o','n','t',0 };
5405 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5408 static UINT ACTION_CCPSearch( MSIPACKAGE *package )
5410 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5411 return msi_unimplemented_action_stub( package, "CCPSearch", table );
5414 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5416 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5417 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5420 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5422 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5423 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5426 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5428 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5429 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5432 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5434 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5435 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5438 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5440 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5441 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5444 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5446 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5447 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5450 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5452 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5453 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5456 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5458 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5459 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5462 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5464 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5465 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5468 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5470 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5471 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5474 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5476 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5477 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5480 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5482 static const WCHAR table[] = { 'A','p','p','I','d',0 };
5483 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5486 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5488 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5489 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5492 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5494 static const WCHAR table[] = { 'M','I','M','E',0 };
5495 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5498 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5500 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5501 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5504 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5506 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5507 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5510 static const struct _actions StandardActions[] = {
5511 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5512 { szAppSearch, ACTION_AppSearch },
5513 { szBindImage, ACTION_BindImage },
5514 { szCCPSearch, ACTION_CCPSearch },
5515 { szCostFinalize, ACTION_CostFinalize },
5516 { szCostInitialize, ACTION_CostInitialize },
5517 { szCreateFolders, ACTION_CreateFolders },
5518 { szCreateShortcuts, ACTION_CreateShortcuts },
5519 { szDeleteServices, ACTION_DeleteServices },
5520 { szDisableRollback, NULL },
5521 { szDuplicateFiles, ACTION_DuplicateFiles },
5522 { szExecuteAction, ACTION_ExecuteAction },
5523 { szFileCost, ACTION_FileCost },
5524 { szFindRelatedProducts, ACTION_FindRelatedProducts },
5525 { szForceReboot, ACTION_ForceReboot },
5526 { szInstallAdminPackage, NULL },
5527 { szInstallExecute, ACTION_InstallExecute },
5528 { szInstallExecuteAgain, ACTION_InstallExecute },
5529 { szInstallFiles, ACTION_InstallFiles},
5530 { szInstallFinalize, ACTION_InstallFinalize },
5531 { szInstallInitialize, ACTION_InstallInitialize },
5532 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5533 { szInstallValidate, ACTION_InstallValidate },
5534 { szIsolateComponents, ACTION_IsolateComponents },
5535 { szLaunchConditions, ACTION_LaunchConditions },
5536 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5537 { szMoveFiles, ACTION_MoveFiles },
5538 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5539 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5540 { szInstallODBC, ACTION_InstallODBC },
5541 { szInstallServices, ACTION_InstallServices },
5542 { szPatchFiles, ACTION_PatchFiles },
5543 { szProcessComponents, ACTION_ProcessComponents },
5544 { szPublishComponents, ACTION_PublishComponents },
5545 { szPublishFeatures, ACTION_PublishFeatures },
5546 { szPublishProduct, ACTION_PublishProduct },
5547 { szRegisterClassInfo, ACTION_RegisterClassInfo },
5548 { szRegisterComPlus, ACTION_RegisterComPlus},
5549 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5550 { szRegisterFonts, ACTION_RegisterFonts },
5551 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5552 { szRegisterProduct, ACTION_RegisterProduct },
5553 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5554 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5555 { szRegisterUser, ACTION_RegisterUser },
5556 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5557 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5558 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5559 { szRemoveFiles, ACTION_RemoveFiles },
5560 { szRemoveFolders, ACTION_RemoveFolders },
5561 { szRemoveIniValues, ACTION_RemoveIniValues },
5562 { szRemoveODBC, ACTION_RemoveODBC },
5563 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5564 { szRemoveShortcuts, ACTION_RemoveShortcuts },
5565 { szResolveSource, ACTION_ResolveSource },
5566 { szRMCCPSearch, ACTION_RMCCPSearch },
5567 { szScheduleReboot, NULL },
5568 { szSelfRegModules, ACTION_SelfRegModules },
5569 { szSelfUnregModules, ACTION_SelfUnregModules },
5570 { szSetODBCFolders, NULL },
5571 { szStartServices, ACTION_StartServices },
5572 { szStopServices, ACTION_StopServices },
5573 { szUnpublishComponents, ACTION_UnpublishComponents },
5574 { szUnpublishFeatures, ACTION_UnpublishFeatures },
5575 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5576 { szUnregisterComPlus, ACTION_UnregisterComPlus },
5577 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5578 { szUnregisterFonts, ACTION_UnregisterFonts },
5579 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5580 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5581 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5582 { szValidateProductID, ACTION_ValidateProductID },
5583 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5584 { szWriteIniValues, ACTION_WriteIniValues },
5585 { szWriteRegistryValues, ACTION_WriteRegistryValues },
5586 { NULL, NULL },