ntdll: Add some I/O completion tests.
[wine/wine64.git] / dlls / msi / action.c
blob30663085e29133b1bf644903e0d45e55385fc1a0
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 package->script->CurrentlyScripting= FALSE;
700 /* process the ending type action */
701 if (rc == ERROR_SUCCESS)
702 ACTION_PerformActionSequence(package,-1,ui);
703 else if (rc == ERROR_INSTALL_USEREXIT)
704 ACTION_PerformActionSequence(package,-2,ui);
705 else if (rc == ERROR_INSTALL_SUSPEND)
706 ACTION_PerformActionSequence(package,-4,ui);
707 else /* failed */
708 ACTION_PerformActionSequence(package,-3,ui);
710 /* finish up running custom actions */
711 ACTION_FinishCustomActions(package);
713 return rc;
716 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
718 UINT rc = ERROR_SUCCESS;
719 MSIRECORD * row = 0;
720 static const WCHAR ExecSeqQuery[] =
721 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
722 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
723 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
724 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
726 static const WCHAR UISeqQuery[] =
727 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
728 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
729 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
730 ' ', '=',' ','%','i',0};
732 if (UI)
733 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
734 else
735 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
737 if (row)
739 LPCWSTR action, cond;
741 TRACE("Running the actions\n");
743 /* check conditions */
744 cond = MSI_RecordGetString(row,2);
746 /* this is a hack to skip errors in the condition code */
747 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
748 goto end;
750 action = MSI_RecordGetString(row,1);
751 if (!action)
753 ERR("failed to fetch action\n");
754 rc = ERROR_FUNCTION_FAILED;
755 goto end;
758 if (UI)
759 rc = ACTION_PerformUIAction(package,action,-1);
760 else
761 rc = ACTION_PerformAction(package,action,-1,FALSE);
762 end:
763 msiobj_release(&row->hdr);
765 else
766 rc = ERROR_SUCCESS;
768 return rc;
771 typedef struct {
772 MSIPACKAGE* package;
773 BOOL UI;
774 } iterate_action_param;
776 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
778 iterate_action_param *iap= (iterate_action_param*)param;
779 UINT rc;
780 LPCWSTR cond, action;
782 action = MSI_RecordGetString(row,1);
783 if (!action)
785 ERR("Error is retrieving action name\n");
786 return ERROR_FUNCTION_FAILED;
789 /* check conditions */
790 cond = MSI_RecordGetString(row,2);
792 /* this is a hack to skip errors in the condition code */
793 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
795 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
796 return ERROR_SUCCESS;
799 if (iap->UI)
800 rc = ACTION_PerformUIAction(iap->package,action,-1);
801 else
802 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
804 msi_dialog_check_messages( NULL );
806 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
807 rc = iap->package->CurrentInstallState;
809 if (rc == ERROR_FUNCTION_NOT_CALLED)
810 rc = ERROR_SUCCESS;
812 if (rc != ERROR_SUCCESS)
813 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
815 return rc;
818 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
820 MSIQUERY * view;
821 UINT r;
822 static const WCHAR query[] =
823 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
824 '`','%','s','`',
825 ' ','W','H','E','R','E',' ',
826 '`','S','e','q','u','e','n','c','e','`',' ',
827 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
828 '`','S','e','q','u','e','n','c','e','`',0};
829 iterate_action_param iap;
832 * FIXME: probably should be checking UILevel in the
833 * ACTION_PerformUIAction/ACTION_PerformAction
834 * rather than saving the UI level here. Those
835 * two functions can be merged too.
837 iap.package = package;
838 iap.UI = TRUE;
840 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
842 r = MSI_OpenQuery( package->db, &view, query, szTable );
843 if (r == ERROR_SUCCESS)
845 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
846 msiobj_release(&view->hdr);
849 return r;
852 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
854 MSIQUERY * view;
855 UINT rc;
856 static const WCHAR ExecSeqQuery[] =
857 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
858 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
859 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
860 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
861 'O','R','D','E','R',' ', 'B','Y',' ',
862 '`','S','e','q','u','e','n','c','e','`',0 };
863 MSIRECORD * row = 0;
864 static const WCHAR IVQuery[] =
865 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
866 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
867 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
868 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
869 ' ','\'', 'I','n','s','t','a','l','l',
870 'V','a','l','i','d','a','t','e','\'', 0};
871 INT seq = 0;
872 iterate_action_param iap;
874 iap.package = package;
875 iap.UI = FALSE;
877 if (package->script->ExecuteSequenceRun)
879 TRACE("Execute Sequence already Run\n");
880 return ERROR_SUCCESS;
883 package->script->ExecuteSequenceRun = TRUE;
885 /* get the sequence number */
886 if (UIran)
888 row = MSI_QueryGetRecord(package->db, IVQuery);
889 if( !row )
890 return ERROR_FUNCTION_FAILED;
891 seq = MSI_RecordGetInteger(row,1);
892 msiobj_release(&row->hdr);
895 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
896 if (rc == ERROR_SUCCESS)
898 TRACE("Running the actions\n");
900 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
901 msiobj_release(&view->hdr);
904 return rc;
907 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
909 MSIQUERY * view;
910 UINT rc;
911 static const WCHAR ExecSeqQuery [] =
912 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
913 '`','I','n','s','t','a','l','l',
914 'U','I','S','e','q','u','e','n','c','e','`',
915 ' ','W','H','E','R','E',' ',
916 '`','S','e','q','u','e','n','c','e','`',' ',
917 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
918 '`','S','e','q','u','e','n','c','e','`',0};
919 iterate_action_param iap;
921 iap.package = package;
922 iap.UI = TRUE;
924 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
926 if (rc == ERROR_SUCCESS)
928 TRACE("Running the actions\n");
930 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
931 msiobj_release(&view->hdr);
934 return rc;
937 /********************************************************
938 * ACTION helper functions and functions that perform the actions
939 *******************************************************/
940 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
941 UINT* rc, BOOL force )
943 BOOL ret = FALSE;
944 BOOL run = force;
945 int i;
947 if (!run && !package->script->CurrentlyScripting)
948 run = TRUE;
950 if (!run)
952 if (strcmpW(action,szInstallFinalize) == 0 ||
953 strcmpW(action,szInstallExecute) == 0 ||
954 strcmpW(action,szInstallExecuteAgain) == 0)
955 run = TRUE;
958 i = 0;
959 while (StandardActions[i].action != NULL)
961 if (strcmpW(StandardActions[i].action, action)==0)
963 if (!run)
965 ui_actioninfo(package, action, TRUE, 0);
966 *rc = schedule_action(package,INSTALL_SCRIPT,action);
967 ui_actioninfo(package, action, FALSE, *rc);
969 else
971 ui_actionstart(package, action);
972 if (StandardActions[i].handler)
974 *rc = StandardActions[i].handler(package);
976 else
978 FIXME("unhandled standard action %s\n",debugstr_w(action));
979 *rc = ERROR_SUCCESS;
982 ret = TRUE;
983 break;
985 i++;
987 return ret;
990 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
991 UINT* rc, UINT script, BOOL force )
993 BOOL ret=FALSE;
994 UINT arc;
996 arc = ACTION_CustomAction(package, action, script, force);
998 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1000 *rc = arc;
1001 ret = TRUE;
1003 return ret;
1007 * A lot of actions are really important even if they don't do anything
1008 * explicit... Lots of properties are set at the beginning of the installation
1009 * CostFinalize does a bunch of work to translate the directories and such
1011 * But until I get write access to the database that is hard, so I am going to
1012 * hack it to see if I can get something to run.
1014 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1016 UINT rc = ERROR_SUCCESS;
1017 BOOL handled;
1019 TRACE("Performing action (%s)\n",debugstr_w(action));
1021 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1023 if (!handled)
1024 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1026 if (!handled)
1028 FIXME("unhandled msi action %s\n",debugstr_w(action));
1029 rc = ERROR_FUNCTION_NOT_CALLED;
1032 return rc;
1035 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1037 UINT rc = ERROR_SUCCESS;
1038 BOOL handled = FALSE;
1040 TRACE("Performing action (%s)\n",debugstr_w(action));
1042 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1044 if (!handled)
1045 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1047 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1048 handled = TRUE;
1050 if (!handled)
1052 FIXME("unhandled msi action %s\n",debugstr_w(action));
1053 rc = ERROR_FUNCTION_NOT_CALLED;
1056 return rc;
1061 * Actual Action Handlers
1064 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1066 MSIPACKAGE *package = (MSIPACKAGE*)param;
1067 LPCWSTR dir;
1068 LPWSTR full_path;
1069 MSIRECORD *uirow;
1070 MSIFOLDER *folder;
1072 dir = MSI_RecordGetString(row,1);
1073 if (!dir)
1075 ERR("Unable to get folder id\n");
1076 return ERROR_SUCCESS;
1079 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1080 if (!full_path)
1082 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1083 return ERROR_SUCCESS;
1086 TRACE("Folder is %s\n",debugstr_w(full_path));
1088 /* UI stuff */
1089 uirow = MSI_CreateRecord(1);
1090 MSI_RecordSetStringW(uirow,1,full_path);
1091 ui_actiondata(package,szCreateFolders,uirow);
1092 msiobj_release( &uirow->hdr );
1094 if (folder->State == 0)
1095 create_full_pathW(full_path);
1097 folder->State = 3;
1099 msi_free(full_path);
1100 return ERROR_SUCCESS;
1103 /* FIXME: probably should merge this with the above function */
1104 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1106 UINT rc = ERROR_SUCCESS;
1107 MSIFOLDER *folder;
1108 LPWSTR install_path;
1110 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1111 if (!install_path)
1112 return ERROR_FUNCTION_FAILED;
1114 /* create the path */
1115 if (folder->State == 0)
1117 create_full_pathW(install_path);
1118 folder->State = 2;
1120 msi_free(install_path);
1122 return rc;
1125 UINT msi_create_component_directories( MSIPACKAGE *package )
1127 MSICOMPONENT *comp;
1129 /* create all the folders required by the components are going to install */
1130 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1132 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1133 continue;
1134 msi_create_directory( package, comp->Directory );
1137 return ERROR_SUCCESS;
1141 * Also we cannot enable/disable components either, so for now I am just going
1142 * to do all the directories for all the components.
1144 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1146 static const WCHAR ExecSeqQuery[] =
1147 {'S','E','L','E','C','T',' ',
1148 '`','D','i','r','e','c','t','o','r','y','_','`',
1149 ' ','F','R','O','M',' ',
1150 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1151 UINT rc;
1152 MSIQUERY *view;
1154 /* create all the empty folders specified in the CreateFolder table */
1155 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1156 if (rc != ERROR_SUCCESS)
1157 return ERROR_SUCCESS;
1159 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1160 msiobj_release(&view->hdr);
1162 msi_create_component_directories( package );
1164 return rc;
1167 static UINT load_component( MSIRECORD *row, LPVOID param )
1169 MSIPACKAGE *package = param;
1170 MSICOMPONENT *comp;
1172 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1173 if (!comp)
1174 return ERROR_FUNCTION_FAILED;
1176 list_add_tail( &package->components, &comp->entry );
1178 /* fill in the data */
1179 comp->Component = msi_dup_record_field( row, 1 );
1181 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1183 comp->ComponentId = msi_dup_record_field( row, 2 );
1184 comp->Directory = msi_dup_record_field( row, 3 );
1185 comp->Attributes = MSI_RecordGetInteger(row,4);
1186 comp->Condition = msi_dup_record_field( row, 5 );
1187 comp->KeyPath = msi_dup_record_field( row, 6 );
1189 comp->Installed = INSTALLSTATE_UNKNOWN;
1190 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1192 return ERROR_SUCCESS;
1195 static UINT load_all_components( MSIPACKAGE *package )
1197 static const WCHAR query[] = {
1198 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1199 '`','C','o','m','p','o','n','e','n','t','`',0 };
1200 MSIQUERY *view;
1201 UINT r;
1203 if (!list_empty(&package->components))
1204 return ERROR_SUCCESS;
1206 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1207 if (r != ERROR_SUCCESS)
1208 return r;
1210 r = MSI_IterateRecords(view, NULL, load_component, package);
1211 msiobj_release(&view->hdr);
1212 return r;
1215 typedef struct {
1216 MSIPACKAGE *package;
1217 MSIFEATURE *feature;
1218 } _ilfs;
1220 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1222 ComponentList *cl;
1224 cl = msi_alloc( sizeof (*cl) );
1225 if ( !cl )
1226 return ERROR_NOT_ENOUGH_MEMORY;
1227 cl->component = comp;
1228 list_add_tail( &feature->Components, &cl->entry );
1230 return ERROR_SUCCESS;
1233 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1235 FeatureList *fl;
1237 fl = msi_alloc( sizeof(*fl) );
1238 if ( !fl )
1239 return ERROR_NOT_ENOUGH_MEMORY;
1240 fl->feature = child;
1241 list_add_tail( &parent->Children, &fl->entry );
1243 return ERROR_SUCCESS;
1246 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1248 _ilfs* ilfs= (_ilfs*)param;
1249 LPCWSTR component;
1250 MSICOMPONENT *comp;
1252 component = MSI_RecordGetString(row,1);
1254 /* check to see if the component is already loaded */
1255 comp = get_loaded_component( ilfs->package, component );
1256 if (!comp)
1258 ERR("unknown component %s\n", debugstr_w(component));
1259 return ERROR_FUNCTION_FAILED;
1262 add_feature_component( ilfs->feature, comp );
1263 comp->Enabled = TRUE;
1265 return ERROR_SUCCESS;
1268 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1270 MSIFEATURE *feature;
1272 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1274 if ( !lstrcmpW( feature->Feature, name ) )
1275 return feature;
1278 return NULL;
1281 static UINT load_feature(MSIRECORD * row, LPVOID param)
1283 MSIPACKAGE* package = (MSIPACKAGE*)param;
1284 MSIFEATURE* feature;
1285 static const WCHAR Query1[] =
1286 {'S','E','L','E','C','T',' ',
1287 '`','C','o','m','p','o','n','e','n','t','_','`',
1288 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1289 'C','o','m','p','o','n','e','n','t','s','`',' ',
1290 'W','H','E','R','E',' ',
1291 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1292 MSIQUERY * view;
1293 UINT rc;
1294 _ilfs ilfs;
1296 /* fill in the data */
1298 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1299 if (!feature)
1300 return ERROR_NOT_ENOUGH_MEMORY;
1302 list_init( &feature->Children );
1303 list_init( &feature->Components );
1305 feature->Feature = msi_dup_record_field( row, 1 );
1307 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1309 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1310 feature->Title = msi_dup_record_field( row, 3 );
1311 feature->Description = msi_dup_record_field( row, 4 );
1313 if (!MSI_RecordIsNull(row,5))
1314 feature->Display = MSI_RecordGetInteger(row,5);
1316 feature->Level= MSI_RecordGetInteger(row,6);
1317 feature->Directory = msi_dup_record_field( row, 7 );
1318 feature->Attributes = MSI_RecordGetInteger(row,8);
1320 feature->Installed = INSTALLSTATE_UNKNOWN;
1321 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1323 list_add_tail( &package->features, &feature->entry );
1325 /* load feature components */
1327 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1328 if (rc != ERROR_SUCCESS)
1329 return ERROR_SUCCESS;
1331 ilfs.package = package;
1332 ilfs.feature = feature;
1334 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1335 msiobj_release(&view->hdr);
1337 return ERROR_SUCCESS;
1340 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1342 MSIPACKAGE* package = (MSIPACKAGE*)param;
1343 MSIFEATURE *parent, *child;
1345 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1346 if (!child)
1347 return ERROR_FUNCTION_FAILED;
1349 if (!child->Feature_Parent)
1350 return ERROR_SUCCESS;
1352 parent = find_feature_by_name( package, child->Feature_Parent );
1353 if (!parent)
1354 return ERROR_FUNCTION_FAILED;
1356 add_feature_child( parent, child );
1357 return ERROR_SUCCESS;
1360 static UINT load_all_features( MSIPACKAGE *package )
1362 static const WCHAR query[] = {
1363 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1364 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1365 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1366 MSIQUERY *view;
1367 UINT r;
1369 if (!list_empty(&package->features))
1370 return ERROR_SUCCESS;
1372 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1373 if (r != ERROR_SUCCESS)
1374 return r;
1376 r = MSI_IterateRecords( view, NULL, load_feature, package );
1377 if (r != ERROR_SUCCESS)
1378 return r;
1380 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1381 msiobj_release( &view->hdr );
1383 return r;
1386 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1388 if (!p)
1389 return p;
1390 p = strchrW(p, ch);
1391 if (!p)
1392 return p;
1393 *p = 0;
1394 return p+1;
1397 static UINT load_file(MSIRECORD *row, LPVOID param)
1399 MSIPACKAGE* package = (MSIPACKAGE*)param;
1400 LPCWSTR component;
1401 MSIFILE *file;
1403 /* fill in the data */
1405 file = msi_alloc_zero( sizeof (MSIFILE) );
1406 if (!file)
1407 return ERROR_NOT_ENOUGH_MEMORY;
1409 file->File = msi_dup_record_field( row, 1 );
1411 component = MSI_RecordGetString( row, 2 );
1412 file->Component = get_loaded_component( package, component );
1414 if (!file->Component)
1415 ERR("Unfound Component %s\n",debugstr_w(component));
1417 file->FileName = msi_dup_record_field( row, 3 );
1418 reduce_to_longfilename( file->FileName );
1420 file->ShortName = msi_dup_record_field( row, 3 );
1421 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1423 file->FileSize = MSI_RecordGetInteger( row, 4 );
1424 file->Version = msi_dup_record_field( row, 5 );
1425 file->Language = msi_dup_record_field( row, 6 );
1426 file->Attributes = MSI_RecordGetInteger( row, 7 );
1427 file->Sequence = MSI_RecordGetInteger( row, 8 );
1429 file->state = msifs_invalid;
1431 /* if the compressed bits are not set in the file attributes,
1432 * then read the information from the package word count property
1434 if (file->Attributes & msidbFileAttributesCompressed)
1436 file->IsCompressed = TRUE;
1438 else if (file->Attributes & msidbFileAttributesNoncompressed)
1440 file->IsCompressed = FALSE;
1442 else
1444 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1447 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1449 list_add_tail( &package->files, &file->entry );
1451 return ERROR_SUCCESS;
1454 static UINT load_all_files(MSIPACKAGE *package)
1456 MSIQUERY * view;
1457 UINT rc;
1458 static const WCHAR Query[] =
1459 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1460 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1461 '`','S','e','q','u','e','n','c','e','`', 0};
1463 if (!list_empty(&package->files))
1464 return ERROR_SUCCESS;
1466 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1467 if (rc != ERROR_SUCCESS)
1468 return ERROR_SUCCESS;
1470 rc = MSI_IterateRecords(view, NULL, load_file, package);
1471 msiobj_release(&view->hdr);
1473 return ERROR_SUCCESS;
1476 static UINT load_folder( MSIRECORD *row, LPVOID param )
1478 MSIPACKAGE *package = param;
1479 static const WCHAR szDot[] = { '.',0 };
1480 static WCHAR szEmpty[] = { 0 };
1481 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1482 MSIFOLDER *folder;
1484 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1485 if (!folder)
1486 return ERROR_NOT_ENOUGH_MEMORY;
1488 folder->Directory = msi_dup_record_field( row, 1 );
1490 TRACE("%s\n", debugstr_w(folder->Directory));
1492 p = msi_dup_record_field(row, 3);
1494 /* split src and target dir */
1495 tgt_short = p;
1496 src_short = folder_split_path( p, ':' );
1498 /* split the long and short paths */
1499 tgt_long = folder_split_path( tgt_short, '|' );
1500 src_long = folder_split_path( src_short, '|' );
1502 /* check for no-op dirs */
1503 if (!lstrcmpW(szDot, tgt_short))
1504 tgt_short = szEmpty;
1505 if (!lstrcmpW(szDot, src_short))
1506 src_short = szEmpty;
1508 if (!tgt_long)
1509 tgt_long = tgt_short;
1511 if (!src_short) {
1512 src_short = tgt_short;
1513 src_long = tgt_long;
1516 if (!src_long)
1517 src_long = src_short;
1519 /* FIXME: use the target short path too */
1520 folder->TargetDefault = strdupW(tgt_long);
1521 folder->SourceShortPath = strdupW(src_short);
1522 folder->SourceLongPath = strdupW(src_long);
1523 msi_free(p);
1525 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1526 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1527 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1529 folder->Parent = msi_dup_record_field( row, 2 );
1531 folder->Property = msi_dup_property( package, folder->Directory );
1533 list_add_tail( &package->folders, &folder->entry );
1535 TRACE("returning %p\n", folder);
1537 return ERROR_SUCCESS;
1540 static UINT load_all_folders( MSIPACKAGE *package )
1542 static const WCHAR query[] = {
1543 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1544 '`','D','i','r','e','c','t','o','r','y','`',0 };
1545 MSIQUERY *view;
1546 UINT r;
1548 if (!list_empty(&package->folders))
1549 return ERROR_SUCCESS;
1551 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1552 if (r != ERROR_SUCCESS)
1553 return r;
1555 r = MSI_IterateRecords(view, NULL, load_folder, package);
1556 msiobj_release(&view->hdr);
1557 return r;
1561 * I am not doing any of the costing functionality yet.
1562 * Mostly looking at doing the Component and Feature loading
1564 * The native MSI does A LOT of modification to tables here. Mostly adding
1565 * a lot of temporary columns to the Feature and Component tables.
1567 * note: Native msi also tracks the short filename. But I am only going to
1568 * track the long ones. Also looking at this directory table
1569 * it appears that the directory table does not get the parents
1570 * resolved base on property only based on their entries in the
1571 * directory table.
1573 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1575 static const WCHAR szCosting[] =
1576 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1577 static const WCHAR szZero[] = { '0', 0 };
1579 MSI_SetPropertyW(package, szCosting, szZero);
1580 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1582 load_all_components( package );
1583 load_all_features( package );
1584 load_all_files( package );
1585 load_all_folders( package );
1587 return ERROR_SUCCESS;
1590 static UINT execute_script(MSIPACKAGE *package, UINT script )
1592 UINT i;
1593 UINT rc = ERROR_SUCCESS;
1595 TRACE("Executing Script %i\n",script);
1597 if (!package->script)
1599 ERR("no script!\n");
1600 return ERROR_FUNCTION_FAILED;
1603 for (i = 0; i < package->script->ActionCount[script]; i++)
1605 LPWSTR action;
1606 action = package->script->Actions[script][i];
1607 ui_actionstart(package, action);
1608 TRACE("Executing Action (%s)\n",debugstr_w(action));
1609 rc = ACTION_PerformAction(package, action, script, TRUE);
1610 if (rc != ERROR_SUCCESS)
1611 break;
1613 msi_free_action_script(package, script);
1614 return rc;
1617 static UINT ACTION_FileCost(MSIPACKAGE *package)
1619 return ERROR_SUCCESS;
1622 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1624 MSICOMPONENT *comp;
1626 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1628 INSTALLSTATE res;
1630 if (!comp->ComponentId)
1631 continue;
1633 res = MsiGetComponentPathW( package->ProductCode,
1634 comp->ComponentId, NULL, NULL);
1635 if (res < 0)
1636 res = INSTALLSTATE_ABSENT;
1637 comp->Installed = res;
1641 /* scan for and update current install states */
1642 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1644 MSICOMPONENT *comp;
1645 MSIFEATURE *feature;
1647 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1649 ComponentList *cl;
1650 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1652 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1654 comp= cl->component;
1656 if (!comp->ComponentId)
1658 res = INSTALLSTATE_ABSENT;
1659 break;
1662 if (res == INSTALLSTATE_ABSENT)
1663 res = comp->Installed;
1664 else
1666 if (res == comp->Installed)
1667 continue;
1669 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1670 res != INSTALLSTATE_SOURCE)
1672 res = INSTALLSTATE_INCOMPLETE;
1676 feature->Installed = res;
1680 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1681 INSTALLSTATE state)
1683 static const WCHAR all[]={'A','L','L',0};
1684 LPWSTR override;
1685 MSIFEATURE *feature;
1687 override = msi_dup_property( package, property );
1688 if (!override)
1689 return FALSE;
1691 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1693 if (strcmpiW(override,all)==0)
1694 msi_feature_set_state( feature, state );
1695 else
1697 LPWSTR ptr = override;
1698 LPWSTR ptr2 = strchrW(override,',');
1700 while (ptr)
1702 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1703 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1705 msi_feature_set_state( feature, state );
1706 break;
1708 if (ptr2)
1710 ptr=ptr2+1;
1711 ptr2 = strchrW(ptr,',');
1713 else
1714 break;
1718 msi_free(override);
1720 return TRUE;
1723 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1725 int install_level;
1726 static const WCHAR szlevel[] =
1727 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1728 static const WCHAR szAddLocal[] =
1729 {'A','D','D','L','O','C','A','L',0};
1730 static const WCHAR szAddSource[] =
1731 {'A','D','D','S','O','U','R','C','E',0};
1732 static const WCHAR szRemove[] =
1733 {'R','E','M','O','V','E',0};
1734 static const WCHAR szReinstall[] =
1735 {'R','E','I','N','S','T','A','L','L',0};
1736 BOOL override = FALSE;
1737 MSICOMPONENT* component;
1738 MSIFEATURE *feature;
1741 /* I do not know if this is where it should happen.. but */
1743 TRACE("Checking Install Level\n");
1745 install_level = msi_get_property_int( package, szlevel, 1 );
1747 /* ok here is the _real_ rub
1748 * all these activation/deactivation things happen in order and things
1749 * later on the list override things earlier on the list.
1750 * 1) INSTALLLEVEL processing
1751 * 2) ADDLOCAL
1752 * 3) REMOVE
1753 * 4) ADDSOURCE
1754 * 5) ADDDEFAULT
1755 * 6) REINSTALL
1756 * 7) COMPADDLOCAL
1757 * 8) COMPADDSOURCE
1758 * 9) FILEADDLOCAL
1759 * 10) FILEADDSOURCE
1760 * 11) FILEADDDEFAULT
1761 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1762 * ignored for all the features. seems strange, especially since it is not
1763 * documented anywhere, but it is how it works.
1765 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1766 * REMOVE are the big ones, since we don't handle administrative installs
1767 * yet anyway.
1769 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1770 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1771 override |= process_state_property(package,szAddSource,INSTALLSTATE_SOURCE);
1772 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1774 if (!override)
1776 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1778 BOOL feature_state = ((feature->Level > 0) &&
1779 (feature->Level <= install_level));
1781 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1783 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1784 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1785 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1786 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1787 else
1788 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1792 /* disable child features of unselected parent features */
1793 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1795 FeatureList *fl;
1797 if (feature->Level > 0 && feature->Level <= install_level)
1798 continue;
1800 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1801 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1804 else
1806 /* set the Preselected Property */
1807 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1808 static const WCHAR szOne[] = { '1', 0 };
1810 MSI_SetPropertyW(package,szPreselected,szOne);
1814 * now we want to enable or disable components base on feature
1817 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1819 ComponentList *cl;
1821 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1822 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1824 /* features with components that have compressed files are made local */
1825 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1827 if (cl->component->Enabled &&
1828 cl->component->ForceLocalState &&
1829 feature->Action == INSTALLSTATE_SOURCE)
1831 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1832 break;
1836 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1838 component = cl->component;
1840 if (!component->Enabled)
1841 continue;
1843 switch (feature->Action)
1845 case INSTALLSTATE_ABSENT:
1846 component->anyAbsent = 1;
1847 break;
1848 case INSTALLSTATE_ADVERTISED:
1849 component->hasAdvertiseFeature = 1;
1850 break;
1851 case INSTALLSTATE_SOURCE:
1852 component->hasSourceFeature = 1;
1853 break;
1854 case INSTALLSTATE_LOCAL:
1855 component->hasLocalFeature = 1;
1856 break;
1857 case INSTALLSTATE_DEFAULT:
1858 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1859 component->hasAdvertiseFeature = 1;
1860 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1861 component->hasSourceFeature = 1;
1862 else
1863 component->hasLocalFeature = 1;
1864 break;
1865 default:
1866 break;
1871 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1873 /* if the component isn't enabled, leave it alone */
1874 if (!component->Enabled)
1875 continue;
1877 /* check if it's local or source */
1878 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1879 (component->hasLocalFeature || component->hasSourceFeature))
1881 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1882 !component->ForceLocalState)
1883 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1884 else
1885 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1886 continue;
1889 /* if any feature is local, the component must be local too */
1890 if (component->hasLocalFeature)
1892 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1893 continue;
1896 if (component->hasSourceFeature)
1898 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1899 continue;
1902 if (component->hasAdvertiseFeature)
1904 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1905 continue;
1908 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1909 if (component->anyAbsent)
1910 msi_component_set_state(component, INSTALLSTATE_ABSENT);
1913 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1915 if (component->Action == INSTALLSTATE_DEFAULT)
1917 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1918 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1921 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1922 debugstr_w(component->Component), component->Installed, component->Action);
1926 return ERROR_SUCCESS;
1929 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1931 MSIPACKAGE *package = (MSIPACKAGE*)param;
1932 LPCWSTR name;
1933 LPWSTR path;
1934 MSIFOLDER *f;
1936 name = MSI_RecordGetString(row,1);
1938 f = get_loaded_folder(package, name);
1939 if (!f) return ERROR_SUCCESS;
1941 /* reset the ResolvedTarget */
1942 msi_free(f->ResolvedTarget);
1943 f->ResolvedTarget = NULL;
1945 /* This helper function now does ALL the work */
1946 TRACE("Dir %s ...\n",debugstr_w(name));
1947 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1948 TRACE("resolves to %s\n",debugstr_w(path));
1949 msi_free(path);
1951 return ERROR_SUCCESS;
1954 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1956 MSIPACKAGE *package = (MSIPACKAGE*)param;
1957 LPCWSTR name;
1958 MSIFEATURE *feature;
1960 name = MSI_RecordGetString( row, 1 );
1962 feature = get_loaded_feature( package, name );
1963 if (!feature)
1964 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1965 else
1967 LPCWSTR Condition;
1968 Condition = MSI_RecordGetString(row,3);
1970 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1972 int level = MSI_RecordGetInteger(row,2);
1973 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
1974 feature->Level = level;
1977 return ERROR_SUCCESS;
1980 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1982 static const WCHAR name_fmt[] =
1983 {'%','u','.','%','u','.','%','u','.','%','u',0};
1984 static WCHAR name[] = {'\\',0};
1985 VS_FIXEDFILEINFO *lpVer;
1986 WCHAR filever[0x100];
1987 LPVOID version;
1988 DWORD versize;
1989 DWORD handle;
1990 UINT sz;
1992 TRACE("%s\n", debugstr_w(filename));
1994 versize = GetFileVersionInfoSizeW( filename, &handle );
1995 if (!versize)
1996 return NULL;
1998 version = msi_alloc( versize );
1999 GetFileVersionInfoW( filename, 0, versize, version );
2001 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2003 msi_free( version );
2004 return NULL;
2007 sprintfW( filever, name_fmt,
2008 HIWORD(lpVer->dwFileVersionMS),
2009 LOWORD(lpVer->dwFileVersionMS),
2010 HIWORD(lpVer->dwFileVersionLS),
2011 LOWORD(lpVer->dwFileVersionLS));
2013 msi_free( version );
2015 return strdupW( filever );
2018 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2020 LPWSTR file_version;
2021 MSIFILE *file;
2023 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2025 MSICOMPONENT* comp = file->Component;
2026 LPWSTR p;
2028 if (!comp)
2029 continue;
2031 if (file->IsCompressed)
2032 comp->ForceLocalState = TRUE;
2034 /* calculate target */
2035 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2037 msi_free(file->TargetPath);
2039 TRACE("file %s is named %s\n",
2040 debugstr_w(file->File), debugstr_w(file->FileName));
2042 file->TargetPath = build_directory_name(2, p, file->FileName);
2044 msi_free(p);
2046 TRACE("file %s resolves to %s\n",
2047 debugstr_w(file->File), debugstr_w(file->TargetPath));
2049 /* don't check files of components that aren't installed */
2050 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2051 comp->Installed == INSTALLSTATE_ABSENT)
2053 file->state = msifs_missing; /* assume files are missing */
2054 continue;
2057 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2059 file->state = msifs_missing;
2060 comp->Cost += file->FileSize;
2061 comp->Installed = INSTALLSTATE_INCOMPLETE;
2062 continue;
2065 if (file->Version &&
2066 (file_version = msi_get_disk_file_version( file->TargetPath )))
2068 TRACE("new %s old %s\n", debugstr_w(file->Version),
2069 debugstr_w(file_version));
2070 /* FIXME: seems like a bad way to compare version numbers */
2071 if (lstrcmpiW(file_version, file->Version)<0)
2073 file->state = msifs_overwrite;
2074 comp->Cost += file->FileSize;
2075 comp->Installed = INSTALLSTATE_INCOMPLETE;
2077 else
2078 file->state = msifs_present;
2079 msi_free( file_version );
2081 else
2082 file->state = msifs_present;
2085 return ERROR_SUCCESS;
2089 * A lot is done in this function aside from just the costing.
2090 * The costing needs to be implemented at some point but for now I am going
2091 * to focus on the directory building
2094 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2096 static const WCHAR ExecSeqQuery[] =
2097 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2098 '`','D','i','r','e','c','t','o','r','y','`',0};
2099 static const WCHAR ConditionQuery[] =
2100 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2101 '`','C','o','n','d','i','t','i','o','n','`',0};
2102 static const WCHAR szCosting[] =
2103 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2104 static const WCHAR szlevel[] =
2105 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2106 static const WCHAR szOne[] = { '1', 0 };
2107 MSICOMPONENT *comp;
2108 UINT rc;
2109 MSIQUERY * view;
2110 LPWSTR level;
2112 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2113 return ERROR_SUCCESS;
2115 TRACE("Building Directory properties\n");
2117 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2118 if (rc == ERROR_SUCCESS)
2120 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2121 package);
2122 msiobj_release(&view->hdr);
2125 /* read components states from the registry */
2126 ACTION_GetComponentInstallStates(package);
2128 TRACE("File calculations\n");
2129 msi_check_file_install_states( package );
2131 TRACE("Evaluating Condition Table\n");
2133 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2134 if (rc == ERROR_SUCCESS)
2136 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2137 package);
2138 msiobj_release(&view->hdr);
2141 TRACE("Enabling or Disabling Components\n");
2142 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2144 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2146 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2147 comp->Enabled = FALSE;
2151 MSI_SetPropertyW(package,szCosting,szOne);
2152 /* set default run level if not set */
2153 level = msi_dup_property( package, szlevel );
2154 if (!level)
2155 MSI_SetPropertyW(package,szlevel, szOne);
2156 msi_free(level);
2158 ACTION_UpdateFeatureInstallStates(package);
2160 return MSI_SetFeatureStates(package);
2163 /* OK this value is "interpreted" and then formatted based on the
2164 first few characters */
2165 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2166 DWORD *size)
2168 LPSTR data = NULL;
2169 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2171 if (value[1]=='x')
2173 LPWSTR ptr;
2174 CHAR byte[5];
2175 LPWSTR deformated = NULL;
2176 int count;
2178 deformat_string(package, &value[2], &deformated);
2180 /* binary value type */
2181 ptr = deformated;
2182 *type = REG_BINARY;
2183 if (strlenW(ptr)%2)
2184 *size = (strlenW(ptr)/2)+1;
2185 else
2186 *size = strlenW(ptr)/2;
2188 data = msi_alloc(*size);
2190 byte[0] = '0';
2191 byte[1] = 'x';
2192 byte[4] = 0;
2193 count = 0;
2194 /* if uneven pad with a zero in front */
2195 if (strlenW(ptr)%2)
2197 byte[2]= '0';
2198 byte[3]= *ptr;
2199 ptr++;
2200 data[count] = (BYTE)strtol(byte,NULL,0);
2201 count ++;
2202 TRACE("Uneven byte count\n");
2204 while (*ptr)
2206 byte[2]= *ptr;
2207 ptr++;
2208 byte[3]= *ptr;
2209 ptr++;
2210 data[count] = (BYTE)strtol(byte,NULL,0);
2211 count ++;
2213 msi_free(deformated);
2215 TRACE("Data %i bytes(%i)\n",*size,count);
2217 else
2219 LPWSTR deformated;
2220 LPWSTR p;
2221 DWORD d = 0;
2222 deformat_string(package, &value[1], &deformated);
2224 *type=REG_DWORD;
2225 *size = sizeof(DWORD);
2226 data = msi_alloc(*size);
2227 p = deformated;
2228 if (*p == '-')
2229 p++;
2230 while (*p)
2232 if ( (*p < '0') || (*p > '9') )
2233 break;
2234 d *= 10;
2235 d += (*p - '0');
2236 p++;
2238 if (deformated[0] == '-')
2239 d = -d;
2240 *(LPDWORD)data = d;
2241 TRACE("DWORD %i\n",*(LPDWORD)data);
2243 msi_free(deformated);
2246 else
2248 static const WCHAR szMulti[] = {'[','~',']',0};
2249 LPCWSTR ptr;
2250 *type=REG_SZ;
2252 if (value[0]=='#')
2254 if (value[1]=='%')
2256 ptr = &value[2];
2257 *type=REG_EXPAND_SZ;
2259 else
2260 ptr = &value[1];
2262 else
2263 ptr=value;
2265 if (strstrW(value,szMulti))
2266 *type = REG_MULTI_SZ;
2268 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2270 return data;
2273 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2275 MSIPACKAGE *package = (MSIPACKAGE*)param;
2276 static const WCHAR szHCR[] =
2277 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2278 'R','O','O','T','\\',0};
2279 static const WCHAR szHCU[] =
2280 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2281 'U','S','E','R','\\',0};
2282 static const WCHAR szHLM[] =
2283 {'H','K','E','Y','_','L','O','C','A','L','_',
2284 'M','A','C','H','I','N','E','\\',0};
2285 static const WCHAR szHU[] =
2286 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2288 LPSTR value_data = NULL;
2289 HKEY root_key, hkey;
2290 DWORD type,size;
2291 LPWSTR deformated;
2292 LPCWSTR szRoot, component, name, key, value;
2293 MSICOMPONENT *comp;
2294 MSIRECORD * uirow;
2295 LPWSTR uikey;
2296 INT root;
2297 BOOL check_first = FALSE;
2298 UINT rc;
2300 ui_progress(package,2,0,0,0);
2302 value = NULL;
2303 key = NULL;
2304 uikey = NULL;
2305 name = NULL;
2307 component = MSI_RecordGetString(row, 6);
2308 comp = get_loaded_component(package,component);
2309 if (!comp)
2310 return ERROR_SUCCESS;
2312 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2314 TRACE("Skipping write due to disabled component %s\n",
2315 debugstr_w(component));
2317 comp->Action = comp->Installed;
2319 return ERROR_SUCCESS;
2322 comp->Action = INSTALLSTATE_LOCAL;
2324 name = MSI_RecordGetString(row, 4);
2325 if( MSI_RecordIsNull(row,5) && name )
2327 /* null values can have special meanings */
2328 if (name[0]=='-' && name[1] == 0)
2329 return ERROR_SUCCESS;
2330 else if ((name[0]=='+' && name[1] == 0) ||
2331 (name[0] == '*' && name[1] == 0))
2332 name = NULL;
2333 check_first = TRUE;
2336 root = MSI_RecordGetInteger(row,2);
2337 key = MSI_RecordGetString(row, 3);
2339 /* get the root key */
2340 switch (root)
2342 case -1:
2344 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2345 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2346 if (all_users && all_users[0] == '1')
2348 root_key = HKEY_LOCAL_MACHINE;
2349 szRoot = szHLM;
2351 else
2353 root_key = HKEY_CURRENT_USER;
2354 szRoot = szHCU;
2356 msi_free(all_users);
2358 break;
2359 case 0: root_key = HKEY_CLASSES_ROOT;
2360 szRoot = szHCR;
2361 break;
2362 case 1: root_key = HKEY_CURRENT_USER;
2363 szRoot = szHCU;
2364 break;
2365 case 2: root_key = HKEY_LOCAL_MACHINE;
2366 szRoot = szHLM;
2367 break;
2368 case 3: root_key = HKEY_USERS;
2369 szRoot = szHU;
2370 break;
2371 default:
2372 ERR("Unknown root %i\n",root);
2373 root_key=NULL;
2374 szRoot = NULL;
2375 break;
2377 if (!root_key)
2378 return ERROR_SUCCESS;
2380 deformat_string(package, key , &deformated);
2381 size = strlenW(deformated) + strlenW(szRoot) + 1;
2382 uikey = msi_alloc(size*sizeof(WCHAR));
2383 strcpyW(uikey,szRoot);
2384 strcatW(uikey,deformated);
2386 if (RegCreateKeyW( root_key, deformated, &hkey))
2388 ERR("Could not create key %s\n",debugstr_w(deformated));
2389 msi_free(deformated);
2390 msi_free(uikey);
2391 return ERROR_SUCCESS;
2393 msi_free(deformated);
2395 value = MSI_RecordGetString(row,5);
2396 if (value)
2397 value_data = parse_value(package, value, &type, &size);
2398 else
2400 static const WCHAR szEmpty[] = {0};
2401 value_data = (LPSTR)strdupW(szEmpty);
2402 size = 0;
2403 type = REG_SZ;
2406 deformat_string(package, name, &deformated);
2408 /* get the double nulls to terminate SZ_MULTI */
2409 if (type == REG_MULTI_SZ)
2410 size +=sizeof(WCHAR);
2412 if (!check_first)
2414 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2415 debugstr_w(uikey));
2416 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2418 else
2420 DWORD sz = 0;
2421 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2422 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2424 TRACE("value %s of %s checked already exists\n",
2425 debugstr_w(deformated), debugstr_w(uikey));
2427 else
2429 TRACE("Checked and setting value %s of %s\n",
2430 debugstr_w(deformated), debugstr_w(uikey));
2431 if (deformated || size)
2432 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2435 RegCloseKey(hkey);
2437 uirow = MSI_CreateRecord(3);
2438 MSI_RecordSetStringW(uirow,2,deformated);
2439 MSI_RecordSetStringW(uirow,1,uikey);
2441 if (type == REG_SZ)
2442 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2443 else
2444 MSI_RecordSetStringW(uirow,3,value);
2446 ui_actiondata(package,szWriteRegistryValues,uirow);
2447 msiobj_release( &uirow->hdr );
2449 msi_free(value_data);
2450 msi_free(deformated);
2451 msi_free(uikey);
2453 return ERROR_SUCCESS;
2456 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2458 UINT rc;
2459 MSIQUERY * view;
2460 static const WCHAR ExecSeqQuery[] =
2461 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2462 '`','R','e','g','i','s','t','r','y','`',0 };
2464 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2465 if (rc != ERROR_SUCCESS)
2466 return ERROR_SUCCESS;
2468 /* increment progress bar each time action data is sent */
2469 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2471 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2473 msiobj_release(&view->hdr);
2474 return rc;
2477 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2479 package->script->CurrentlyScripting = TRUE;
2481 return ERROR_SUCCESS;
2485 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2487 MSICOMPONENT *comp;
2488 DWORD progress = 0;
2489 DWORD total = 0;
2490 static const WCHAR q1[]=
2491 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2492 '`','R','e','g','i','s','t','r','y','`',0};
2493 UINT rc;
2494 MSIQUERY * view;
2495 MSIFEATURE *feature;
2496 MSIFILE *file;
2498 TRACE("InstallValidate\n");
2500 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2501 if (rc == ERROR_SUCCESS)
2503 MSI_IterateRecords( view, &progress, NULL, package );
2504 msiobj_release( &view->hdr );
2505 total += progress * REG_PROGRESS_VALUE;
2508 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2509 total += COMPONENT_PROGRESS_VALUE;
2511 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2512 total += file->FileSize;
2514 ui_progress(package,0,total,0,0);
2516 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2518 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2519 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2520 feature->ActionRequest);
2523 return ERROR_SUCCESS;
2526 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2528 MSIPACKAGE* package = (MSIPACKAGE*)param;
2529 LPCWSTR cond = NULL;
2530 LPCWSTR message = NULL;
2531 UINT r;
2533 static const WCHAR title[]=
2534 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2536 cond = MSI_RecordGetString(row,1);
2538 r = MSI_EvaluateConditionW(package,cond);
2539 if (r == MSICONDITION_FALSE)
2541 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2543 LPWSTR deformated;
2544 message = MSI_RecordGetString(row,2);
2545 deformat_string(package,message,&deformated);
2546 MessageBoxW(NULL,deformated,title,MB_OK);
2547 msi_free(deformated);
2550 return ERROR_INSTALL_FAILURE;
2553 return ERROR_SUCCESS;
2556 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2558 UINT rc;
2559 MSIQUERY * view = NULL;
2560 static const WCHAR ExecSeqQuery[] =
2561 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2562 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2564 TRACE("Checking launch conditions\n");
2566 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2567 if (rc != ERROR_SUCCESS)
2568 return ERROR_SUCCESS;
2570 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2571 msiobj_release(&view->hdr);
2573 return rc;
2576 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2579 if (!cmp->KeyPath)
2580 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2582 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2584 MSIRECORD * row = 0;
2585 UINT root,len;
2586 LPWSTR deformated,buffer,deformated_name;
2587 LPCWSTR key,name;
2588 static const WCHAR ExecSeqQuery[] =
2589 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2590 '`','R','e','g','i','s','t','r','y','`',' ',
2591 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2592 ' ','=',' ' ,'\'','%','s','\'',0 };
2593 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2594 static const WCHAR fmt2[]=
2595 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2597 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2598 if (!row)
2599 return NULL;
2601 root = MSI_RecordGetInteger(row,2);
2602 key = MSI_RecordGetString(row, 3);
2603 name = MSI_RecordGetString(row, 4);
2604 deformat_string(package, key , &deformated);
2605 deformat_string(package, name, &deformated_name);
2607 len = strlenW(deformated) + 6;
2608 if (deformated_name)
2609 len+=strlenW(deformated_name);
2611 buffer = msi_alloc( len *sizeof(WCHAR));
2613 if (deformated_name)
2614 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2615 else
2616 sprintfW(buffer,fmt,root,deformated);
2618 msi_free(deformated);
2619 msi_free(deformated_name);
2620 msiobj_release(&row->hdr);
2622 return buffer;
2624 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2626 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2627 return NULL;
2629 else
2631 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2633 if (file)
2634 return strdupW( file->TargetPath );
2636 return NULL;
2639 static HKEY openSharedDLLsKey(void)
2641 HKEY hkey=0;
2642 static const WCHAR path[] =
2643 {'S','o','f','t','w','a','r','e','\\',
2644 'M','i','c','r','o','s','o','f','t','\\',
2645 'W','i','n','d','o','w','s','\\',
2646 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2647 'S','h','a','r','e','d','D','L','L','s',0};
2649 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2650 return hkey;
2653 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2655 HKEY hkey;
2656 DWORD count=0;
2657 DWORD type;
2658 DWORD sz = sizeof(count);
2659 DWORD rc;
2661 hkey = openSharedDLLsKey();
2662 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2663 if (rc != ERROR_SUCCESS)
2664 count = 0;
2665 RegCloseKey(hkey);
2666 return count;
2669 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2671 HKEY hkey;
2673 hkey = openSharedDLLsKey();
2674 if (count > 0)
2675 msi_reg_set_val_dword( hkey, path, count );
2676 else
2677 RegDeleteValueW(hkey,path);
2678 RegCloseKey(hkey);
2679 return count;
2683 * Return TRUE if the count should be written out and FALSE if not
2685 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2687 MSIFEATURE *feature;
2688 INT count = 0;
2689 BOOL write = FALSE;
2691 /* only refcount DLLs */
2692 if (comp->KeyPath == NULL ||
2693 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2694 comp->Attributes & msidbComponentAttributesODBCDataSource)
2695 write = FALSE;
2696 else
2698 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2699 write = (count > 0);
2701 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2702 write = TRUE;
2705 /* increment counts */
2706 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2708 ComponentList *cl;
2710 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2711 continue;
2713 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2715 if ( cl->component == comp )
2716 count++;
2720 /* decrement counts */
2721 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2723 ComponentList *cl;
2725 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2726 continue;
2728 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2730 if ( cl->component == comp )
2731 count--;
2735 /* ref count all the files in the component */
2736 if (write)
2738 MSIFILE *file;
2740 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2742 if (file->Component == comp)
2743 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2747 /* add a count for permenent */
2748 if (comp->Attributes & msidbComponentAttributesPermanent)
2749 count ++;
2751 comp->RefCount = count;
2753 if (write)
2754 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2758 * Ok further analysis makes me think that this work is
2759 * actually done in the PublishComponents and PublishFeatures
2760 * step, and not here. It appears like the keypath and all that is
2761 * resolved in this step, however actually written in the Publish steps.
2762 * But we will leave it here for now because it is unclear
2764 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2766 WCHAR squished_pc[GUID_SIZE];
2767 WCHAR squished_cc[GUID_SIZE];
2768 UINT rc;
2769 MSICOMPONENT *comp;
2770 HKEY hkey=0,hkey2=0;
2772 TRACE("\n");
2774 /* writes the Component and Features values to the registry */
2776 rc = MSIREG_OpenComponents(&hkey);
2777 if (rc != ERROR_SUCCESS)
2778 return rc;
2780 squash_guid(package->ProductCode,squished_pc);
2781 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2783 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2785 MSIRECORD * uirow;
2787 ui_progress(package,2,0,0,0);
2788 if (!comp->ComponentId)
2789 continue;
2791 squash_guid(comp->ComponentId,squished_cc);
2793 msi_free(comp->FullKeypath);
2794 comp->FullKeypath = resolve_keypath( package, comp );
2796 /* do the refcounting */
2797 ACTION_RefCountComponent( package, comp );
2799 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2800 debugstr_w(comp->Component),
2801 debugstr_w(squished_cc),
2802 debugstr_w(comp->FullKeypath),
2803 comp->RefCount);
2805 * Write the keypath out if the component is to be registered
2806 * and delete the key if the component is to be deregistered
2808 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2810 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2811 if (rc != ERROR_SUCCESS)
2812 continue;
2814 if (!comp->FullKeypath)
2815 continue;
2817 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2819 if (comp->Attributes & msidbComponentAttributesPermanent)
2821 static const WCHAR szPermKey[] =
2822 { '0','0','0','0','0','0','0','0','0','0','0','0',
2823 '0','0','0','0','0','0','0','0','0','0','0','0',
2824 '0','0','0','0','0','0','0','0',0 };
2826 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2829 RegCloseKey(hkey2);
2831 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey2, TRUE);
2832 if (rc != ERROR_SUCCESS)
2833 continue;
2835 msi_reg_set_val_str(hkey2, squished_pc, comp->FullKeypath);
2836 RegCloseKey(hkey2);
2838 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2840 DWORD res;
2842 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2843 if (rc != ERROR_SUCCESS)
2844 continue;
2846 RegDeleteValueW(hkey2,squished_pc);
2848 /* if the key is empty delete it */
2849 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2850 RegCloseKey(hkey2);
2851 if (res == ERROR_NO_MORE_ITEMS)
2852 RegDeleteKeyW(hkey,squished_cc);
2854 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2857 /* UI stuff */
2858 uirow = MSI_CreateRecord(3);
2859 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2860 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2861 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2862 ui_actiondata(package,szProcessComponents,uirow);
2863 msiobj_release( &uirow->hdr );
2865 RegCloseKey(hkey);
2866 return rc;
2869 typedef struct {
2870 CLSID clsid;
2871 LPWSTR source;
2873 LPWSTR path;
2874 ITypeLib *ptLib;
2875 } typelib_struct;
2877 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2878 LPWSTR lpszName, LONG_PTR lParam)
2880 TLIBATTR *attr;
2881 typelib_struct *tl_struct = (typelib_struct*) lParam;
2882 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2883 int sz;
2884 HRESULT res;
2886 if (!IS_INTRESOURCE(lpszName))
2888 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2889 return TRUE;
2892 sz = strlenW(tl_struct->source)+4;
2893 sz *= sizeof(WCHAR);
2895 if ((INT_PTR)lpszName == 1)
2896 tl_struct->path = strdupW(tl_struct->source);
2897 else
2899 tl_struct->path = msi_alloc(sz);
2900 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2903 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2904 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2905 if (!SUCCEEDED(res))
2907 msi_free(tl_struct->path);
2908 tl_struct->path = NULL;
2910 return TRUE;
2913 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2914 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2916 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2917 return FALSE;
2920 msi_free(tl_struct->path);
2921 tl_struct->path = NULL;
2923 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2924 ITypeLib_Release(tl_struct->ptLib);
2926 return TRUE;
2929 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2931 MSIPACKAGE* package = (MSIPACKAGE*)param;
2932 LPCWSTR component;
2933 MSICOMPONENT *comp;
2934 MSIFILE *file;
2935 typelib_struct tl_struct;
2936 HMODULE module;
2937 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2939 component = MSI_RecordGetString(row,3);
2940 comp = get_loaded_component(package,component);
2941 if (!comp)
2942 return ERROR_SUCCESS;
2944 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2946 TRACE("Skipping typelib reg due to disabled component\n");
2948 comp->Action = comp->Installed;
2950 return ERROR_SUCCESS;
2953 comp->Action = INSTALLSTATE_LOCAL;
2955 file = get_loaded_file( package, comp->KeyPath );
2956 if (!file)
2957 return ERROR_SUCCESS;
2959 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2960 if (module)
2962 LPCWSTR guid;
2963 guid = MSI_RecordGetString(row,1);
2964 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2965 tl_struct.source = strdupW( file->TargetPath );
2966 tl_struct.path = NULL;
2968 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2969 (LONG_PTR)&tl_struct);
2971 if (tl_struct.path)
2973 LPWSTR help = NULL;
2974 LPCWSTR helpid;
2975 HRESULT res;
2977 helpid = MSI_RecordGetString(row,6);
2979 if (helpid)
2980 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2981 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2982 msi_free(help);
2984 if (!SUCCEEDED(res))
2985 ERR("Failed to register type library %s\n",
2986 debugstr_w(tl_struct.path));
2987 else
2989 ui_actiondata(package,szRegisterTypeLibraries,row);
2991 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2994 ITypeLib_Release(tl_struct.ptLib);
2995 msi_free(tl_struct.path);
2997 else
2998 ERR("Failed to load type library %s\n",
2999 debugstr_w(tl_struct.source));
3001 FreeLibrary(module);
3002 msi_free(tl_struct.source);
3004 else
3005 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3007 return ERROR_SUCCESS;
3010 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3013 * OK this is a bit confusing.. I am given a _Component key and I believe
3014 * that the file that is being registered as a type library is the "key file
3015 * of that component" which I interpret to mean "The file in the KeyPath of
3016 * that component".
3018 UINT rc;
3019 MSIQUERY * view;
3020 static const WCHAR Query[] =
3021 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3022 '`','T','y','p','e','L','i','b','`',0};
3024 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3025 if (rc != ERROR_SUCCESS)
3026 return ERROR_SUCCESS;
3028 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3029 msiobj_release(&view->hdr);
3030 return rc;
3033 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3035 MSIPACKAGE *package = (MSIPACKAGE*)param;
3036 LPWSTR target_file, target_folder, filename;
3037 LPCWSTR buffer, extension;
3038 MSICOMPONENT *comp;
3039 static const WCHAR szlnk[]={'.','l','n','k',0};
3040 IShellLinkW *sl = NULL;
3041 IPersistFile *pf = NULL;
3042 HRESULT res;
3044 buffer = MSI_RecordGetString(row,4);
3045 comp = get_loaded_component(package,buffer);
3046 if (!comp)
3047 return ERROR_SUCCESS;
3049 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3051 TRACE("Skipping shortcut creation due to disabled component\n");
3053 comp->Action = comp->Installed;
3055 return ERROR_SUCCESS;
3058 comp->Action = INSTALLSTATE_LOCAL;
3060 ui_actiondata(package,szCreateShortcuts,row);
3062 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3063 &IID_IShellLinkW, (LPVOID *) &sl );
3065 if (FAILED( res ))
3067 ERR("CLSID_ShellLink not available\n");
3068 goto err;
3071 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3072 if (FAILED( res ))
3074 ERR("QueryInterface(IID_IPersistFile) failed\n");
3075 goto err;
3078 buffer = MSI_RecordGetString(row,2);
3079 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3081 /* may be needed because of a bug somehwere else */
3082 create_full_pathW(target_folder);
3084 filename = msi_dup_record_field( row, 3 );
3085 reduce_to_longfilename(filename);
3087 extension = strchrW(filename,'.');
3088 if (!extension || strcmpiW(extension,szlnk))
3090 int len = strlenW(filename);
3091 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3092 memcpy(filename + len, szlnk, sizeof(szlnk));
3094 target_file = build_directory_name(2, target_folder, filename);
3095 msi_free(target_folder);
3096 msi_free(filename);
3098 buffer = MSI_RecordGetString(row,5);
3099 if (strchrW(buffer,'['))
3101 LPWSTR deformated;
3102 deformat_string(package,buffer,&deformated);
3103 IShellLinkW_SetPath(sl,deformated);
3104 msi_free(deformated);
3106 else
3108 FIXME("poorly handled shortcut format, advertised shortcut\n");
3109 IShellLinkW_SetPath(sl,comp->FullKeypath);
3112 if (!MSI_RecordIsNull(row,6))
3114 LPWSTR deformated;
3115 buffer = MSI_RecordGetString(row,6);
3116 deformat_string(package,buffer,&deformated);
3117 IShellLinkW_SetArguments(sl,deformated);
3118 msi_free(deformated);
3121 if (!MSI_RecordIsNull(row,7))
3123 buffer = MSI_RecordGetString(row,7);
3124 IShellLinkW_SetDescription(sl,buffer);
3127 if (!MSI_RecordIsNull(row,8))
3128 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3130 if (!MSI_RecordIsNull(row,9))
3132 LPWSTR Path;
3133 INT index;
3135 buffer = MSI_RecordGetString(row,9);
3137 Path = build_icon_path(package,buffer);
3138 index = MSI_RecordGetInteger(row,10);
3140 /* no value means 0 */
3141 if (index == MSI_NULL_INTEGER)
3142 index = 0;
3144 IShellLinkW_SetIconLocation(sl,Path,index);
3145 msi_free(Path);
3148 if (!MSI_RecordIsNull(row,11))
3149 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3151 if (!MSI_RecordIsNull(row,12))
3153 LPWSTR Path;
3154 buffer = MSI_RecordGetString(row,12);
3155 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3156 if (Path)
3157 IShellLinkW_SetWorkingDirectory(sl,Path);
3158 msi_free(Path);
3161 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3162 IPersistFile_Save(pf,target_file,FALSE);
3164 msi_free(target_file);
3166 err:
3167 if (pf)
3168 IPersistFile_Release( pf );
3169 if (sl)
3170 IShellLinkW_Release( sl );
3172 return ERROR_SUCCESS;
3175 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3177 UINT rc;
3178 HRESULT res;
3179 MSIQUERY * view;
3180 static const WCHAR Query[] =
3181 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3182 '`','S','h','o','r','t','c','u','t','`',0};
3184 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3185 if (rc != ERROR_SUCCESS)
3186 return ERROR_SUCCESS;
3188 res = CoInitialize( NULL );
3189 if (FAILED (res))
3191 ERR("CoInitialize failed\n");
3192 return ERROR_FUNCTION_FAILED;
3195 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3196 msiobj_release(&view->hdr);
3198 CoUninitialize();
3200 return rc;
3203 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3205 MSIPACKAGE* package = (MSIPACKAGE*)param;
3206 HANDLE the_file;
3207 LPWSTR FilePath;
3208 LPCWSTR FileName;
3209 CHAR buffer[1024];
3210 DWORD sz;
3211 UINT rc;
3212 MSIRECORD *uirow;
3214 FileName = MSI_RecordGetString(row,1);
3215 if (!FileName)
3217 ERR("Unable to get FileName\n");
3218 return ERROR_SUCCESS;
3221 FilePath = build_icon_path(package,FileName);
3223 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3225 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3226 FILE_ATTRIBUTE_NORMAL, NULL);
3228 if (the_file == INVALID_HANDLE_VALUE)
3230 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3231 msi_free(FilePath);
3232 return ERROR_SUCCESS;
3237 DWORD write;
3238 sz = 1024;
3239 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3240 if (rc != ERROR_SUCCESS)
3242 ERR("Failed to get stream\n");
3243 CloseHandle(the_file);
3244 DeleteFileW(FilePath);
3245 break;
3247 WriteFile(the_file,buffer,sz,&write,NULL);
3248 } while (sz == 1024);
3250 msi_free(FilePath);
3252 CloseHandle(the_file);
3254 uirow = MSI_CreateRecord(1);
3255 MSI_RecordSetStringW(uirow,1,FileName);
3256 ui_actiondata(package,szPublishProduct,uirow);
3257 msiobj_release( &uirow->hdr );
3259 return ERROR_SUCCESS;
3262 static BOOL msi_check_publish(MSIPACKAGE *package)
3264 MSIFEATURE *feature;
3266 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3268 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3269 return TRUE;
3272 return FALSE;
3276 * 99% of the work done here is only done for
3277 * advertised installs. However this is where the
3278 * Icon table is processed and written out
3279 * so that is what I am going to do here.
3281 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3283 UINT rc;
3284 MSIQUERY * view;
3285 MSISOURCELISTINFO *info;
3286 MSIMEDIADISK *disk;
3287 static const WCHAR Query[]=
3288 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3289 '`','I','c','o','n','`',0};
3290 /* for registry stuff */
3291 HKEY hkey=0;
3292 HKEY hukey=0;
3293 HKEY hudkey=0, props=0;
3294 static const WCHAR szProductLanguage[] =
3295 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3296 static const WCHAR szARPProductIcon[] =
3297 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3298 static const WCHAR szProductVersion[] =
3299 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3300 DWORD langid;
3301 LPWSTR buffer;
3302 DWORD size;
3303 MSIHANDLE hDb, hSumInfo;
3305 /* FIXME: also need to publish if the product is in advertise mode */
3306 if (!msi_check_publish(package))
3307 return ERROR_SUCCESS;
3309 /* write out icon files */
3311 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3312 if (rc == ERROR_SUCCESS)
3314 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3315 msiobj_release(&view->hdr);
3318 /* ok there is a lot more done here but i need to figure out what */
3320 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3321 if (rc != ERROR_SUCCESS)
3322 goto end;
3324 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3325 if (rc != ERROR_SUCCESS)
3326 goto end;
3328 rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3329 if (rc != ERROR_SUCCESS)
3330 goto end;
3332 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode,&props,TRUE);
3333 if (rc != ERROR_SUCCESS)
3334 goto end;
3336 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3337 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3338 msi_free(buffer);
3340 langid = msi_get_property_int( package, szProductLanguage, 0 );
3341 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3343 buffer = msi_dup_property( package, szARPProductIcon );
3344 if (buffer)
3346 LPWSTR path = build_icon_path(package,buffer);
3347 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3348 msi_free( path );
3350 msi_free(buffer);
3352 buffer = msi_dup_property( package, szProductVersion );
3353 if (buffer)
3355 DWORD verdword = msi_version_str_to_dword(buffer);
3356 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3358 msi_free(buffer);
3360 /* FIXME: Need to write more keys to the user registry */
3362 hDb= alloc_msihandle( &package->db->hdr );
3363 if (!hDb) {
3364 rc = ERROR_NOT_ENOUGH_MEMORY;
3365 goto end;
3367 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3368 MsiCloseHandle(hDb);
3369 if (rc == ERROR_SUCCESS)
3371 WCHAR guidbuffer[0x200];
3372 size = 0x200;
3373 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3374 guidbuffer, &size);
3375 if (rc == ERROR_SUCCESS)
3377 WCHAR squashed[GUID_SIZE];
3378 /* for now we only care about the first guid */
3379 LPWSTR ptr = strchrW(guidbuffer,';');
3380 if (ptr) *ptr = 0;
3381 squash_guid(guidbuffer,squashed);
3382 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3384 else
3386 ERR("Unable to query Revision_Number...\n");
3387 rc = ERROR_SUCCESS;
3389 MsiCloseHandle(hSumInfo);
3391 else
3393 ERR("Unable to open Summary Information\n");
3394 rc = ERROR_SUCCESS;
3397 /* publish the SourceList info */
3398 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3400 MsiSourceListSetInfoW(package->ProductCode, NULL,
3401 info->context, info->options,
3402 info->property, info->value);
3405 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3407 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3408 disk->context, disk->options,
3409 disk->disk_id, disk->volume_label, disk->disk_prompt);
3412 end:
3413 RegCloseKey(hkey);
3414 RegCloseKey(hukey);
3415 RegCloseKey(hudkey);
3416 RegCloseKey(props);
3418 return rc;
3421 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3423 MSIPACKAGE *package = (MSIPACKAGE*)param;
3424 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3425 LPWSTR deformated_section, deformated_key, deformated_value;
3426 LPWSTR folder, fullname = NULL;
3427 MSIRECORD * uirow;
3428 INT action;
3429 MSICOMPONENT *comp;
3430 static const WCHAR szWindowsFolder[] =
3431 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3433 component = MSI_RecordGetString(row, 8);
3434 comp = get_loaded_component(package,component);
3436 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3438 TRACE("Skipping ini file due to disabled component %s\n",
3439 debugstr_w(component));
3441 comp->Action = comp->Installed;
3443 return ERROR_SUCCESS;
3446 comp->Action = INSTALLSTATE_LOCAL;
3448 identifier = MSI_RecordGetString(row,1);
3449 filename = MSI_RecordGetString(row,2);
3450 dirproperty = MSI_RecordGetString(row,3);
3451 section = MSI_RecordGetString(row,4);
3452 key = MSI_RecordGetString(row,5);
3453 value = MSI_RecordGetString(row,6);
3454 action = MSI_RecordGetInteger(row,7);
3456 deformat_string(package,section,&deformated_section);
3457 deformat_string(package,key,&deformated_key);
3458 deformat_string(package,value,&deformated_value);
3460 if (dirproperty)
3462 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3463 if (!folder)
3464 folder = msi_dup_property( package, dirproperty );
3466 else
3467 folder = msi_dup_property( package, szWindowsFolder );
3469 if (!folder)
3471 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3472 goto cleanup;
3475 fullname = build_directory_name(2, folder, filename);
3477 if (action == 0)
3479 TRACE("Adding value %s to section %s in %s\n",
3480 debugstr_w(deformated_key), debugstr_w(deformated_section),
3481 debugstr_w(fullname));
3482 WritePrivateProfileStringW(deformated_section, deformated_key,
3483 deformated_value, fullname);
3485 else if (action == 1)
3487 WCHAR returned[10];
3488 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3489 returned, 10, fullname);
3490 if (returned[0] == 0)
3492 TRACE("Adding value %s to section %s in %s\n",
3493 debugstr_w(deformated_key), debugstr_w(deformated_section),
3494 debugstr_w(fullname));
3496 WritePrivateProfileStringW(deformated_section, deformated_key,
3497 deformated_value, fullname);
3500 else if (action == 3)
3501 FIXME("Append to existing section not yet implemented\n");
3503 uirow = MSI_CreateRecord(4);
3504 MSI_RecordSetStringW(uirow,1,identifier);
3505 MSI_RecordSetStringW(uirow,2,deformated_section);
3506 MSI_RecordSetStringW(uirow,3,deformated_key);
3507 MSI_RecordSetStringW(uirow,4,deformated_value);
3508 ui_actiondata(package,szWriteIniValues,uirow);
3509 msiobj_release( &uirow->hdr );
3510 cleanup:
3511 msi_free(fullname);
3512 msi_free(folder);
3513 msi_free(deformated_key);
3514 msi_free(deformated_value);
3515 msi_free(deformated_section);
3516 return ERROR_SUCCESS;
3519 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3521 UINT rc;
3522 MSIQUERY * view;
3523 static const WCHAR ExecSeqQuery[] =
3524 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3525 '`','I','n','i','F','i','l','e','`',0};
3527 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3528 if (rc != ERROR_SUCCESS)
3530 TRACE("no IniFile table\n");
3531 return ERROR_SUCCESS;
3534 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3535 msiobj_release(&view->hdr);
3536 return rc;
3539 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3541 MSIPACKAGE *package = (MSIPACKAGE*)param;
3542 LPCWSTR filename;
3543 LPWSTR FullName;
3544 MSIFILE *file;
3545 DWORD len;
3546 static const WCHAR ExeStr[] =
3547 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3548 static const WCHAR close[] = {'\"',0};
3549 STARTUPINFOW si;
3550 PROCESS_INFORMATION info;
3551 BOOL brc;
3552 MSIRECORD *uirow;
3553 LPWSTR uipath, p;
3555 memset(&si,0,sizeof(STARTUPINFOW));
3557 filename = MSI_RecordGetString(row,1);
3558 file = get_loaded_file( package, filename );
3560 if (!file)
3562 ERR("Unable to find file id %s\n",debugstr_w(filename));
3563 return ERROR_SUCCESS;
3566 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3568 FullName = msi_alloc(len*sizeof(WCHAR));
3569 strcpyW(FullName,ExeStr);
3570 strcatW( FullName, file->TargetPath );
3571 strcatW(FullName,close);
3573 TRACE("Registering %s\n",debugstr_w(FullName));
3574 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3575 &si, &info);
3577 if (brc)
3578 msi_dialog_check_messages(info.hProcess);
3580 msi_free(FullName);
3582 /* the UI chunk */
3583 uirow = MSI_CreateRecord( 2 );
3584 uipath = strdupW( file->TargetPath );
3585 p = strrchrW(uipath,'\\');
3586 if (p)
3587 p[0]=0;
3588 MSI_RecordSetStringW( uirow, 1, &p[1] );
3589 MSI_RecordSetStringW( uirow, 2, uipath);
3590 ui_actiondata( package, szSelfRegModules, uirow);
3591 msiobj_release( &uirow->hdr );
3592 msi_free( uipath );
3593 /* FIXME: call ui_progress? */
3595 return ERROR_SUCCESS;
3598 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3600 UINT rc;
3601 MSIQUERY * view;
3602 static const WCHAR ExecSeqQuery[] =
3603 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3604 '`','S','e','l','f','R','e','g','`',0};
3606 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3607 if (rc != ERROR_SUCCESS)
3609 TRACE("no SelfReg table\n");
3610 return ERROR_SUCCESS;
3613 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3614 msiobj_release(&view->hdr);
3616 return ERROR_SUCCESS;
3619 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3621 MSIFEATURE *feature;
3622 UINT rc;
3623 HKEY hkey=0;
3624 HKEY hukey=0;
3625 HKEY userdata=0;
3627 if (!msi_check_publish(package))
3628 return ERROR_SUCCESS;
3630 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3631 if (rc != ERROR_SUCCESS)
3632 goto end;
3634 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3635 if (rc != ERROR_SUCCESS)
3636 goto end;
3638 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &userdata, TRUE);
3639 if (rc != ERROR_SUCCESS)
3640 goto end;
3642 /* here the guids are base 85 encoded */
3643 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3645 ComponentList *cl;
3646 LPWSTR data = NULL;
3647 GUID clsid;
3648 INT size;
3649 BOOL absent = FALSE;
3650 MSIRECORD *uirow;
3652 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3653 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3654 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3655 absent = TRUE;
3657 size = 1;
3658 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3660 size += 21;
3662 if (feature->Feature_Parent)
3663 size += strlenW( feature->Feature_Parent )+2;
3665 data = msi_alloc(size * sizeof(WCHAR));
3667 data[0] = 0;
3668 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3670 MSICOMPONENT* component = cl->component;
3671 WCHAR buf[21];
3673 buf[0] = 0;
3674 if (component->ComponentId)
3676 TRACE("From %s\n",debugstr_w(component->ComponentId));
3677 CLSIDFromString(component->ComponentId, &clsid);
3678 encode_base85_guid(&clsid,buf);
3679 TRACE("to %s\n",debugstr_w(buf));
3680 strcatW(data,buf);
3684 if (feature->Feature_Parent)
3686 static const WCHAR sep[] = {'\2',0};
3687 strcatW(data,sep);
3688 strcatW(data,feature->Feature_Parent);
3691 msi_reg_set_val_str( hkey, feature->Feature, data );
3692 msi_reg_set_val_str( userdata, feature->Feature, data );
3693 msi_free(data);
3695 size = 0;
3696 if (feature->Feature_Parent)
3697 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3698 if (!absent)
3700 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3701 (LPBYTE)feature->Feature_Parent,size);
3703 else
3705 size += 2*sizeof(WCHAR);
3706 data = msi_alloc(size);
3707 data[0] = 0x6;
3708 data[1] = 0;
3709 if (feature->Feature_Parent)
3710 strcpyW( &data[1], feature->Feature_Parent );
3711 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3712 (LPBYTE)data,size);
3713 msi_free(data);
3716 /* the UI chunk */
3717 uirow = MSI_CreateRecord( 1 );
3718 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3719 ui_actiondata( package, szPublishFeatures, uirow);
3720 msiobj_release( &uirow->hdr );
3721 /* FIXME: call ui_progress? */
3724 end:
3725 RegCloseKey(hkey);
3726 RegCloseKey(hukey);
3727 return rc;
3730 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3732 UINT r;
3733 HKEY hkey;
3735 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3737 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3738 if (r == ERROR_SUCCESS)
3740 RegDeleteValueW(hkey, feature->Feature);
3741 RegCloseKey(hkey);
3744 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3745 if (r == ERROR_SUCCESS)
3747 RegDeleteValueW(hkey, feature->Feature);
3748 RegCloseKey(hkey);
3751 return ERROR_SUCCESS;
3754 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3756 MSIFEATURE *feature;
3758 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3760 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3761 return FALSE;
3764 return TRUE;
3767 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3769 MSIFEATURE *feature;
3771 if (!msi_check_unpublish(package))
3772 return ERROR_SUCCESS;
3774 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3776 msi_unpublish_feature(package, feature);
3779 return ERROR_SUCCESS;
3782 static UINT msi_get_local_package_name( LPWSTR path )
3784 static const WCHAR szInstaller[] = {
3785 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3786 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3787 DWORD time, len, i;
3788 HANDLE handle;
3790 time = GetTickCount();
3791 GetWindowsDirectoryW( path, MAX_PATH );
3792 lstrcatW( path, szInstaller );
3793 CreateDirectoryW( path, NULL );
3795 len = lstrlenW(path);
3796 for (i=0; i<0x10000; i++)
3798 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3799 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3800 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3801 if (handle != INVALID_HANDLE_VALUE)
3803 CloseHandle(handle);
3804 break;
3806 if (GetLastError() != ERROR_FILE_EXISTS &&
3807 GetLastError() != ERROR_SHARING_VIOLATION)
3808 return ERROR_FUNCTION_FAILED;
3811 return ERROR_SUCCESS;
3814 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3816 static const WCHAR szOriginalDatabase[] =
3817 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3818 WCHAR packagefile[MAX_PATH];
3819 LPWSTR msiFilePath;
3820 HKEY props;
3821 UINT r;
3823 r = msi_get_local_package_name( packagefile );
3824 if (r != ERROR_SUCCESS)
3825 return r;
3827 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3829 msiFilePath = msi_dup_property( package, szOriginalDatabase );
3830 r = CopyFileW( msiFilePath, packagefile, FALSE);
3832 if (!r)
3834 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3835 debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3836 msi_free( msiFilePath );
3837 return ERROR_FUNCTION_FAILED;
3839 msi_free( msiFilePath );
3841 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3843 r = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3844 if (r != ERROR_SUCCESS)
3845 return r;
3847 msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3848 RegCloseKey(props);
3849 return ERROR_SUCCESS;
3852 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3854 LPWSTR prop, val, key;
3855 static const LPCSTR propval[] = {
3856 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3857 "ARPCONTACT", "Contact",
3858 "ARPCOMMENTS", "Comments",
3859 "ProductName", "DisplayName",
3860 "ProductVersion", "DisplayVersion",
3861 "ARPHELPLINK", "HelpLink",
3862 "ARPHELPTELEPHONE", "HelpTelephone",
3863 "ARPINSTALLLOCATION", "InstallLocation",
3864 "SourceDir", "InstallSource",
3865 "Manufacturer", "Publisher",
3866 "ARPREADME", "Readme",
3867 "ARPSIZE", "Size",
3868 "ARPURLINFOABOUT", "URLInfoAbout",
3869 "ARPURLUPDATEINFO", "URLUpdateInfo",
3870 NULL,
3872 const LPCSTR *p = propval;
3874 while( *p )
3876 prop = strdupAtoW( *p++ );
3877 key = strdupAtoW( *p++ );
3878 val = msi_dup_property( package, prop );
3879 msi_reg_set_val_str( hkey, key, val );
3880 msi_free(val);
3881 msi_free(key);
3882 msi_free(prop);
3884 return ERROR_SUCCESS;
3887 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3889 HKEY hkey=0;
3890 HKEY hudkey=0, props=0;
3891 LPWSTR buffer = NULL;
3892 UINT rc;
3893 DWORD size, langid;
3894 static const WCHAR szWindowsInstaller[] =
3895 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3896 static const WCHAR szUpgradeCode[] =
3897 {'U','p','g','r','a','d','e','C','o','d','e',0};
3898 static const WCHAR modpath_fmt[] =
3899 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3900 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3901 static const WCHAR szModifyPath[] =
3902 {'M','o','d','i','f','y','P','a','t','h',0};
3903 static const WCHAR szUninstallString[] =
3904 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3905 static const WCHAR szEstimatedSize[] =
3906 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3907 static const WCHAR szProductLanguage[] =
3908 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3909 static const WCHAR szProductVersion[] =
3910 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3912 SYSTEMTIME systime;
3913 static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3914 LPWSTR upgrade_code;
3915 WCHAR szDate[9];
3917 /* FIXME: also need to publish if the product is in advertise mode */
3918 if (!msi_check_publish(package))
3919 return ERROR_SUCCESS;
3921 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3922 if (rc != ERROR_SUCCESS)
3923 return rc;
3925 /* dump all the info i can grab */
3926 /* FIXME: Flesh out more information */
3928 msi_write_uninstall_property_vals( package, hkey );
3930 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3932 msi_make_package_local( package, hkey );
3934 /* do ModifyPath and UninstallString */
3935 size = deformat_string(package,modpath_fmt,&buffer);
3936 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3937 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3938 msi_free(buffer);
3940 /* FIXME: Write real Estimated Size when we have it */
3941 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3943 GetLocalTime(&systime);
3944 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3945 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3947 langid = msi_get_property_int( package, szProductLanguage, 0 );
3948 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3950 buffer = msi_dup_property( package, szProductVersion );
3951 if (buffer)
3953 DWORD verdword = msi_version_str_to_dword(buffer);
3955 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3956 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3957 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3959 msi_free(buffer);
3961 /* Handle Upgrade Codes */
3962 upgrade_code = msi_dup_property( package, szUpgradeCode );
3963 if (upgrade_code)
3965 HKEY hkey2;
3966 WCHAR squashed[33];
3967 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3968 squash_guid(package->ProductCode,squashed);
3969 msi_reg_set_val_str( hkey2, squashed, NULL );
3970 RegCloseKey(hkey2);
3971 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3972 squash_guid(package->ProductCode,squashed);
3973 msi_reg_set_val_str( hkey2, squashed, NULL );
3974 RegCloseKey(hkey2);
3976 msi_free(upgrade_code);
3979 RegCloseKey(hkey);
3981 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3982 if (rc != ERROR_SUCCESS)
3983 return rc;
3985 RegCloseKey(hudkey);
3987 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3988 if (rc != ERROR_SUCCESS)
3989 return rc;
3991 msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
3992 RegCloseKey(props);
3994 return ERROR_SUCCESS;
3997 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
3999 return execute_script(package,INSTALL_SCRIPT);
4002 static UINT msi_unpublish_product(MSIPACKAGE *package)
4004 LPWSTR remove = NULL;
4005 LPWSTR *features = NULL;
4006 BOOL full_uninstall = TRUE;
4007 MSIFEATURE *feature;
4009 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4010 static const WCHAR szAll[] = {'A','L','L',0};
4012 remove = msi_dup_property(package, szRemove);
4013 if (!remove)
4014 return ERROR_SUCCESS;
4016 features = msi_split_string(remove, ',');
4017 if (!features)
4019 msi_free(remove);
4020 ERR("REMOVE feature list is empty!\n");
4021 return ERROR_FUNCTION_FAILED;
4024 if (!lstrcmpW(features[0], szAll))
4025 full_uninstall = TRUE;
4026 else
4028 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4030 if (feature->Action != INSTALLSTATE_ABSENT)
4031 full_uninstall = FALSE;
4035 if (!full_uninstall)
4036 goto done;
4038 MSIREG_DeleteProductKey(package->ProductCode);
4039 MSIREG_DeleteUserProductKey(package->ProductCode);
4040 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4041 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4042 MSIREG_DeleteUninstallKey(package->ProductCode);
4044 done:
4045 msi_free(remove);
4046 msi_free(features);
4047 return ERROR_SUCCESS;
4050 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4052 UINT rc;
4054 rc = msi_unpublish_product(package);
4055 if (rc != ERROR_SUCCESS)
4056 return rc;
4058 /* turn off scheduling */
4059 package->script->CurrentlyScripting= FALSE;
4061 /* first do the same as an InstallExecute */
4062 rc = ACTION_InstallExecute(package);
4063 if (rc != ERROR_SUCCESS)
4064 return rc;
4066 /* then handle Commit Actions */
4067 rc = execute_script(package,COMMIT_SCRIPT);
4069 return rc;
4072 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4074 static const WCHAR RunOnce[] = {
4075 'S','o','f','t','w','a','r','e','\\',
4076 'M','i','c','r','o','s','o','f','t','\\',
4077 'W','i','n','d','o','w','s','\\',
4078 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4079 'R','u','n','O','n','c','e',0};
4080 static const WCHAR InstallRunOnce[] = {
4081 'S','o','f','t','w','a','r','e','\\',
4082 'M','i','c','r','o','s','o','f','t','\\',
4083 'W','i','n','d','o','w','s','\\',
4084 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4085 'I','n','s','t','a','l','l','e','r','\\',
4086 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4088 static const WCHAR msiexec_fmt[] = {
4089 '%','s',
4090 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4091 '\"','%','s','\"',0};
4092 static const WCHAR install_fmt[] = {
4093 '/','I',' ','\"','%','s','\"',' ',
4094 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4095 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4096 WCHAR buffer[256], sysdir[MAX_PATH];
4097 HKEY hkey;
4098 WCHAR squished_pc[100];
4100 squash_guid(package->ProductCode,squished_pc);
4102 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4103 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4104 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4105 squished_pc);
4107 msi_reg_set_val_str( hkey, squished_pc, buffer );
4108 RegCloseKey(hkey);
4110 TRACE("Reboot command %s\n",debugstr_w(buffer));
4112 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4113 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4115 msi_reg_set_val_str( hkey, squished_pc, buffer );
4116 RegCloseKey(hkey);
4118 return ERROR_INSTALL_SUSPEND;
4121 static UINT msi_set_sourcedir_props(MSIPACKAGE *package)
4123 LPWSTR p, source;
4124 DWORD len;
4126 p = strrchrW( package->PackagePath, '\\' );
4127 if (!p)
4128 return ERROR_SUCCESS;
4130 len = p - package->PackagePath + 2;
4131 source = msi_alloc( len * sizeof(WCHAR) );
4132 lstrcpynW( source, package->PackagePath, len );
4134 MSI_SetPropertyW( package, cszSourceDir, source );
4135 MSI_SetPropertyW( package, cszSOURCEDIR, source );
4137 msi_free( source );
4139 return ERROR_SUCCESS;
4142 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4144 DWORD attrib;
4145 UINT rc;
4148 * We are currently doing what should be done here in the top level Install
4149 * however for Administrative and uninstalls this step will be needed
4151 if (!package->PackagePath)
4152 return ERROR_SUCCESS;
4154 msi_set_sourcedir_props(package);
4156 attrib = GetFileAttributesW(package->PackagePath);
4157 if (attrib == INVALID_FILE_ATTRIBUTES)
4159 LPWSTR prompt;
4160 LPWSTR msg;
4161 DWORD size = 0;
4163 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4164 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4165 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4166 if (rc == ERROR_MORE_DATA)
4168 prompt = msi_alloc(size * sizeof(WCHAR));
4169 MsiSourceListGetInfoW(package->ProductCode, NULL,
4170 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4171 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4173 else
4174 prompt = strdupW(package->PackagePath);
4176 msg = generate_error_string(package,1302,1,prompt);
4177 while(attrib == INVALID_FILE_ATTRIBUTES)
4179 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4180 if (rc == IDCANCEL)
4182 rc = ERROR_INSTALL_USEREXIT;
4183 break;
4185 attrib = GetFileAttributesW(package->PackagePath);
4187 msi_free(prompt);
4188 rc = ERROR_SUCCESS;
4190 else
4191 return ERROR_SUCCESS;
4193 return rc;
4196 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4198 HKEY hkey=0;
4199 LPWSTR buffer;
4200 LPWSTR productid;
4201 UINT rc,i;
4203 static const WCHAR szPropKeys[][80] =
4205 {'P','r','o','d','u','c','t','I','D',0},
4206 {'U','S','E','R','N','A','M','E',0},
4207 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4208 {0},
4211 static const WCHAR szRegKeys[][80] =
4213 {'P','r','o','d','u','c','t','I','D',0},
4214 {'R','e','g','O','w','n','e','r',0},
4215 {'R','e','g','C','o','m','p','a','n','y',0},
4216 {0},
4219 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4220 if (!productid)
4221 return ERROR_SUCCESS;
4223 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4224 if (rc != ERROR_SUCCESS)
4225 goto end;
4227 for( i = 0; szPropKeys[i][0]; i++ )
4229 buffer = msi_dup_property( package, szPropKeys[i] );
4230 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4231 msi_free( buffer );
4234 end:
4235 msi_free(productid);
4236 RegCloseKey(hkey);
4238 /* FIXME: call ui_actiondata */
4240 return ERROR_SUCCESS;
4244 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4246 UINT rc;
4248 package->script->InWhatSequence |= SEQUENCE_EXEC;
4249 rc = ACTION_ProcessExecSequence(package,FALSE);
4250 return rc;
4254 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4256 MSIPACKAGE *package = (MSIPACKAGE*)param;
4257 LPCWSTR compgroupid=NULL;
4258 LPCWSTR feature=NULL;
4259 LPCWSTR text = NULL;
4260 LPCWSTR qualifier = NULL;
4261 LPCWSTR component = NULL;
4262 LPWSTR advertise = NULL;
4263 LPWSTR output = NULL;
4264 HKEY hkey;
4265 UINT rc = ERROR_SUCCESS;
4266 MSICOMPONENT *comp;
4267 DWORD sz = 0;
4268 MSIRECORD *uirow;
4270 component = MSI_RecordGetString(rec,3);
4271 comp = get_loaded_component(package,component);
4273 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4274 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4275 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4277 TRACE("Skipping: Component %s not scheduled for install\n",
4278 debugstr_w(component));
4280 return ERROR_SUCCESS;
4283 compgroupid = MSI_RecordGetString(rec,1);
4284 qualifier = MSI_RecordGetString(rec,2);
4286 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4287 if (rc != ERROR_SUCCESS)
4288 goto end;
4290 text = MSI_RecordGetString(rec,4);
4291 feature = MSI_RecordGetString(rec,5);
4293 advertise = create_component_advertise_string(package, comp, feature);
4295 sz = strlenW(advertise);
4297 if (text)
4298 sz += lstrlenW(text);
4300 sz+=3;
4301 sz *= sizeof(WCHAR);
4303 output = msi_alloc_zero(sz);
4304 strcpyW(output,advertise);
4305 msi_free(advertise);
4307 if (text)
4308 strcatW(output,text);
4310 msi_reg_set_val_multi_str( hkey, qualifier, output );
4312 end:
4313 RegCloseKey(hkey);
4314 msi_free(output);
4316 /* the UI chunk */
4317 uirow = MSI_CreateRecord( 2 );
4318 MSI_RecordSetStringW( uirow, 1, compgroupid );
4319 MSI_RecordSetStringW( uirow, 2, qualifier);
4320 ui_actiondata( package, szPublishComponents, uirow);
4321 msiobj_release( &uirow->hdr );
4322 /* FIXME: call ui_progress? */
4324 return rc;
4328 * At present I am ignorning the advertised components part of this and only
4329 * focusing on the qualified component sets
4331 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4333 UINT rc;
4334 MSIQUERY * view;
4335 static const WCHAR ExecSeqQuery[] =
4336 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4337 '`','P','u','b','l','i','s','h',
4338 'C','o','m','p','o','n','e','n','t','`',0};
4340 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4341 if (rc != ERROR_SUCCESS)
4342 return ERROR_SUCCESS;
4344 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4345 msiobj_release(&view->hdr);
4347 return rc;
4350 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4352 MSIPACKAGE *package = (MSIPACKAGE*)param;
4353 MSIRECORD *row;
4354 MSIFILE *file;
4355 SC_HANDLE hscm, service = NULL;
4356 LPCWSTR name, disp, comp, depends, pass;
4357 LPCWSTR load_order, serv_name, key;
4358 DWORD serv_type, start_type;
4359 DWORD err_control;
4361 static const WCHAR query[] =
4362 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4363 '`','C','o','m','p','o','n','e','n','t','`',' ',
4364 'W','H','E','R','E',' ',
4365 '`','C','o','m','p','o','n','e','n','t','`',' ',
4366 '=','\'','%','s','\'',0};
4368 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4369 if (!hscm)
4371 ERR("Failed to open the SC Manager!\n");
4372 goto done;
4375 start_type = MSI_RecordGetInteger(rec, 5);
4376 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4377 goto done;
4379 depends = MSI_RecordGetString(rec, 8);
4380 if (depends && *depends)
4381 FIXME("Dependency list unhandled!\n");
4383 name = MSI_RecordGetString(rec, 2);
4384 disp = MSI_RecordGetString(rec, 3);
4385 serv_type = MSI_RecordGetInteger(rec, 4);
4386 err_control = MSI_RecordGetInteger(rec, 6);
4387 load_order = MSI_RecordGetString(rec, 7);
4388 serv_name = MSI_RecordGetString(rec, 9);
4389 pass = MSI_RecordGetString(rec, 10);
4390 comp = MSI_RecordGetString(rec, 12);
4392 /* fetch the service path */
4393 row = MSI_QueryGetRecord(package->db, query, comp);
4394 if (!row)
4396 ERR("Control query failed!\n");
4397 goto done;
4400 key = MSI_RecordGetString(row, 6);
4402 file = get_loaded_file(package, key);
4403 msiobj_release(&row->hdr);
4404 if (!file)
4406 ERR("Failed to load the service file\n");
4407 goto done;
4410 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4411 start_type, err_control, file->TargetPath,
4412 load_order, NULL, NULL, serv_name, pass);
4413 if (!service)
4415 if (GetLastError() != ERROR_SERVICE_EXISTS)
4416 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4419 done:
4420 CloseServiceHandle(service);
4421 CloseServiceHandle(hscm);
4423 return ERROR_SUCCESS;
4426 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4428 UINT rc;
4429 MSIQUERY * view;
4430 static const WCHAR ExecSeqQuery[] =
4431 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4432 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4434 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4435 if (rc != ERROR_SUCCESS)
4436 return ERROR_SUCCESS;
4438 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4439 msiobj_release(&view->hdr);
4441 return rc;
4444 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4445 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4447 LPCWSTR *vector;
4448 LPWSTR p, q;
4449 DWORD sep_len;
4451 static const WCHAR separator[] = {'[','~',']',0};
4453 *numargs = 0;
4454 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4456 if (!args)
4457 return NULL;
4459 vector = msi_alloc(sizeof(LPWSTR));
4460 if (!vector)
4461 return NULL;
4463 p = args;
4466 (*numargs)++;
4467 vector[*numargs - 1] = p;
4469 if ((q = strstrW(p, separator)))
4471 *q = '\0';
4473 vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4474 if (!vector)
4475 return NULL;
4477 p = q + sep_len;
4479 } while (q);
4481 return vector;
4484 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4486 MSIPACKAGE *package = (MSIPACKAGE *)param;
4487 MSICOMPONENT *comp;
4488 SC_HANDLE scm, service = NULL;
4489 LPCWSTR name, *vector = NULL;
4490 LPWSTR args;
4491 DWORD event, numargs;
4492 UINT r = ERROR_FUNCTION_FAILED;
4494 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4495 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4496 return ERROR_SUCCESS;
4498 name = MSI_RecordGetString(rec, 2);
4499 event = MSI_RecordGetInteger(rec, 3);
4500 args = strdupW(MSI_RecordGetString(rec, 4));
4502 if (!(event & msidbServiceControlEventStart))
4503 return ERROR_SUCCESS;
4505 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4506 if (!scm)
4508 ERR("Failed to open the service control manager\n");
4509 goto done;
4512 service = OpenServiceW(scm, name, SERVICE_START);
4513 if (!service)
4515 ERR("Failed to open service %s\n", debugstr_w(name));
4516 goto done;
4519 vector = msi_service_args_to_vector(args, &numargs);
4521 if (!StartServiceW(service, numargs, vector))
4523 ERR("Failed to start service %s\n", debugstr_w(name));
4524 goto done;
4527 r = ERROR_SUCCESS;
4529 done:
4530 CloseServiceHandle(service);
4531 CloseServiceHandle(scm);
4533 msi_free(args);
4534 msi_free(vector);
4535 return r;
4538 static UINT ACTION_StartServices( MSIPACKAGE *package )
4540 UINT rc;
4541 MSIQUERY *view;
4543 static const WCHAR query[] = {
4544 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4545 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4547 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4548 if (rc != ERROR_SUCCESS)
4549 return ERROR_SUCCESS;
4551 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4552 msiobj_release(&view->hdr);
4554 return rc;
4557 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4559 MSIFILE *file;
4561 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4563 if (!lstrcmpW(file->File, filename))
4564 return file;
4567 return NULL;
4570 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4572 MSIPACKAGE *package = (MSIPACKAGE*)param;
4573 LPWSTR driver, driver_path, ptr;
4574 WCHAR outpath[MAX_PATH];
4575 MSIFILE *driver_file, *setup_file;
4576 LPCWSTR desc;
4577 DWORD len, usage;
4578 UINT r = ERROR_SUCCESS;
4580 static const WCHAR driver_fmt[] = {
4581 'D','r','i','v','e','r','=','%','s',0};
4582 static const WCHAR setup_fmt[] = {
4583 'S','e','t','u','p','=','%','s',0};
4584 static const WCHAR usage_fmt[] = {
4585 'F','i','l','e','U','s','a','g','e','=','1',0};
4587 desc = MSI_RecordGetString(rec, 3);
4589 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4590 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4592 if (!driver_file || !setup_file)
4594 ERR("ODBC Driver entry not found!\n");
4595 return ERROR_FUNCTION_FAILED;
4598 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4599 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4600 lstrlenW(usage_fmt) + 1;
4601 driver = msi_alloc(len * sizeof(WCHAR));
4602 if (!driver)
4603 return ERROR_OUTOFMEMORY;
4605 ptr = driver;
4606 lstrcpyW(ptr, desc);
4607 ptr += lstrlenW(ptr) + 1;
4609 sprintfW(ptr, driver_fmt, driver_file->FileName);
4610 ptr += lstrlenW(ptr) + 1;
4612 sprintfW(ptr, setup_fmt, setup_file->FileName);
4613 ptr += lstrlenW(ptr) + 1;
4615 lstrcpyW(ptr, usage_fmt);
4616 ptr += lstrlenW(ptr) + 1;
4617 *ptr = '\0';
4619 driver_path = strdupW(driver_file->TargetPath);
4620 ptr = strrchrW(driver_path, '\\');
4621 if (ptr) *ptr = '\0';
4623 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4624 NULL, ODBC_INSTALL_COMPLETE, &usage))
4626 ERR("Failed to install SQL driver!\n");
4627 r = ERROR_FUNCTION_FAILED;
4630 msi_free(driver);
4631 msi_free(driver_path);
4633 return r;
4636 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4638 MSIPACKAGE *package = (MSIPACKAGE*)param;
4639 LPWSTR translator, translator_path, ptr;
4640 WCHAR outpath[MAX_PATH];
4641 MSIFILE *translator_file, *setup_file;
4642 LPCWSTR desc;
4643 DWORD len, usage;
4644 UINT r = ERROR_SUCCESS;
4646 static const WCHAR translator_fmt[] = {
4647 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4648 static const WCHAR setup_fmt[] = {
4649 'S','e','t','u','p','=','%','s',0};
4651 desc = MSI_RecordGetString(rec, 3);
4653 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4654 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4656 if (!translator_file || !setup_file)
4658 ERR("ODBC Translator entry not found!\n");
4659 return ERROR_FUNCTION_FAILED;
4662 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4663 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4664 translator = msi_alloc(len * sizeof(WCHAR));
4665 if (!translator)
4666 return ERROR_OUTOFMEMORY;
4668 ptr = translator;
4669 lstrcpyW(ptr, desc);
4670 ptr += lstrlenW(ptr) + 1;
4672 sprintfW(ptr, translator_fmt, translator_file->FileName);
4673 ptr += lstrlenW(ptr) + 1;
4675 sprintfW(ptr, setup_fmt, setup_file->FileName);
4676 ptr += lstrlenW(ptr) + 1;
4677 *ptr = '\0';
4679 translator_path = strdupW(translator_file->TargetPath);
4680 ptr = strrchrW(translator_path, '\\');
4681 if (ptr) *ptr = '\0';
4683 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4684 NULL, ODBC_INSTALL_COMPLETE, &usage))
4686 ERR("Failed to install SQL translator!\n");
4687 r = ERROR_FUNCTION_FAILED;
4690 msi_free(translator);
4691 msi_free(translator_path);
4693 return r;
4696 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4698 LPWSTR attrs;
4699 LPCWSTR desc, driver;
4700 WORD request = ODBC_ADD_SYS_DSN;
4701 INT registration;
4702 DWORD len;
4703 UINT r = ERROR_SUCCESS;
4705 static const WCHAR attrs_fmt[] = {
4706 'D','S','N','=','%','s',0 };
4708 desc = MSI_RecordGetString(rec, 3);
4709 driver = MSI_RecordGetString(rec, 4);
4710 registration = MSI_RecordGetInteger(rec, 5);
4712 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4713 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4715 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4716 attrs = msi_alloc(len * sizeof(WCHAR));
4717 if (!attrs)
4718 return ERROR_OUTOFMEMORY;
4720 sprintfW(attrs, attrs_fmt, desc);
4721 attrs[len - 1] = '\0';
4723 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4725 ERR("Failed to install SQL data source!\n");
4726 r = ERROR_FUNCTION_FAILED;
4729 msi_free(attrs);
4731 return r;
4734 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4736 UINT rc;
4737 MSIQUERY *view;
4739 static const WCHAR driver_query[] = {
4740 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4741 'O','D','B','C','D','r','i','v','e','r',0 };
4743 static const WCHAR translator_query[] = {
4744 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4745 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4747 static const WCHAR source_query[] = {
4748 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4749 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4751 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4752 if (rc != ERROR_SUCCESS)
4753 return ERROR_SUCCESS;
4755 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4756 msiobj_release(&view->hdr);
4758 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4759 if (rc != ERROR_SUCCESS)
4760 return ERROR_SUCCESS;
4762 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4763 msiobj_release(&view->hdr);
4765 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4766 if (rc != ERROR_SUCCESS)
4767 return ERROR_SUCCESS;
4769 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4770 msiobj_release(&view->hdr);
4772 return rc;
4775 #define ENV_ACT_SETALWAYS 0x1
4776 #define ENV_ACT_SETABSENT 0x2
4777 #define ENV_ACT_REMOVE 0x4
4778 #define ENV_ACT_REMOVEMATCH 0x8
4780 #define ENV_MOD_MACHINE 0x20000000
4781 #define ENV_MOD_APPEND 0x40000000
4782 #define ENV_MOD_PREFIX 0x80000000
4783 #define ENV_MOD_MASK 0xC0000000
4785 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4787 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4789 LPCWSTR cptr = *name;
4790 LPCWSTR ptr = *value;
4792 static const WCHAR prefix[] = {'[','~',']',0};
4793 static const int prefix_len = 3;
4795 *flags = 0;
4796 while (*cptr)
4798 if (*cptr == '=')
4799 *flags |= ENV_ACT_SETALWAYS;
4800 else if (*cptr == '+')
4801 *flags |= ENV_ACT_SETABSENT;
4802 else if (*cptr == '-')
4803 *flags |= ENV_ACT_REMOVE;
4804 else if (*cptr == '!')
4805 *flags |= ENV_ACT_REMOVEMATCH;
4806 else if (*cptr == '*')
4807 *flags |= ENV_MOD_MACHINE;
4808 else
4809 break;
4811 cptr++;
4812 (*name)++;
4815 if (!*cptr)
4817 ERR("Missing environment variable\n");
4818 return ERROR_FUNCTION_FAILED;
4821 if (!strncmpW(ptr, prefix, prefix_len))
4823 *flags |= ENV_MOD_APPEND;
4824 *value += lstrlenW(prefix);
4826 else if (lstrlenW(*value) >= prefix_len)
4828 ptr += lstrlenW(ptr) - prefix_len;
4829 if (!lstrcmpW(ptr, prefix))
4831 *flags |= ENV_MOD_PREFIX;
4832 /* the "[~]" will be removed by deformat_string */;
4836 if (!*flags ||
4837 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4838 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4839 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4840 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4842 ERR("Invalid flags: %08x\n", *flags);
4843 return ERROR_FUNCTION_FAILED;
4846 return ERROR_SUCCESS;
4849 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4851 MSIPACKAGE *package = param;
4852 LPCWSTR name, value, comp;
4853 LPWSTR data = NULL, newval = NULL;
4854 LPWSTR deformatted = NULL, ptr;
4855 DWORD flags, type, size;
4856 LONG res;
4857 HKEY env = NULL, root;
4858 LPCWSTR environment;
4860 static const WCHAR user_env[] =
4861 {'E','n','v','i','r','o','n','m','e','n','t',0};
4862 static const WCHAR machine_env[] =
4863 {'S','y','s','t','e','m','\\',
4864 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4865 'C','o','n','t','r','o','l','\\',
4866 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4867 'E','n','v','i','r','o','n','m','e','n','t',0};
4868 static const WCHAR semicolon[] = {';',0};
4870 name = MSI_RecordGetString(rec, 2);
4871 value = MSI_RecordGetString(rec, 3);
4872 comp = MSI_RecordGetString(rec, 4);
4874 res = env_set_flags(&name, &value, &flags);
4875 if (res != ERROR_SUCCESS)
4876 goto done;
4878 deformat_string(package, value, &deformatted);
4879 if (!deformatted)
4881 res = ERROR_OUTOFMEMORY;
4882 goto done;
4885 value = deformatted;
4887 if (flags & ENV_MOD_MACHINE)
4889 environment = machine_env;
4890 root = HKEY_LOCAL_MACHINE;
4892 else
4894 environment = user_env;
4895 root = HKEY_CURRENT_USER;
4898 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
4899 KEY_ALL_ACCESS, NULL, &env, NULL);
4900 if (res != ERROR_SUCCESS)
4901 goto done;
4903 if (flags & ENV_ACT_REMOVE)
4904 FIXME("Not removing environment variable on uninstall!\n");
4906 size = 0;
4907 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
4908 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
4909 (res == ERROR_SUCCESS && type != REG_SZ))
4910 goto done;
4912 if (res != ERROR_FILE_NOT_FOUND)
4914 if (flags & ENV_ACT_SETABSENT)
4916 res = ERROR_SUCCESS;
4917 goto done;
4920 data = msi_alloc(size);
4921 if (!data)
4923 RegCloseKey(env);
4924 return ERROR_OUTOFMEMORY;
4927 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
4928 if (res != ERROR_SUCCESS)
4929 goto done;
4931 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
4933 res = RegDeleteKeyW(env, name);
4934 goto done;
4937 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
4938 newval = msi_alloc(size);
4939 ptr = newval;
4940 if (!newval)
4942 res = ERROR_OUTOFMEMORY;
4943 goto done;
4946 if (!(flags & ENV_MOD_MASK))
4947 lstrcpyW(newval, value);
4948 else
4950 if (flags & ENV_MOD_PREFIX)
4952 lstrcpyW(newval, value);
4953 lstrcatW(newval, semicolon);
4954 ptr = newval + lstrlenW(value) + 1;
4957 lstrcpyW(ptr, data);
4959 if (flags & ENV_MOD_APPEND)
4961 lstrcatW(newval, semicolon);
4962 lstrcatW(newval, value);
4966 else
4968 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
4969 newval = msi_alloc(size);
4970 if (!newval)
4972 res = ERROR_OUTOFMEMORY;
4973 goto done;
4976 lstrcpyW(newval, value);
4979 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
4980 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
4982 done:
4983 if (env) RegCloseKey(env);
4984 msi_free(deformatted);
4985 msi_free(data);
4986 msi_free(newval);
4987 return res;
4990 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
4992 UINT rc;
4993 MSIQUERY * view;
4994 static const WCHAR ExecSeqQuery[] =
4995 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4996 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
4997 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4998 if (rc != ERROR_SUCCESS)
4999 return ERROR_SUCCESS;
5001 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5002 msiobj_release(&view->hdr);
5004 return rc;
5007 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5009 typedef struct
5011 struct list entry;
5012 LPWSTR sourcename;
5013 LPWSTR destname;
5014 LPWSTR source;
5015 LPWSTR dest;
5016 } FILE_LIST;
5018 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5020 BOOL ret;
5022 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5023 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5025 WARN("Source or dest is directory, not moving\n");
5026 return FALSE;
5029 if (options == msidbMoveFileOptionsMove)
5031 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5032 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5033 if (!ret)
5035 WARN("MoveFile failed: %d\n", GetLastError());
5036 return FALSE;
5039 else
5041 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5042 ret = CopyFileW(source, dest, FALSE);
5043 if (!ret)
5045 WARN("CopyFile failed: %d\n", GetLastError());
5046 return FALSE;
5050 return TRUE;
5053 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5055 LPWSTR path, ptr;
5056 DWORD dirlen, pathlen;
5058 ptr = strrchrW(wildcard, '\\');
5059 dirlen = ptr - wildcard + 1;
5061 pathlen = dirlen + lstrlenW(filename) + 1;
5062 path = msi_alloc(pathlen * sizeof(WCHAR));
5064 lstrcpynW(path, wildcard, dirlen + 1);
5065 lstrcatW(path, filename);
5067 return path;
5070 static void free_file_entry(FILE_LIST *file)
5072 msi_free(file->source);
5073 msi_free(file->dest);
5074 msi_free(file);
5077 static void free_list(FILE_LIST *list)
5079 while (!list_empty(&list->entry))
5081 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5083 list_remove(&file->entry);
5084 free_file_entry(file);
5088 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5090 FILE_LIST *new, *file;
5091 LPWSTR ptr, filename;
5092 DWORD size;
5094 new = msi_alloc_zero(sizeof(FILE_LIST));
5095 if (!new)
5096 return FALSE;
5098 new->source = strdupW(source);
5099 ptr = strrchrW(dest, '\\') + 1;
5100 filename = strrchrW(new->source, '\\') + 1;
5102 new->sourcename = filename;
5104 if (*ptr)
5105 new->destname = ptr;
5106 else
5107 new->destname = new->sourcename;
5109 size = (ptr - dest) + lstrlenW(filename) + 1;
5110 new->dest = msi_alloc(size * sizeof(WCHAR));
5111 if (!new->dest)
5113 free_file_entry(new);
5114 return FALSE;
5117 lstrcpynW(new->dest, dest, ptr - dest + 1);
5118 lstrcatW(new->dest, filename);
5120 if (list_empty(&files->entry))
5122 list_add_head(&files->entry, &new->entry);
5123 return TRUE;
5126 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5128 if (lstrcmpW(source, file->source) < 0)
5130 list_add_before(&file->entry, &new->entry);
5131 return TRUE;
5135 list_add_after(&file->entry, &new->entry);
5136 return TRUE;
5139 BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5141 WIN32_FIND_DATAW wfd;
5142 HANDLE hfile;
5143 LPWSTR path;
5144 BOOL res;
5145 FILE_LIST files, *file;
5146 DWORD size;
5148 hfile = FindFirstFileW(source, &wfd);
5149 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5151 list_init(&files.entry);
5153 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5155 if (is_dot_dir(wfd.cFileName)) continue;
5157 path = wildcard_to_file(source, wfd.cFileName);
5158 if (!path)
5160 free_list(&files);
5161 return FALSE;
5164 add_wildcard(&files, path, dest);
5165 msi_free(path);
5168 /* only the first wildcard match gets renamed to dest */
5169 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5170 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5171 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5172 if (!file->dest)
5174 free_list(&files);
5175 return FALSE;
5178 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5180 while (!list_empty(&files.entry))
5182 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5184 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5186 list_remove(&file->entry);
5187 free_file_entry(file);
5190 return TRUE;
5193 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5195 MSIPACKAGE *package = param;
5196 MSICOMPONENT *comp;
5197 LPCWSTR sourcename, destname;
5198 LPWSTR sourcedir = NULL, destdir = NULL;
5199 LPWSTR source = NULL, dest = NULL;
5200 int options;
5201 DWORD size;
5202 BOOL ret, wildcards;
5204 static const WCHAR backslash[] = {'\\',0};
5206 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5207 if (!comp || !comp->Enabled ||
5208 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5210 TRACE("Component not set for install, not moving file\n");
5211 return ERROR_SUCCESS;
5214 sourcename = MSI_RecordGetString(rec, 3);
5215 destname = MSI_RecordGetString(rec, 4);
5216 options = MSI_RecordGetInteger(rec, 7);
5218 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5219 if (!sourcedir)
5220 goto done;
5222 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5223 if (!destdir)
5224 goto done;
5226 if (!sourcename)
5228 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5229 goto done;
5231 source = strdupW(sourcedir);
5232 if (!source)
5233 goto done;
5235 else
5237 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5238 source = msi_alloc(size * sizeof(WCHAR));
5239 if (!source)
5240 goto done;
5242 lstrcpyW(source, sourcedir);
5243 if (source[lstrlenW(source) - 1] != '\\')
5244 lstrcatW(source, backslash);
5245 lstrcatW(source, sourcename);
5248 wildcards = strchrW(source, '*') || strchrW(source, '?');
5250 if (!destname && !wildcards)
5252 destname = strdupW(sourcename);
5253 if (!destname)
5254 goto done;
5257 size = 0;
5258 if (destname)
5259 size = lstrlenW(destname);
5261 size += lstrlenW(destdir) + 2;
5262 dest = msi_alloc(size * sizeof(WCHAR));
5263 if (!dest)
5264 goto done;
5266 lstrcpyW(dest, destdir);
5267 if (dest[lstrlenW(dest) - 1] != '\\')
5268 lstrcatW(dest, backslash);
5270 if (destname)
5271 lstrcatW(dest, destname);
5273 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5275 ret = CreateDirectoryW(destdir, NULL);
5276 if (!ret)
5278 WARN("CreateDirectory failed: %d\n", GetLastError());
5279 return ERROR_SUCCESS;
5283 if (!wildcards)
5284 msi_move_file(source, dest, options);
5285 else
5286 move_files_wildcard(source, dest, options);
5288 done:
5289 msi_free(sourcedir);
5290 msi_free(destdir);
5291 msi_free(source);
5292 msi_free(dest);
5294 return ERROR_SUCCESS;
5297 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5299 UINT rc;
5300 MSIQUERY *view;
5302 static const WCHAR ExecSeqQuery[] =
5303 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5304 '`','M','o','v','e','F','i','l','e','`',0};
5306 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5307 if (rc != ERROR_SUCCESS)
5308 return ERROR_SUCCESS;
5310 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5311 msiobj_release(&view->hdr);
5313 return rc;
5316 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5317 LPCSTR action, LPCWSTR table )
5319 static const WCHAR query[] = {
5320 'S','E','L','E','C','T',' ','*',' ',
5321 'F','R','O','M',' ','`','%','s','`',0 };
5322 MSIQUERY *view = NULL;
5323 DWORD count = 0;
5324 UINT r;
5326 r = MSI_OpenQuery( package->db, &view, query, table );
5327 if (r == ERROR_SUCCESS)
5329 r = MSI_IterateRecords(view, &count, NULL, package);
5330 msiobj_release(&view->hdr);
5333 if (count)
5334 FIXME("%s -> %u ignored %s table values\n",
5335 action, count, debugstr_w(table));
5337 return ERROR_SUCCESS;
5340 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5342 TRACE("%p\n", package);
5343 return ERROR_SUCCESS;
5346 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5348 static const WCHAR table[] =
5349 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5350 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5353 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5355 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5356 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5359 static UINT ACTION_BindImage( MSIPACKAGE *package )
5361 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5362 return msi_unimplemented_action_stub( package, "BindImage", table );
5365 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5367 static const WCHAR table[] = {
5368 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5369 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5372 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5374 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5375 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5378 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5380 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5381 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5384 static UINT ACTION_StopServices( MSIPACKAGE *package )
5386 static const WCHAR table[] = {
5387 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5388 return msi_unimplemented_action_stub( package, "StopServices", table );
5391 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5393 static const WCHAR table[] = {
5394 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5395 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5397 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5399 static const WCHAR table[] = {
5400 'P','r','o','d','u','c','t','I','D',0 };
5401 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5404 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5406 static const WCHAR table[] = {
5407 'E','n','v','i','r','o','n','m','e','n','t',0 };
5408 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5411 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5413 static const WCHAR table[] = {
5414 'M','s','i','A','s','s','e','m','b','l','y',0 };
5415 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5418 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5420 static const WCHAR table[] = {
5421 'M','s','i','A','s','s','e','m','b','l','y',0 };
5422 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5425 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5427 static const WCHAR table[] = { 'F','o','n','t',0 };
5428 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5431 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5433 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5434 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5437 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5439 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5440 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5443 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5445 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5446 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5449 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5451 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5452 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5455 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5457 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5458 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5461 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5463 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5464 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5467 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5469 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5470 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5473 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5475 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5476 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5479 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5481 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5482 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5485 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5487 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5488 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5491 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5493 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5494 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5497 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5499 static const WCHAR table[] = { 'A','p','p','I','d',0 };
5500 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5503 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5505 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5506 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5509 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5511 static const WCHAR table[] = { 'M','I','M','E',0 };
5512 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5515 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5517 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5518 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5521 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5523 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5524 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5527 static const struct _actions StandardActions[] = {
5528 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5529 { szAppSearch, ACTION_AppSearch },
5530 { szBindImage, ACTION_BindImage },
5531 { szCCPSearch, ACTION_CCPSearch },
5532 { szCostFinalize, ACTION_CostFinalize },
5533 { szCostInitialize, ACTION_CostInitialize },
5534 { szCreateFolders, ACTION_CreateFolders },
5535 { szCreateShortcuts, ACTION_CreateShortcuts },
5536 { szDeleteServices, ACTION_DeleteServices },
5537 { szDisableRollback, NULL },
5538 { szDuplicateFiles, ACTION_DuplicateFiles },
5539 { szExecuteAction, ACTION_ExecuteAction },
5540 { szFileCost, ACTION_FileCost },
5541 { szFindRelatedProducts, ACTION_FindRelatedProducts },
5542 { szForceReboot, ACTION_ForceReboot },
5543 { szInstallAdminPackage, NULL },
5544 { szInstallExecute, ACTION_InstallExecute },
5545 { szInstallExecuteAgain, ACTION_InstallExecute },
5546 { szInstallFiles, ACTION_InstallFiles},
5547 { szInstallFinalize, ACTION_InstallFinalize },
5548 { szInstallInitialize, ACTION_InstallInitialize },
5549 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5550 { szInstallValidate, ACTION_InstallValidate },
5551 { szIsolateComponents, ACTION_IsolateComponents },
5552 { szLaunchConditions, ACTION_LaunchConditions },
5553 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5554 { szMoveFiles, ACTION_MoveFiles },
5555 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5556 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5557 { szInstallODBC, ACTION_InstallODBC },
5558 { szInstallServices, ACTION_InstallServices },
5559 { szPatchFiles, ACTION_PatchFiles },
5560 { szProcessComponents, ACTION_ProcessComponents },
5561 { szPublishComponents, ACTION_PublishComponents },
5562 { szPublishFeatures, ACTION_PublishFeatures },
5563 { szPublishProduct, ACTION_PublishProduct },
5564 { szRegisterClassInfo, ACTION_RegisterClassInfo },
5565 { szRegisterComPlus, ACTION_RegisterComPlus},
5566 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5567 { szRegisterFonts, ACTION_RegisterFonts },
5568 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5569 { szRegisterProduct, ACTION_RegisterProduct },
5570 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5571 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5572 { szRegisterUser, ACTION_RegisterUser },
5573 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5574 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5575 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5576 { szRemoveFiles, ACTION_RemoveFiles },
5577 { szRemoveFolders, ACTION_RemoveFolders },
5578 { szRemoveIniValues, ACTION_RemoveIniValues },
5579 { szRemoveODBC, ACTION_RemoveODBC },
5580 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5581 { szRemoveShortcuts, ACTION_RemoveShortcuts },
5582 { szResolveSource, ACTION_ResolveSource },
5583 { szRMCCPSearch, ACTION_RMCCPSearch },
5584 { szScheduleReboot, NULL },
5585 { szSelfRegModules, ACTION_SelfRegModules },
5586 { szSelfUnregModules, ACTION_SelfUnregModules },
5587 { szSetODBCFolders, NULL },
5588 { szStartServices, ACTION_StartServices },
5589 { szStopServices, ACTION_StopServices },
5590 { szUnpublishComponents, ACTION_UnpublishComponents },
5591 { szUnpublishFeatures, ACTION_UnpublishFeatures },
5592 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5593 { szUnregisterComPlus, ACTION_UnregisterComPlus },
5594 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5595 { szUnregisterFonts, ACTION_UnregisterFonts },
5596 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5597 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5598 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5599 { szValidateProductID, ACTION_ValidateProductID },
5600 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5601 { szWriteIniValues, ACTION_WriteIniValues },
5602 { szWriteRegistryValues, ACTION_WriteRegistryValues },
5603 { NULL, NULL },