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