push ad05fa8ea86b4a1581adad6c24ad723042d385d2
[wine/hacks.git] / dlls / msi / action.c
blob9560c4e9499bb1a2f12f4030064ed17816c995c8
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * Pages I need
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
29 #include <stdarg.h>
31 #define COBJMACROS
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winerror.h"
36 #include "winreg.h"
37 #include "winsvc.h"
38 #include "odbcinst.h"
39 #include "wine/debug.h"
40 #include "msidefs.h"
41 #include "msipriv.h"
42 #include "winuser.h"
43 #include "shlobj.h"
44 #include "wine/unicode.h"
45 #include "winver.h"
47 #define REG_PROGRESS_VALUE 13200
48 #define COMPONENT_PROGRESS_VALUE 24000
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
53 * Prototypes
55 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
56 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
57 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
60 * consts and values used
62 static const WCHAR c_colon[] = {'C',':','\\',0};
64 static const WCHAR szCreateFolders[] =
65 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
66 static const WCHAR szCostFinalize[] =
67 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
68 const WCHAR szInstallFiles[] =
69 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
70 const WCHAR szDuplicateFiles[] =
71 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
72 static const WCHAR szWriteRegistryValues[] =
73 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
74 'V','a','l','u','e','s',0};
75 static const WCHAR szCostInitialize[] =
76 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
77 static const WCHAR szFileCost[] =
78 {'F','i','l','e','C','o','s','t',0};
79 static const WCHAR szInstallInitialize[] =
80 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
81 static const WCHAR szInstallValidate[] =
82 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
83 static const WCHAR szLaunchConditions[] =
84 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
85 static const WCHAR szProcessComponents[] =
86 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
87 static const WCHAR szRegisterTypeLibraries[] =
88 {'R','e','g','i','s','t','e','r','T','y','p','e',
89 'L','i','b','r','a','r','i','e','s',0};
90 const WCHAR szRegisterClassInfo[] =
91 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
92 const WCHAR szRegisterProgIdInfo[] =
93 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
94 static const WCHAR szCreateShortcuts[] =
95 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
96 static const WCHAR szPublishProduct[] =
97 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
98 static const WCHAR szWriteIniValues[] =
99 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
100 static const WCHAR szSelfRegModules[] =
101 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
102 static const WCHAR szPublishFeatures[] =
103 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
104 static const WCHAR szRegisterProduct[] =
105 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
106 static const WCHAR szInstallExecute[] =
107 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
108 static const WCHAR szInstallExecuteAgain[] =
109 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
110 'A','g','a','i','n',0};
111 static const WCHAR szInstallFinalize[] =
112 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
113 static const WCHAR szForceReboot[] =
114 {'F','o','r','c','e','R','e','b','o','o','t',0};
115 static const WCHAR szResolveSource[] =
116 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
117 static const WCHAR szAppSearch[] =
118 {'A','p','p','S','e','a','r','c','h',0};
119 static const WCHAR szAllocateRegistrySpace[] =
120 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
121 'S','p','a','c','e',0};
122 static const WCHAR szBindImage[] =
123 {'B','i','n','d','I','m','a','g','e',0};
124 static const WCHAR szCCPSearch[] =
125 {'C','C','P','S','e','a','r','c','h',0};
126 static const WCHAR szDeleteServices[] =
127 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
128 static const WCHAR szDisableRollback[] =
129 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
130 static const WCHAR szExecuteAction[] =
131 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
132 const WCHAR szFindRelatedProducts[] =
133 {'F','i','n','d','R','e','l','a','t','e','d',
134 'P','r','o','d','u','c','t','s',0};
135 static const WCHAR szInstallAdminPackage[] =
136 {'I','n','s','t','a','l','l','A','d','m','i','n',
137 'P','a','c','k','a','g','e',0};
138 static const WCHAR szInstallSFPCatalogFile[] =
139 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
140 'F','i','l','e',0};
141 static const WCHAR szIsolateComponents[] =
142 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
143 const WCHAR szMigrateFeatureStates[] =
144 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
145 'S','t','a','t','e','s',0};
146 const WCHAR szMoveFiles[] =
147 {'M','o','v','e','F','i','l','e','s',0};
148 static const WCHAR szMsiPublishAssemblies[] =
149 {'M','s','i','P','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szMsiUnpublishAssemblies[] =
152 {'M','s','i','U','n','p','u','b','l','i','s','h',
153 'A','s','s','e','m','b','l','i','e','s',0};
154 static const WCHAR szInstallODBC[] =
155 {'I','n','s','t','a','l','l','O','D','B','C',0};
156 static const WCHAR szInstallServices[] =
157 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
158 const WCHAR szPatchFiles[] =
159 {'P','a','t','c','h','F','i','l','e','s',0};
160 static const WCHAR szPublishComponents[] =
161 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
162 static const WCHAR szRegisterComPlus[] =
163 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
164 const WCHAR szRegisterExtensionInfo[] =
165 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
166 'I','n','f','o',0};
167 static const WCHAR szRegisterFonts[] =
168 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
169 const WCHAR szRegisterMIMEInfo[] =
170 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
171 static const WCHAR szRegisterUser[] =
172 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
173 const WCHAR szRemoveDuplicateFiles[] =
174 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
175 'F','i','l','e','s',0};
176 static const WCHAR szRemoveEnvironmentStrings[] =
177 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
178 'S','t','r','i','n','g','s',0};
179 const WCHAR szRemoveExistingProducts[] =
180 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
181 'P','r','o','d','u','c','t','s',0};
182 const WCHAR szRemoveFiles[] =
183 {'R','e','m','o','v','e','F','i','l','e','s',0};
184 static const WCHAR szRemoveFolders[] =
185 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
186 static const WCHAR szRemoveIniValues[] =
187 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
188 static const WCHAR szRemoveODBC[] =
189 {'R','e','m','o','v','e','O','D','B','C',0};
190 static const WCHAR szRemoveRegistryValues[] =
191 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
192 'V','a','l','u','e','s',0};
193 static const WCHAR szRemoveShortcuts[] =
194 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
195 static const WCHAR szRMCCPSearch[] =
196 {'R','M','C','C','P','S','e','a','r','c','h',0};
197 static const WCHAR szScheduleReboot[] =
198 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
199 static const WCHAR szSelfUnregModules[] =
200 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
201 static const WCHAR szSetODBCFolders[] =
202 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
203 static const WCHAR szStartServices[] =
204 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
205 static const WCHAR szStopServices[] =
206 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
207 static const WCHAR szUnpublishComponents[] =
208 {'U','n','p','u','b','l','i','s','h',
209 'C','o','m','p','o','n','e','n','t','s',0};
210 static const WCHAR szUnpublishFeatures[] =
211 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
212 const WCHAR szUnregisterClassInfo[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
214 'I','n','f','o',0};
215 static const WCHAR szUnregisterComPlus[] =
216 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
217 const WCHAR szUnregisterExtensionInfo[] =
218 {'U','n','r','e','g','i','s','t','e','r',
219 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
220 static const WCHAR szUnregisterFonts[] =
221 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
222 const WCHAR szUnregisterMIMEInfo[] =
223 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
224 const WCHAR szUnregisterProgIdInfo[] =
225 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
226 'I','n','f','o',0};
227 static const WCHAR szUnregisterTypeLibraries[] =
228 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
229 'L','i','b','r','a','r','i','e','s',0};
230 static const WCHAR szValidateProductID[] =
231 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
232 static const WCHAR szWriteEnvironmentStrings[] =
233 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
234 'S','t','r','i','n','g','s',0};
236 /* action handlers */
237 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
239 struct _actions {
240 LPCWSTR action;
241 STANDARDACTIONHANDLER handler;
244 static const struct _actions StandardActions[];
247 /********************************************************
248 * helper functions
249 ********************************************************/
251 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
253 static const WCHAR Query_t[] =
254 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
255 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
256 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
257 ' ','\'','%','s','\'',0};
258 MSIRECORD * row;
260 row = MSI_QueryGetRecord( package->db, Query_t, action );
261 if (!row)
262 return;
263 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
264 msiobj_release(&row->hdr);
267 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
268 UINT rc)
270 MSIRECORD * row;
271 static const WCHAR template_s[]=
272 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
273 '%','s', '.',0};
274 static const WCHAR template_e[]=
275 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
276 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
277 '%','i','.',0};
278 static const WCHAR format[] =
279 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
280 WCHAR message[1024];
281 WCHAR timet[0x100];
283 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
284 if (start)
285 sprintfW(message,template_s,timet,action);
286 else
287 sprintfW(message,template_e,timet,action,rc);
289 row = MSI_CreateRecord(1);
290 MSI_RecordSetStringW(row,1,message);
292 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
293 msiobj_release(&row->hdr);
296 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
298 LPCWSTR ptr,ptr2;
299 BOOL quote;
300 DWORD len;
301 LPWSTR prop = NULL, val = NULL;
303 if (!szCommandLine)
304 return ERROR_SUCCESS;
306 ptr = szCommandLine;
308 while (*ptr)
310 if (*ptr==' ')
312 ptr++;
313 continue;
316 TRACE("Looking at %s\n",debugstr_w(ptr));
318 ptr2 = strchrW(ptr,'=');
319 if (!ptr2)
321 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
322 break;
325 quote = FALSE;
327 len = ptr2-ptr;
328 prop = msi_alloc((len+1)*sizeof(WCHAR));
329 memcpy(prop,ptr,len*sizeof(WCHAR));
330 prop[len]=0;
331 ptr2++;
333 len = 0;
334 ptr = ptr2;
335 while (*ptr && (quote || (!quote && *ptr!=' ')))
337 if (*ptr == '"')
338 quote = !quote;
339 ptr++;
340 len++;
343 if (*ptr2=='"')
345 ptr2++;
346 len -= 2;
348 val = msi_alloc((len+1)*sizeof(WCHAR));
349 memcpy(val,ptr2,len*sizeof(WCHAR));
350 val[len] = 0;
352 if (lstrlenW(prop) > 0)
354 TRACE("Found commandline property (%s) = (%s)\n",
355 debugstr_w(prop), debugstr_w(val));
356 MSI_SetPropertyW(package,prop,val);
358 msi_free(val);
359 msi_free(prop);
362 return ERROR_SUCCESS;
366 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
368 LPCWSTR pc;
369 LPWSTR p, *ret = NULL;
370 UINT count = 0;
372 if (!str)
373 return ret;
375 /* count the number of substrings */
376 for ( pc = str, count = 0; pc; count++ )
378 pc = strchrW( pc, sep );
379 if (pc)
380 pc++;
383 /* allocate space for an array of substring pointers and the substrings */
384 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
385 (lstrlenW(str)+1) * sizeof(WCHAR) );
386 if (!ret)
387 return ret;
389 /* copy the string and set the pointers */
390 p = (LPWSTR) &ret[count+1];
391 lstrcpyW( p, str );
392 for( count = 0; (ret[count] = p); count++ )
394 p = strchrW( p, sep );
395 if (p)
396 *p++ = 0;
399 return ret;
402 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
404 WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
405 LPWSTR prod_code, patch_product;
406 UINT ret;
408 prod_code = msi_dup_property( package, szProductCode );
409 patch_product = msi_get_suminfo_product( patch );
411 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
413 if ( strstrW( patch_product, prod_code ) )
414 ret = ERROR_SUCCESS;
415 else
416 ret = ERROR_FUNCTION_FAILED;
418 msi_free( patch_product );
419 msi_free( prod_code );
421 return ret;
424 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
425 MSIDATABASE *patch_db, LPCWSTR name )
427 UINT ret = ERROR_FUNCTION_FAILED;
428 IStorage *stg = NULL;
429 HRESULT r;
431 TRACE("%p %s\n", package, debugstr_w(name) );
433 if (*name++ != ':')
435 ERR("expected a colon in %s\n", debugstr_w(name));
436 return ERROR_FUNCTION_FAILED;
439 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
440 if (SUCCEEDED(r))
442 ret = msi_check_transform_applicable( package, stg );
443 if (ret == ERROR_SUCCESS)
444 msi_table_apply_transform( package->db, stg );
445 else
446 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
447 IStorage_Release( stg );
449 else
450 ERR("failed to open substorage %s\n", debugstr_w(name));
452 return ERROR_SUCCESS;
455 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
457 static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
458 LPWSTR guid_list, *guids, product_code;
459 UINT i, ret = ERROR_FUNCTION_FAILED;
461 product_code = msi_dup_property( package, szProdCode );
462 if (!product_code)
464 /* FIXME: the property ProductCode should be written into the DB somewhere */
465 ERR("no product code to check\n");
466 return ERROR_SUCCESS;
469 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
470 guids = msi_split_string( guid_list, ';' );
471 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
473 if (!lstrcmpW( guids[i], product_code ))
474 ret = ERROR_SUCCESS;
476 msi_free( guids );
477 msi_free( guid_list );
478 msi_free( product_code );
480 return ret;
483 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
485 MSISUMMARYINFO *si;
486 LPWSTR str, *substorage;
487 UINT i, r = ERROR_SUCCESS;
489 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
490 if (!si)
491 return ERROR_FUNCTION_FAILED;
493 msi_check_patch_applicable( package, si );
495 /* enumerate the substorage */
496 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
497 substorage = msi_split_string( str, ';' );
498 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
499 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
500 msi_free( substorage );
501 msi_free( str );
503 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
505 msiobj_release( &si->hdr );
507 return r;
510 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
512 MSIDATABASE *patch_db = NULL;
513 UINT r;
515 TRACE("%p %s\n", package, debugstr_w( file ) );
517 /* FIXME:
518 * We probably want to make sure we only open a patch collection here.
519 * Patch collections (.msp) and databases (.msi) have different GUIDs
520 * but currently MSI_OpenDatabaseW will accept both.
522 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
523 if ( r != ERROR_SUCCESS )
525 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
526 return r;
529 msi_parse_patch_summary( package, patch_db );
532 * There might be a CAB file in the patch package,
533 * so append it to the list of storage to search for streams.
535 append_storage_to_db( package->db, patch_db->storage );
537 msiobj_release( &patch_db->hdr );
539 return ERROR_SUCCESS;
542 /* get the PATCH property, and apply all the patches it specifies */
543 static UINT msi_apply_patches( MSIPACKAGE *package )
545 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
546 LPWSTR patch_list, *patches;
547 UINT i, r = ERROR_SUCCESS;
549 patch_list = msi_dup_property( package, szPatch );
551 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
553 patches = msi_split_string( patch_list, ';' );
554 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
555 r = msi_apply_patch_package( package, patches[i] );
557 msi_free( patches );
558 msi_free( patch_list );
560 return r;
563 static UINT msi_apply_transforms( MSIPACKAGE *package )
565 static const WCHAR szTransforms[] = {
566 'T','R','A','N','S','F','O','R','M','S',0 };
567 LPWSTR xform_list, *xforms;
568 UINT i, r = ERROR_SUCCESS;
570 xform_list = msi_dup_property( package, szTransforms );
571 xforms = msi_split_string( xform_list, ';' );
573 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
575 if (xforms[i][0] == ':')
576 r = msi_apply_substorage_transform( package, package->db, &xforms[i][1] );
577 else
578 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
581 msi_free( xforms );
582 msi_free( xform_list );
584 return r;
587 BOOL ui_sequence_exists( MSIPACKAGE *package )
589 MSIQUERY *view;
590 UINT rc;
592 static const WCHAR ExecSeqQuery [] =
593 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
594 '`','I','n','s','t','a','l','l',
595 'U','I','S','e','q','u','e','n','c','e','`',
596 ' ','W','H','E','R','E',' ',
597 '`','S','e','q','u','e','n','c','e','`',' ',
598 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
599 '`','S','e','q','u','e','n','c','e','`',0};
601 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
602 if (rc == ERROR_SUCCESS)
604 msiobj_release(&view->hdr);
605 return TRUE;
608 return FALSE;
611 /****************************************************
612 * TOP level entry points
613 *****************************************************/
615 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
616 LPCWSTR szCommandLine )
618 UINT rc;
619 BOOL ui = FALSE, ui_exists;
620 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
621 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
622 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
624 MSI_SetPropertyW(package, szAction, szInstall);
626 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
628 package->script->InWhatSequence = SEQUENCE_INSTALL;
630 if (szPackagePath)
632 LPWSTR p, check, dir;
633 LPCWSTR file;
635 dir = strdupW(szPackagePath);
636 p = strrchrW(dir, '\\');
637 if (p)
639 *(++p) = 0;
640 file = szPackagePath + (p - dir);
642 else
644 msi_free(dir);
645 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
646 GetCurrentDirectoryW(MAX_PATH, dir);
647 lstrcatW(dir, cszbs);
648 file = szPackagePath;
651 msi_free( package->PackagePath );
652 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
653 if (!package->PackagePath)
655 msi_free(dir);
656 return ERROR_OUTOFMEMORY;
659 lstrcpyW(package->PackagePath, dir);
660 lstrcatW(package->PackagePath, file);
662 check = msi_dup_property( package, cszSourceDir );
663 if (!check)
664 MSI_SetPropertyW(package, cszSourceDir, dir);
665 msi_free(check);
667 check = msi_dup_property( package, cszSOURCEDIR );
668 if (!check)
669 MSI_SetPropertyW(package, cszSOURCEDIR, dir);
671 msi_free(dir);
672 msi_free(check);
675 msi_parse_command_line( package, szCommandLine );
677 msi_apply_transforms( package );
678 msi_apply_patches( package );
680 /* properties may have been added by a transform */
681 msi_clone_properties( package );
683 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
685 package->script->InWhatSequence |= SEQUENCE_UI;
686 rc = ACTION_ProcessUISequence(package);
687 ui = TRUE;
688 ui_exists = ui_sequence_exists(package);
689 if (rc == ERROR_SUCCESS || !ui_exists)
691 package->script->InWhatSequence |= SEQUENCE_EXEC;
692 rc = ACTION_ProcessExecSequence(package,ui_exists);
695 else
696 rc = ACTION_ProcessExecSequence(package,FALSE);
698 if (rc == -1)
700 /* install was halted but should be considered a success */
701 rc = ERROR_SUCCESS;
704 package->script->CurrentlyScripting= FALSE;
706 /* process the ending type action */
707 if (rc == ERROR_SUCCESS)
708 ACTION_PerformActionSequence(package,-1,ui);
709 else if (rc == ERROR_INSTALL_USEREXIT)
710 ACTION_PerformActionSequence(package,-2,ui);
711 else if (rc == ERROR_INSTALL_SUSPEND)
712 ACTION_PerformActionSequence(package,-4,ui);
713 else /* failed */
714 ACTION_PerformActionSequence(package,-3,ui);
716 /* finish up running custom actions */
717 ACTION_FinishCustomActions(package);
719 return rc;
722 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
724 UINT rc = ERROR_SUCCESS;
725 MSIRECORD * row = 0;
726 static const WCHAR ExecSeqQuery[] =
727 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
728 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
729 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
730 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
732 static const WCHAR UISeqQuery[] =
733 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
734 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
735 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
736 ' ', '=',' ','%','i',0};
738 if (UI)
739 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
740 else
741 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
743 if (row)
745 LPCWSTR action, cond;
747 TRACE("Running the actions\n");
749 /* check conditions */
750 cond = MSI_RecordGetString(row,2);
752 /* this is a hack to skip errors in the condition code */
753 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
754 goto end;
756 action = MSI_RecordGetString(row,1);
757 if (!action)
759 ERR("failed to fetch action\n");
760 rc = ERROR_FUNCTION_FAILED;
761 goto end;
764 if (UI)
765 rc = ACTION_PerformUIAction(package,action,-1);
766 else
767 rc = ACTION_PerformAction(package,action,-1,FALSE);
768 end:
769 msiobj_release(&row->hdr);
771 else
772 rc = ERROR_SUCCESS;
774 return rc;
777 typedef struct {
778 MSIPACKAGE* package;
779 BOOL UI;
780 } iterate_action_param;
782 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
784 iterate_action_param *iap= (iterate_action_param*)param;
785 UINT rc;
786 LPCWSTR cond, action;
788 action = MSI_RecordGetString(row,1);
789 if (!action)
791 ERR("Error is retrieving action name\n");
792 return ERROR_FUNCTION_FAILED;
795 /* check conditions */
796 cond = MSI_RecordGetString(row,2);
798 /* this is a hack to skip errors in the condition code */
799 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
801 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
802 return ERROR_SUCCESS;
805 if (iap->UI)
806 rc = ACTION_PerformUIAction(iap->package,action,-1);
807 else
808 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
810 msi_dialog_check_messages( NULL );
812 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
813 rc = iap->package->CurrentInstallState;
815 if (rc == ERROR_FUNCTION_NOT_CALLED)
816 rc = ERROR_SUCCESS;
818 if (rc != ERROR_SUCCESS)
819 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
821 return rc;
824 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
826 MSIQUERY * view;
827 UINT r;
828 static const WCHAR query[] =
829 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
830 '`','%','s','`',
831 ' ','W','H','E','R','E',' ',
832 '`','S','e','q','u','e','n','c','e','`',' ',
833 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
834 '`','S','e','q','u','e','n','c','e','`',0};
835 iterate_action_param iap;
838 * FIXME: probably should be checking UILevel in the
839 * ACTION_PerformUIAction/ACTION_PerformAction
840 * rather than saving the UI level here. Those
841 * two functions can be merged too.
843 iap.package = package;
844 iap.UI = TRUE;
846 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
848 r = MSI_OpenQuery( package->db, &view, query, szTable );
849 if (r == ERROR_SUCCESS)
851 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
852 msiobj_release(&view->hdr);
855 return r;
858 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
860 MSIQUERY * view;
861 UINT rc;
862 static const WCHAR ExecSeqQuery[] =
863 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
864 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
865 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
866 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
867 'O','R','D','E','R',' ', 'B','Y',' ',
868 '`','S','e','q','u','e','n','c','e','`',0 };
869 MSIRECORD * row = 0;
870 static const WCHAR IVQuery[] =
871 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
872 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
873 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
874 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
875 ' ','\'', 'I','n','s','t','a','l','l',
876 'V','a','l','i','d','a','t','e','\'', 0};
877 INT seq = 0;
878 iterate_action_param iap;
880 iap.package = package;
881 iap.UI = FALSE;
883 if (package->script->ExecuteSequenceRun)
885 TRACE("Execute Sequence already Run\n");
886 return ERROR_SUCCESS;
889 package->script->ExecuteSequenceRun = TRUE;
891 /* get the sequence number */
892 if (UIran)
894 row = MSI_QueryGetRecord(package->db, IVQuery);
895 if( !row )
896 return ERROR_FUNCTION_FAILED;
897 seq = MSI_RecordGetInteger(row,1);
898 msiobj_release(&row->hdr);
901 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
902 if (rc == ERROR_SUCCESS)
904 TRACE("Running the actions\n");
906 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
907 msiobj_release(&view->hdr);
910 return rc;
913 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
915 MSIQUERY * view;
916 UINT rc;
917 static const WCHAR ExecSeqQuery [] =
918 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
919 '`','I','n','s','t','a','l','l',
920 'U','I','S','e','q','u','e','n','c','e','`',
921 ' ','W','H','E','R','E',' ',
922 '`','S','e','q','u','e','n','c','e','`',' ',
923 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
924 '`','S','e','q','u','e','n','c','e','`',0};
925 iterate_action_param iap;
927 iap.package = package;
928 iap.UI = TRUE;
930 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
932 if (rc == ERROR_SUCCESS)
934 TRACE("Running the actions\n");
936 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
937 msiobj_release(&view->hdr);
940 return rc;
943 /********************************************************
944 * ACTION helper functions and functions that perform the actions
945 *******************************************************/
946 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
947 UINT* rc, BOOL force )
949 BOOL ret = FALSE;
950 BOOL run = force;
951 int i;
953 if (!run && !package->script->CurrentlyScripting)
954 run = TRUE;
956 if (!run)
958 if (strcmpW(action,szInstallFinalize) == 0 ||
959 strcmpW(action,szInstallExecute) == 0 ||
960 strcmpW(action,szInstallExecuteAgain) == 0)
961 run = TRUE;
964 i = 0;
965 while (StandardActions[i].action != NULL)
967 if (strcmpW(StandardActions[i].action, action)==0)
969 if (!run)
971 ui_actioninfo(package, action, TRUE, 0);
972 *rc = schedule_action(package,INSTALL_SCRIPT,action);
973 ui_actioninfo(package, action, FALSE, *rc);
975 else
977 ui_actionstart(package, action);
978 if (StandardActions[i].handler)
980 *rc = StandardActions[i].handler(package);
982 else
984 FIXME("unhandled standard action %s\n",debugstr_w(action));
985 *rc = ERROR_SUCCESS;
988 ret = TRUE;
989 break;
991 i++;
993 return ret;
996 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
997 UINT* rc, UINT script, BOOL force )
999 BOOL ret=FALSE;
1000 UINT arc;
1002 arc = ACTION_CustomAction(package, action, script, force);
1004 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1006 *rc = arc;
1007 ret = TRUE;
1009 return ret;
1013 * A lot of actions are really important even if they don't do anything
1014 * explicit... Lots of properties are set at the beginning of the installation
1015 * CostFinalize does a bunch of work to translate the directories and such
1017 * But until I get write access to the database that is hard, so I am going to
1018 * hack it to see if I can get something to run.
1020 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1022 UINT rc = ERROR_SUCCESS;
1023 BOOL handled;
1025 TRACE("Performing action (%s)\n",debugstr_w(action));
1027 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1029 if (!handled)
1030 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1032 if (!handled)
1034 FIXME("unhandled msi action %s\n",debugstr_w(action));
1035 rc = ERROR_FUNCTION_NOT_CALLED;
1038 return rc;
1041 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1043 UINT rc = ERROR_SUCCESS;
1044 BOOL handled = FALSE;
1046 TRACE("Performing action (%s)\n",debugstr_w(action));
1048 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1050 if (!handled)
1051 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1053 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1054 handled = TRUE;
1056 if (!handled)
1058 FIXME("unhandled msi action %s\n",debugstr_w(action));
1059 rc = ERROR_FUNCTION_NOT_CALLED;
1062 return rc;
1067 * Actual Action Handlers
1070 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1072 MSIPACKAGE *package = (MSIPACKAGE*)param;
1073 LPCWSTR dir;
1074 LPWSTR full_path;
1075 MSIRECORD *uirow;
1076 MSIFOLDER *folder;
1078 dir = MSI_RecordGetString(row,1);
1079 if (!dir)
1081 ERR("Unable to get folder id\n");
1082 return ERROR_SUCCESS;
1085 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1086 if (!full_path)
1088 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1089 return ERROR_SUCCESS;
1092 TRACE("Folder is %s\n",debugstr_w(full_path));
1094 /* UI stuff */
1095 uirow = MSI_CreateRecord(1);
1096 MSI_RecordSetStringW(uirow,1,full_path);
1097 ui_actiondata(package,szCreateFolders,uirow);
1098 msiobj_release( &uirow->hdr );
1100 if (folder->State == 0)
1101 create_full_pathW(full_path);
1103 folder->State = 3;
1105 msi_free(full_path);
1106 return ERROR_SUCCESS;
1109 /* FIXME: probably should merge this with the above function */
1110 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1112 UINT rc = ERROR_SUCCESS;
1113 MSIFOLDER *folder;
1114 LPWSTR install_path;
1116 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1117 if (!install_path)
1118 return ERROR_FUNCTION_FAILED;
1120 /* create the path */
1121 if (folder->State == 0)
1123 create_full_pathW(install_path);
1124 folder->State = 2;
1126 msi_free(install_path);
1128 return rc;
1131 UINT msi_create_component_directories( MSIPACKAGE *package )
1133 MSICOMPONENT *comp;
1135 /* create all the folders required by the components are going to install */
1136 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1138 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1139 continue;
1140 msi_create_directory( package, comp->Directory );
1143 return ERROR_SUCCESS;
1147 * Also we cannot enable/disable components either, so for now I am just going
1148 * to do all the directories for all the components.
1150 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1152 static const WCHAR ExecSeqQuery[] =
1153 {'S','E','L','E','C','T',' ',
1154 '`','D','i','r','e','c','t','o','r','y','_','`',
1155 ' ','F','R','O','M',' ',
1156 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1157 UINT rc;
1158 MSIQUERY *view;
1160 /* create all the empty folders specified in the CreateFolder table */
1161 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1162 if (rc != ERROR_SUCCESS)
1163 return ERROR_SUCCESS;
1165 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1166 msiobj_release(&view->hdr);
1168 msi_create_component_directories( package );
1170 return rc;
1173 static UINT load_component( MSIRECORD *row, LPVOID param )
1175 MSIPACKAGE *package = param;
1176 MSICOMPONENT *comp;
1178 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1179 if (!comp)
1180 return ERROR_FUNCTION_FAILED;
1182 list_add_tail( &package->components, &comp->entry );
1184 /* fill in the data */
1185 comp->Component = msi_dup_record_field( row, 1 );
1187 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1189 comp->ComponentId = msi_dup_record_field( row, 2 );
1190 comp->Directory = msi_dup_record_field( row, 3 );
1191 comp->Attributes = MSI_RecordGetInteger(row,4);
1192 comp->Condition = msi_dup_record_field( row, 5 );
1193 comp->KeyPath = msi_dup_record_field( row, 6 );
1195 comp->Installed = INSTALLSTATE_UNKNOWN;
1196 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1198 return ERROR_SUCCESS;
1201 static UINT load_all_components( MSIPACKAGE *package )
1203 static const WCHAR query[] = {
1204 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1205 '`','C','o','m','p','o','n','e','n','t','`',0 };
1206 MSIQUERY *view;
1207 UINT r;
1209 if (!list_empty(&package->components))
1210 return ERROR_SUCCESS;
1212 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1213 if (r != ERROR_SUCCESS)
1214 return r;
1216 r = MSI_IterateRecords(view, NULL, load_component, package);
1217 msiobj_release(&view->hdr);
1218 return r;
1221 typedef struct {
1222 MSIPACKAGE *package;
1223 MSIFEATURE *feature;
1224 } _ilfs;
1226 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1228 ComponentList *cl;
1230 cl = msi_alloc( sizeof (*cl) );
1231 if ( !cl )
1232 return ERROR_NOT_ENOUGH_MEMORY;
1233 cl->component = comp;
1234 list_add_tail( &feature->Components, &cl->entry );
1236 return ERROR_SUCCESS;
1239 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1241 FeatureList *fl;
1243 fl = msi_alloc( sizeof(*fl) );
1244 if ( !fl )
1245 return ERROR_NOT_ENOUGH_MEMORY;
1246 fl->feature = child;
1247 list_add_tail( &parent->Children, &fl->entry );
1249 return ERROR_SUCCESS;
1252 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1254 _ilfs* ilfs= (_ilfs*)param;
1255 LPCWSTR component;
1256 MSICOMPONENT *comp;
1258 component = MSI_RecordGetString(row,1);
1260 /* check to see if the component is already loaded */
1261 comp = get_loaded_component( ilfs->package, component );
1262 if (!comp)
1264 ERR("unknown component %s\n", debugstr_w(component));
1265 return ERROR_FUNCTION_FAILED;
1268 add_feature_component( ilfs->feature, comp );
1269 comp->Enabled = TRUE;
1271 return ERROR_SUCCESS;
1274 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1276 MSIFEATURE *feature;
1278 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1280 if ( !lstrcmpW( feature->Feature, name ) )
1281 return feature;
1284 return NULL;
1287 static UINT load_feature(MSIRECORD * row, LPVOID param)
1289 MSIPACKAGE* package = (MSIPACKAGE*)param;
1290 MSIFEATURE* feature;
1291 static const WCHAR Query1[] =
1292 {'S','E','L','E','C','T',' ',
1293 '`','C','o','m','p','o','n','e','n','t','_','`',
1294 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1295 'C','o','m','p','o','n','e','n','t','s','`',' ',
1296 'W','H','E','R','E',' ',
1297 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1298 MSIQUERY * view;
1299 UINT rc;
1300 _ilfs ilfs;
1302 /* fill in the data */
1304 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1305 if (!feature)
1306 return ERROR_NOT_ENOUGH_MEMORY;
1308 list_init( &feature->Children );
1309 list_init( &feature->Components );
1311 feature->Feature = msi_dup_record_field( row, 1 );
1313 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1315 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1316 feature->Title = msi_dup_record_field( row, 3 );
1317 feature->Description = msi_dup_record_field( row, 4 );
1319 if (!MSI_RecordIsNull(row,5))
1320 feature->Display = MSI_RecordGetInteger(row,5);
1322 feature->Level= MSI_RecordGetInteger(row,6);
1323 feature->Directory = msi_dup_record_field( row, 7 );
1324 feature->Attributes = MSI_RecordGetInteger(row,8);
1326 feature->Installed = INSTALLSTATE_UNKNOWN;
1327 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1329 list_add_tail( &package->features, &feature->entry );
1331 /* load feature components */
1333 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1334 if (rc != ERROR_SUCCESS)
1335 return ERROR_SUCCESS;
1337 ilfs.package = package;
1338 ilfs.feature = feature;
1340 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1341 msiobj_release(&view->hdr);
1343 return ERROR_SUCCESS;
1346 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1348 MSIPACKAGE* package = (MSIPACKAGE*)param;
1349 MSIFEATURE *parent, *child;
1351 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1352 if (!child)
1353 return ERROR_FUNCTION_FAILED;
1355 if (!child->Feature_Parent)
1356 return ERROR_SUCCESS;
1358 parent = find_feature_by_name( package, child->Feature_Parent );
1359 if (!parent)
1360 return ERROR_FUNCTION_FAILED;
1362 add_feature_child( parent, child );
1363 return ERROR_SUCCESS;
1366 static UINT load_all_features( MSIPACKAGE *package )
1368 static const WCHAR query[] = {
1369 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1370 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1371 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1372 MSIQUERY *view;
1373 UINT r;
1375 if (!list_empty(&package->features))
1376 return ERROR_SUCCESS;
1378 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1379 if (r != ERROR_SUCCESS)
1380 return r;
1382 r = MSI_IterateRecords( view, NULL, load_feature, package );
1383 if (r != ERROR_SUCCESS)
1384 return r;
1386 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1387 msiobj_release( &view->hdr );
1389 return r;
1392 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1394 if (!p)
1395 return p;
1396 p = strchrW(p, ch);
1397 if (!p)
1398 return p;
1399 *p = 0;
1400 return p+1;
1403 static UINT load_file(MSIRECORD *row, LPVOID param)
1405 MSIPACKAGE* package = (MSIPACKAGE*)param;
1406 LPCWSTR component;
1407 MSIFILE *file;
1409 /* fill in the data */
1411 file = msi_alloc_zero( sizeof (MSIFILE) );
1412 if (!file)
1413 return ERROR_NOT_ENOUGH_MEMORY;
1415 file->File = msi_dup_record_field( row, 1 );
1417 component = MSI_RecordGetString( row, 2 );
1418 file->Component = get_loaded_component( package, component );
1420 if (!file->Component)
1421 ERR("Unfound Component %s\n",debugstr_w(component));
1423 file->FileName = msi_dup_record_field( row, 3 );
1424 reduce_to_longfilename( file->FileName );
1426 file->ShortName = msi_dup_record_field( row, 3 );
1427 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1429 file->FileSize = MSI_RecordGetInteger( row, 4 );
1430 file->Version = msi_dup_record_field( row, 5 );
1431 file->Language = msi_dup_record_field( row, 6 );
1432 file->Attributes = MSI_RecordGetInteger( row, 7 );
1433 file->Sequence = MSI_RecordGetInteger( row, 8 );
1435 file->state = msifs_invalid;
1437 /* if the compressed bits are not set in the file attributes,
1438 * then read the information from the package word count property
1440 if (file->Attributes & msidbFileAttributesCompressed)
1442 file->IsCompressed = TRUE;
1444 else if (file->Attributes & msidbFileAttributesNoncompressed)
1446 file->IsCompressed = FALSE;
1448 else
1450 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1453 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1455 list_add_tail( &package->files, &file->entry );
1457 return ERROR_SUCCESS;
1460 static UINT load_all_files(MSIPACKAGE *package)
1462 MSIQUERY * view;
1463 UINT rc;
1464 static const WCHAR Query[] =
1465 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1466 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1467 '`','S','e','q','u','e','n','c','e','`', 0};
1469 if (!list_empty(&package->files))
1470 return ERROR_SUCCESS;
1472 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1473 if (rc != ERROR_SUCCESS)
1474 return ERROR_SUCCESS;
1476 rc = MSI_IterateRecords(view, NULL, load_file, package);
1477 msiobj_release(&view->hdr);
1479 return ERROR_SUCCESS;
1482 static UINT load_folder( MSIRECORD *row, LPVOID param )
1484 MSIPACKAGE *package = param;
1485 static const WCHAR szDot[] = { '.',0 };
1486 static WCHAR szEmpty[] = { 0 };
1487 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1488 MSIFOLDER *folder;
1490 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1491 if (!folder)
1492 return ERROR_NOT_ENOUGH_MEMORY;
1494 folder->Directory = msi_dup_record_field( row, 1 );
1496 TRACE("%s\n", debugstr_w(folder->Directory));
1498 p = msi_dup_record_field(row, 3);
1500 /* split src and target dir */
1501 tgt_short = p;
1502 src_short = folder_split_path( p, ':' );
1504 /* split the long and short paths */
1505 tgt_long = folder_split_path( tgt_short, '|' );
1506 src_long = folder_split_path( src_short, '|' );
1508 /* check for no-op dirs */
1509 if (!lstrcmpW(szDot, tgt_short))
1510 tgt_short = szEmpty;
1511 if (!lstrcmpW(szDot, src_short))
1512 src_short = szEmpty;
1514 if (!tgt_long)
1515 tgt_long = tgt_short;
1517 if (!src_short) {
1518 src_short = tgt_short;
1519 src_long = tgt_long;
1522 if (!src_long)
1523 src_long = src_short;
1525 /* FIXME: use the target short path too */
1526 folder->TargetDefault = strdupW(tgt_long);
1527 folder->SourceShortPath = strdupW(src_short);
1528 folder->SourceLongPath = strdupW(src_long);
1529 msi_free(p);
1531 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1532 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1533 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1535 folder->Parent = msi_dup_record_field( row, 2 );
1537 folder->Property = msi_dup_property( package, folder->Directory );
1539 list_add_tail( &package->folders, &folder->entry );
1541 TRACE("returning %p\n", folder);
1543 return ERROR_SUCCESS;
1546 static UINT load_all_folders( MSIPACKAGE *package )
1548 static const WCHAR query[] = {
1549 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1550 '`','D','i','r','e','c','t','o','r','y','`',0 };
1551 MSIQUERY *view;
1552 UINT r;
1554 if (!list_empty(&package->folders))
1555 return ERROR_SUCCESS;
1557 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1558 if (r != ERROR_SUCCESS)
1559 return r;
1561 r = MSI_IterateRecords(view, NULL, load_folder, package);
1562 msiobj_release(&view->hdr);
1563 return r;
1567 * I am not doing any of the costing functionality yet.
1568 * Mostly looking at doing the Component and Feature loading
1570 * The native MSI does A LOT of modification to tables here. Mostly adding
1571 * a lot of temporary columns to the Feature and Component tables.
1573 * note: Native msi also tracks the short filename. But I am only going to
1574 * track the long ones. Also looking at this directory table
1575 * it appears that the directory table does not get the parents
1576 * resolved base on property only based on their entries in the
1577 * directory table.
1579 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1581 static const WCHAR szCosting[] =
1582 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1583 static const WCHAR szZero[] = { '0', 0 };
1585 MSI_SetPropertyW(package, szCosting, szZero);
1586 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1588 load_all_components( package );
1589 load_all_features( package );
1590 load_all_files( package );
1591 load_all_folders( package );
1593 return ERROR_SUCCESS;
1596 static UINT execute_script(MSIPACKAGE *package, UINT script )
1598 int i;
1599 UINT rc = ERROR_SUCCESS;
1601 TRACE("Executing Script %i\n",script);
1603 if (!package->script)
1605 ERR("no script!\n");
1606 return ERROR_FUNCTION_FAILED;
1609 for (i = 0; i < package->script->ActionCount[script]; i++)
1611 LPWSTR action;
1612 action = package->script->Actions[script][i];
1613 ui_actionstart(package, action);
1614 TRACE("Executing Action (%s)\n",debugstr_w(action));
1615 rc = ACTION_PerformAction(package, action, script, TRUE);
1616 if (rc != ERROR_SUCCESS)
1617 break;
1619 msi_free_action_script(package, script);
1620 return rc;
1623 static UINT ACTION_FileCost(MSIPACKAGE *package)
1625 return ERROR_SUCCESS;
1628 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1630 MSICOMPONENT *comp;
1632 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1634 INSTALLSTATE res;
1636 if (!comp->ComponentId)
1637 continue;
1639 res = MsiGetComponentPathW( package->ProductCode,
1640 comp->ComponentId, NULL, NULL);
1641 if (res < 0)
1642 res = INSTALLSTATE_ABSENT;
1643 comp->Installed = res;
1647 /* scan for and update current install states */
1648 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1650 MSICOMPONENT *comp;
1651 MSIFEATURE *feature;
1653 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1655 ComponentList *cl;
1656 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1658 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1660 comp= cl->component;
1662 if (!comp->ComponentId)
1664 res = INSTALLSTATE_ABSENT;
1665 break;
1668 if (res == INSTALLSTATE_ABSENT)
1669 res = comp->Installed;
1670 else
1672 if (res == comp->Installed)
1673 continue;
1675 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1676 res != INSTALLSTATE_SOURCE)
1678 res = INSTALLSTATE_INCOMPLETE;
1682 feature->Installed = res;
1686 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1687 INSTALLSTATE state)
1689 static const WCHAR all[]={'A','L','L',0};
1690 LPWSTR override;
1691 MSIFEATURE *feature;
1693 override = msi_dup_property( package, property );
1694 if (!override)
1695 return FALSE;
1697 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1699 if (strcmpiW(override,all)==0)
1700 msi_feature_set_state( feature, state );
1701 else
1703 LPWSTR ptr = override;
1704 LPWSTR ptr2 = strchrW(override,',');
1706 while (ptr)
1708 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1709 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1711 msi_feature_set_state( feature, state );
1712 break;
1714 if (ptr2)
1716 ptr=ptr2+1;
1717 ptr2 = strchrW(ptr,',');
1719 else
1720 break;
1724 msi_free(override);
1726 return TRUE;
1729 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1731 int install_level;
1732 static const WCHAR szlevel[] =
1733 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1734 static const WCHAR szAddLocal[] =
1735 {'A','D','D','L','O','C','A','L',0};
1736 static const WCHAR szRemove[] =
1737 {'R','E','M','O','V','E',0};
1738 static const WCHAR szReinstall[] =
1739 {'R','E','I','N','S','T','A','L','L',0};
1740 BOOL override = FALSE;
1741 MSICOMPONENT* component;
1742 MSIFEATURE *feature;
1745 /* I do not know if this is where it should happen.. but */
1747 TRACE("Checking Install Level\n");
1749 install_level = msi_get_property_int( package, szlevel, 1 );
1751 /* ok here is the _real_ rub
1752 * all these activation/deactivation things happen in order and things
1753 * later on the list override things earlier on the list.
1754 * 1) INSTALLLEVEL processing
1755 * 2) ADDLOCAL
1756 * 3) REMOVE
1757 * 4) ADDSOURCE
1758 * 5) ADDDEFAULT
1759 * 6) REINSTALL
1760 * 7) COMPADDLOCAL
1761 * 8) COMPADDSOURCE
1762 * 9) FILEADDLOCAL
1763 * 10) FILEADDSOURCE
1764 * 11) FILEADDDEFAULT
1765 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1766 * ignored for all the features. seems strange, especially since it is not
1767 * documented anywhere, but it is how it works.
1769 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1770 * REMOVE are the big ones, since we don't handle administrative installs
1771 * yet anyway.
1773 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1774 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1775 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1777 if (!override)
1779 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1781 BOOL feature_state = ((feature->Level > 0) &&
1782 (feature->Level <= install_level));
1784 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1786 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1787 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1788 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1789 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1790 else
1791 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1795 /* disable child features of unselected parent features */
1796 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1798 FeatureList *fl;
1800 if (feature->Level > 0 && feature->Level <= install_level)
1801 continue;
1803 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1804 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1807 else
1809 /* set the Preselected Property */
1810 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1811 static const WCHAR szOne[] = { '1', 0 };
1813 MSI_SetPropertyW(package,szPreselected,szOne);
1817 * now we want to enable or disable components base on feature
1820 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1822 ComponentList *cl;
1824 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1825 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1827 /* features with components that have compressed files are made local */
1828 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1830 if (cl->component->Enabled &&
1831 cl->component->ForceLocalState &&
1832 feature->Action == INSTALLSTATE_SOURCE)
1834 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1835 break;
1839 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1841 component = cl->component;
1843 if (!component->Enabled)
1844 continue;
1846 switch (feature->Action)
1848 case INSTALLSTATE_ABSENT:
1849 component->anyAbsent = 1;
1850 break;
1851 case INSTALLSTATE_ADVERTISED:
1852 component->hasAdvertiseFeature = 1;
1853 break;
1854 case INSTALLSTATE_SOURCE:
1855 component->hasSourceFeature = 1;
1856 break;
1857 case INSTALLSTATE_LOCAL:
1858 component->hasLocalFeature = 1;
1859 break;
1860 case INSTALLSTATE_DEFAULT:
1861 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1862 component->hasAdvertiseFeature = 1;
1863 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1864 component->hasSourceFeature = 1;
1865 else
1866 component->hasLocalFeature = 1;
1867 break;
1868 default:
1869 break;
1874 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1876 /* if the component isn't enabled, leave it alone */
1877 if (!component->Enabled)
1878 continue;
1880 /* check if it's local or source */
1881 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1882 (component->hasLocalFeature || component->hasSourceFeature))
1884 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1885 !component->ForceLocalState)
1886 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1887 else
1888 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1889 continue;
1892 /* if any feature is local, the component must be local too */
1893 if (component->hasLocalFeature)
1895 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1896 continue;
1899 if (component->hasSourceFeature)
1901 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1902 continue;
1905 if (component->hasAdvertiseFeature)
1907 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1908 continue;
1911 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1912 if (component->anyAbsent)
1913 msi_component_set_state(component, INSTALLSTATE_ABSENT);
1916 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1918 if (component->Action == INSTALLSTATE_DEFAULT)
1920 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1921 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1924 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1925 debugstr_w(component->Component), component->Installed, component->Action);
1929 return ERROR_SUCCESS;
1932 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1934 MSIPACKAGE *package = (MSIPACKAGE*)param;
1935 LPCWSTR name;
1936 LPWSTR path;
1937 MSIFOLDER *f;
1939 name = MSI_RecordGetString(row,1);
1941 f = get_loaded_folder(package, name);
1942 if (!f) return ERROR_SUCCESS;
1944 /* reset the ResolvedTarget */
1945 msi_free(f->ResolvedTarget);
1946 f->ResolvedTarget = NULL;
1948 /* This helper function now does ALL the work */
1949 TRACE("Dir %s ...\n",debugstr_w(name));
1950 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1951 TRACE("resolves to %s\n",debugstr_w(path));
1952 msi_free(path);
1954 return ERROR_SUCCESS;
1957 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1959 MSIPACKAGE *package = (MSIPACKAGE*)param;
1960 LPCWSTR name;
1961 MSIFEATURE *feature;
1963 name = MSI_RecordGetString( row, 1 );
1965 feature = get_loaded_feature( package, name );
1966 if (!feature)
1967 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1968 else
1970 LPCWSTR Condition;
1971 Condition = MSI_RecordGetString(row,3);
1973 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1975 int level = MSI_RecordGetInteger(row,2);
1976 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
1977 feature->Level = level;
1980 return ERROR_SUCCESS;
1983 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1985 static const WCHAR name_fmt[] =
1986 {'%','u','.','%','u','.','%','u','.','%','u',0};
1987 static WCHAR name[] = {'\\',0};
1988 VS_FIXEDFILEINFO *lpVer;
1989 WCHAR filever[0x100];
1990 LPVOID version;
1991 DWORD versize;
1992 DWORD handle;
1993 UINT sz;
1995 TRACE("%s\n", debugstr_w(filename));
1997 versize = GetFileVersionInfoSizeW( filename, &handle );
1998 if (!versize)
1999 return NULL;
2001 version = msi_alloc( versize );
2002 GetFileVersionInfoW( filename, 0, versize, version );
2004 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2006 msi_free( version );
2007 return NULL;
2010 sprintfW( filever, name_fmt,
2011 HIWORD(lpVer->dwFileVersionMS),
2012 LOWORD(lpVer->dwFileVersionMS),
2013 HIWORD(lpVer->dwFileVersionLS),
2014 LOWORD(lpVer->dwFileVersionLS));
2016 msi_free( version );
2018 return strdupW( filever );
2021 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2023 LPWSTR file_version;
2024 MSIFILE *file;
2026 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2028 MSICOMPONENT* comp = file->Component;
2029 LPWSTR p;
2031 if (!comp)
2032 continue;
2034 if (file->IsCompressed)
2035 comp->ForceLocalState = TRUE;
2037 /* calculate target */
2038 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2040 msi_free(file->TargetPath);
2042 TRACE("file %s is named %s\n",
2043 debugstr_w(file->File), debugstr_w(file->FileName));
2045 file->TargetPath = build_directory_name(2, p, file->FileName);
2047 msi_free(p);
2049 TRACE("file %s resolves to %s\n",
2050 debugstr_w(file->File), debugstr_w(file->TargetPath));
2052 /* don't check files of components that aren't installed */
2053 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2054 comp->Installed == INSTALLSTATE_ABSENT)
2056 file->state = msifs_missing; /* assume files are missing */
2057 continue;
2060 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2062 file->state = msifs_missing;
2063 comp->Cost += file->FileSize;
2064 comp->Installed = INSTALLSTATE_INCOMPLETE;
2065 continue;
2068 if (file->Version &&
2069 (file_version = msi_get_disk_file_version( file->TargetPath )))
2071 TRACE("new %s old %s\n", debugstr_w(file->Version),
2072 debugstr_w(file_version));
2073 /* FIXME: seems like a bad way to compare version numbers */
2074 if (lstrcmpiW(file_version, file->Version)<0)
2076 file->state = msifs_overwrite;
2077 comp->Cost += file->FileSize;
2078 comp->Installed = INSTALLSTATE_INCOMPLETE;
2080 else
2081 file->state = msifs_present;
2082 msi_free( file_version );
2084 else
2085 file->state = msifs_present;
2088 return ERROR_SUCCESS;
2092 * A lot is done in this function aside from just the costing.
2093 * The costing needs to be implemented at some point but for now I am going
2094 * to focus on the directory building
2097 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2099 static const WCHAR ExecSeqQuery[] =
2100 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2101 '`','D','i','r','e','c','t','o','r','y','`',0};
2102 static const WCHAR ConditionQuery[] =
2103 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2104 '`','C','o','n','d','i','t','i','o','n','`',0};
2105 static const WCHAR szCosting[] =
2106 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2107 static const WCHAR szlevel[] =
2108 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2109 static const WCHAR szOne[] = { '1', 0 };
2110 MSICOMPONENT *comp;
2111 UINT rc;
2112 MSIQUERY * view;
2113 LPWSTR level;
2115 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2116 return ERROR_SUCCESS;
2118 TRACE("Building Directory properties\n");
2120 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2121 if (rc == ERROR_SUCCESS)
2123 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2124 package);
2125 msiobj_release(&view->hdr);
2128 /* read components states from the registry */
2129 ACTION_GetComponentInstallStates(package);
2131 TRACE("File calculations\n");
2132 msi_check_file_install_states( package );
2134 TRACE("Evaluating Condition Table\n");
2136 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2137 if (rc == ERROR_SUCCESS)
2139 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2140 package);
2141 msiobj_release(&view->hdr);
2144 TRACE("Enabling or Disabling Components\n");
2145 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2147 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2149 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2150 comp->Enabled = FALSE;
2154 MSI_SetPropertyW(package,szCosting,szOne);
2155 /* set default run level if not set */
2156 level = msi_dup_property( package, szlevel );
2157 if (!level)
2158 MSI_SetPropertyW(package,szlevel, szOne);
2159 msi_free(level);
2161 ACTION_UpdateFeatureInstallStates(package);
2163 return MSI_SetFeatureStates(package);
2166 /* OK this value is "interpreted" and then formatted based on the
2167 first few characters */
2168 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2169 DWORD *size)
2171 LPSTR data = NULL;
2172 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2174 if (value[1]=='x')
2176 LPWSTR ptr;
2177 CHAR byte[5];
2178 LPWSTR deformated = NULL;
2179 int count;
2181 deformat_string(package, &value[2], &deformated);
2183 /* binary value type */
2184 ptr = deformated;
2185 *type = REG_BINARY;
2186 if (strlenW(ptr)%2)
2187 *size = (strlenW(ptr)/2)+1;
2188 else
2189 *size = strlenW(ptr)/2;
2191 data = msi_alloc(*size);
2193 byte[0] = '0';
2194 byte[1] = 'x';
2195 byte[4] = 0;
2196 count = 0;
2197 /* if uneven pad with a zero in front */
2198 if (strlenW(ptr)%2)
2200 byte[2]= '0';
2201 byte[3]= *ptr;
2202 ptr++;
2203 data[count] = (BYTE)strtol(byte,NULL,0);
2204 count ++;
2205 TRACE("Uneven byte count\n");
2207 while (*ptr)
2209 byte[2]= *ptr;
2210 ptr++;
2211 byte[3]= *ptr;
2212 ptr++;
2213 data[count] = (BYTE)strtol(byte,NULL,0);
2214 count ++;
2216 msi_free(deformated);
2218 TRACE("Data %i bytes(%i)\n",*size,count);
2220 else
2222 LPWSTR deformated;
2223 LPWSTR p;
2224 DWORD d = 0;
2225 deformat_string(package, &value[1], &deformated);
2227 *type=REG_DWORD;
2228 *size = sizeof(DWORD);
2229 data = msi_alloc(*size);
2230 p = deformated;
2231 if (*p == '-')
2232 p++;
2233 while (*p)
2235 if ( (*p < '0') || (*p > '9') )
2236 break;
2237 d *= 10;
2238 d += (*p - '0');
2239 p++;
2241 if (deformated[0] == '-')
2242 d = -d;
2243 *(LPDWORD)data = d;
2244 TRACE("DWORD %i\n",*(LPDWORD)data);
2246 msi_free(deformated);
2249 else
2251 static const WCHAR szMulti[] = {'[','~',']',0};
2252 LPCWSTR ptr;
2253 *type=REG_SZ;
2255 if (value[0]=='#')
2257 if (value[1]=='%')
2259 ptr = &value[2];
2260 *type=REG_EXPAND_SZ;
2262 else
2263 ptr = &value[1];
2265 else
2266 ptr=value;
2268 if (strstrW(value,szMulti))
2269 *type = REG_MULTI_SZ;
2271 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2273 return data;
2276 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2278 MSIPACKAGE *package = (MSIPACKAGE*)param;
2279 static const WCHAR szHCR[] =
2280 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2281 'R','O','O','T','\\',0};
2282 static const WCHAR szHCU[] =
2283 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2284 'U','S','E','R','\\',0};
2285 static const WCHAR szHLM[] =
2286 {'H','K','E','Y','_','L','O','C','A','L','_',
2287 'M','A','C','H','I','N','E','\\',0};
2288 static const WCHAR szHU[] =
2289 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2291 LPSTR value_data = NULL;
2292 HKEY root_key, hkey;
2293 DWORD type,size;
2294 LPWSTR deformated;
2295 LPCWSTR szRoot, component, name, key, value;
2296 MSICOMPONENT *comp;
2297 MSIRECORD * uirow;
2298 LPWSTR uikey;
2299 INT root;
2300 BOOL check_first = FALSE;
2301 UINT rc;
2303 ui_progress(package,2,0,0,0);
2305 value = NULL;
2306 key = NULL;
2307 uikey = NULL;
2308 name = NULL;
2310 component = MSI_RecordGetString(row, 6);
2311 comp = get_loaded_component(package,component);
2312 if (!comp)
2313 return ERROR_SUCCESS;
2315 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2317 TRACE("Skipping write due to disabled component %s\n",
2318 debugstr_w(component));
2320 comp->Action = comp->Installed;
2322 return ERROR_SUCCESS;
2325 comp->Action = INSTALLSTATE_LOCAL;
2327 name = MSI_RecordGetString(row, 4);
2328 if( MSI_RecordIsNull(row,5) && name )
2330 /* null values can have special meanings */
2331 if (name[0]=='-' && name[1] == 0)
2332 return ERROR_SUCCESS;
2333 else if ((name[0]=='+' && name[1] == 0) ||
2334 (name[0] == '*' && name[1] == 0))
2335 name = NULL;
2336 check_first = TRUE;
2339 root = MSI_RecordGetInteger(row,2);
2340 key = MSI_RecordGetString(row, 3);
2342 /* get the root key */
2343 switch (root)
2345 case -1:
2347 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2348 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2349 if (all_users && all_users[0] == '1')
2351 root_key = HKEY_LOCAL_MACHINE;
2352 szRoot = szHLM;
2354 else
2356 root_key = HKEY_CURRENT_USER;
2357 szRoot = szHCU;
2359 msi_free(all_users);
2361 break;
2362 case 0: root_key = HKEY_CLASSES_ROOT;
2363 szRoot = szHCR;
2364 break;
2365 case 1: root_key = HKEY_CURRENT_USER;
2366 szRoot = szHCU;
2367 break;
2368 case 2: root_key = HKEY_LOCAL_MACHINE;
2369 szRoot = szHLM;
2370 break;
2371 case 3: root_key = HKEY_USERS;
2372 szRoot = szHU;
2373 break;
2374 default:
2375 ERR("Unknown root %i\n",root);
2376 root_key=NULL;
2377 szRoot = NULL;
2378 break;
2380 if (!root_key)
2381 return ERROR_SUCCESS;
2383 deformat_string(package, key , &deformated);
2384 size = strlenW(deformated) + strlenW(szRoot) + 1;
2385 uikey = msi_alloc(size*sizeof(WCHAR));
2386 strcpyW(uikey,szRoot);
2387 strcatW(uikey,deformated);
2389 if (RegCreateKeyW( root_key, deformated, &hkey))
2391 ERR("Could not create key %s\n",debugstr_w(deformated));
2392 msi_free(deformated);
2393 msi_free(uikey);
2394 return ERROR_SUCCESS;
2396 msi_free(deformated);
2398 value = MSI_RecordGetString(row,5);
2399 if (value)
2400 value_data = parse_value(package, value, &type, &size);
2401 else
2403 static const WCHAR szEmpty[] = {0};
2404 value_data = (LPSTR)strdupW(szEmpty);
2405 size = 0;
2406 type = REG_SZ;
2409 deformat_string(package, name, &deformated);
2411 /* get the double nulls to terminate SZ_MULTI */
2412 if (type == REG_MULTI_SZ)
2413 size +=sizeof(WCHAR);
2415 if (!check_first)
2417 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2418 debugstr_w(uikey));
2419 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2421 else
2423 DWORD sz = 0;
2424 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2425 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2427 TRACE("value %s of %s checked already exists\n",
2428 debugstr_w(deformated), debugstr_w(uikey));
2430 else
2432 TRACE("Checked and setting value %s of %s\n",
2433 debugstr_w(deformated), debugstr_w(uikey));
2434 if (deformated || size)
2435 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2438 RegCloseKey(hkey);
2440 uirow = MSI_CreateRecord(3);
2441 MSI_RecordSetStringW(uirow,2,deformated);
2442 MSI_RecordSetStringW(uirow,1,uikey);
2444 if (type == REG_SZ)
2445 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2446 else
2447 MSI_RecordSetStringW(uirow,3,value);
2449 ui_actiondata(package,szWriteRegistryValues,uirow);
2450 msiobj_release( &uirow->hdr );
2452 msi_free(value_data);
2453 msi_free(deformated);
2454 msi_free(uikey);
2456 return ERROR_SUCCESS;
2459 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2461 UINT rc;
2462 MSIQUERY * view;
2463 static const WCHAR ExecSeqQuery[] =
2464 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2465 '`','R','e','g','i','s','t','r','y','`',0 };
2467 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2468 if (rc != ERROR_SUCCESS)
2469 return ERROR_SUCCESS;
2471 /* increment progress bar each time action data is sent */
2472 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2474 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2476 msiobj_release(&view->hdr);
2477 return rc;
2480 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2482 package->script->CurrentlyScripting = TRUE;
2484 return ERROR_SUCCESS;
2488 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2490 MSICOMPONENT *comp;
2491 DWORD progress = 0;
2492 DWORD total = 0;
2493 static const WCHAR q1[]=
2494 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2495 '`','R','e','g','i','s','t','r','y','`',0};
2496 UINT rc;
2497 MSIQUERY * view;
2498 MSIFEATURE *feature;
2499 MSIFILE *file;
2501 TRACE("InstallValidate\n");
2503 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2504 if (rc == ERROR_SUCCESS)
2506 MSI_IterateRecords( view, &progress, NULL, package );
2507 msiobj_release( &view->hdr );
2508 total += progress * REG_PROGRESS_VALUE;
2511 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2512 total += COMPONENT_PROGRESS_VALUE;
2514 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2515 total += file->FileSize;
2517 ui_progress(package,0,total,0,0);
2519 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2521 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2522 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2523 feature->ActionRequest);
2526 return ERROR_SUCCESS;
2529 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2531 MSIPACKAGE* package = (MSIPACKAGE*)param;
2532 LPCWSTR cond = NULL;
2533 LPCWSTR message = NULL;
2534 UINT r;
2536 static const WCHAR title[]=
2537 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2539 cond = MSI_RecordGetString(row,1);
2541 r = MSI_EvaluateConditionW(package,cond);
2542 if (r == MSICONDITION_FALSE)
2544 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2546 LPWSTR deformated;
2547 message = MSI_RecordGetString(row,2);
2548 deformat_string(package,message,&deformated);
2549 MessageBoxW(NULL,deformated,title,MB_OK);
2550 msi_free(deformated);
2553 return ERROR_INSTALL_FAILURE;
2556 return ERROR_SUCCESS;
2559 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2561 UINT rc;
2562 MSIQUERY * view = NULL;
2563 static const WCHAR ExecSeqQuery[] =
2564 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2565 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2567 TRACE("Checking launch conditions\n");
2569 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2570 if (rc != ERROR_SUCCESS)
2571 return ERROR_SUCCESS;
2573 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2574 msiobj_release(&view->hdr);
2576 return rc;
2579 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2582 if (!cmp->KeyPath)
2583 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2585 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2587 MSIRECORD * row = 0;
2588 UINT root,len;
2589 LPWSTR deformated,buffer,deformated_name;
2590 LPCWSTR key,name;
2591 static const WCHAR ExecSeqQuery[] =
2592 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2593 '`','R','e','g','i','s','t','r','y','`',' ',
2594 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2595 ' ','=',' ' ,'\'','%','s','\'',0 };
2596 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2597 static const WCHAR fmt2[]=
2598 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2600 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2601 if (!row)
2602 return NULL;
2604 root = MSI_RecordGetInteger(row,2);
2605 key = MSI_RecordGetString(row, 3);
2606 name = MSI_RecordGetString(row, 4);
2607 deformat_string(package, key , &deformated);
2608 deformat_string(package, name, &deformated_name);
2610 len = strlenW(deformated) + 6;
2611 if (deformated_name)
2612 len+=strlenW(deformated_name);
2614 buffer = msi_alloc( len *sizeof(WCHAR));
2616 if (deformated_name)
2617 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2618 else
2619 sprintfW(buffer,fmt,root,deformated);
2621 msi_free(deformated);
2622 msi_free(deformated_name);
2623 msiobj_release(&row->hdr);
2625 return buffer;
2627 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2629 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2630 return NULL;
2632 else
2634 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2636 if (file)
2637 return strdupW( file->TargetPath );
2639 return NULL;
2642 static HKEY openSharedDLLsKey(void)
2644 HKEY hkey=0;
2645 static const WCHAR path[] =
2646 {'S','o','f','t','w','a','r','e','\\',
2647 'M','i','c','r','o','s','o','f','t','\\',
2648 'W','i','n','d','o','w','s','\\',
2649 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2650 'S','h','a','r','e','d','D','L','L','s',0};
2652 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2653 return hkey;
2656 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2658 HKEY hkey;
2659 DWORD count=0;
2660 DWORD type;
2661 DWORD sz = sizeof(count);
2662 DWORD rc;
2664 hkey = openSharedDLLsKey();
2665 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2666 if (rc != ERROR_SUCCESS)
2667 count = 0;
2668 RegCloseKey(hkey);
2669 return count;
2672 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2674 HKEY hkey;
2676 hkey = openSharedDLLsKey();
2677 if (count > 0)
2678 msi_reg_set_val_dword( hkey, path, count );
2679 else
2680 RegDeleteValueW(hkey,path);
2681 RegCloseKey(hkey);
2682 return count;
2686 * Return TRUE if the count should be written out and FALSE if not
2688 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2690 MSIFEATURE *feature;
2691 INT count = 0;
2692 BOOL write = FALSE;
2694 /* only refcount DLLs */
2695 if (comp->KeyPath == NULL ||
2696 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2697 comp->Attributes & msidbComponentAttributesODBCDataSource)
2698 write = FALSE;
2699 else
2701 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2702 write = (count > 0);
2704 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2705 write = TRUE;
2708 /* increment counts */
2709 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2711 ComponentList *cl;
2713 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2714 continue;
2716 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2718 if ( cl->component == comp )
2719 count++;
2723 /* decrement counts */
2724 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2726 ComponentList *cl;
2728 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2729 continue;
2731 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2733 if ( cl->component == comp )
2734 count--;
2738 /* ref count all the files in the component */
2739 if (write)
2741 MSIFILE *file;
2743 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2745 if (file->Component == comp)
2746 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2750 /* add a count for permenent */
2751 if (comp->Attributes & msidbComponentAttributesPermanent)
2752 count ++;
2754 comp->RefCount = count;
2756 if (write)
2757 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2761 * Ok further analysis makes me think that this work is
2762 * actually done in the PublishComponents and PublishFeatures
2763 * step, and not here. It appears like the keypath and all that is
2764 * resolved in this step, however actually written in the Publish steps.
2765 * But we will leave it here for now because it is unclear
2767 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2769 WCHAR squished_pc[GUID_SIZE];
2770 WCHAR squished_cc[GUID_SIZE];
2771 UINT rc;
2772 MSICOMPONENT *comp;
2773 HKEY hkey=0,hkey2=0;
2775 TRACE("\n");
2777 /* writes the Component and Features values to the registry */
2779 rc = MSIREG_OpenComponents(&hkey);
2780 if (rc != ERROR_SUCCESS)
2781 return rc;
2783 squash_guid(package->ProductCode,squished_pc);
2784 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2786 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2788 MSIRECORD * uirow;
2790 ui_progress(package,2,0,0,0);
2791 if (!comp->ComponentId)
2792 continue;
2794 squash_guid(comp->ComponentId,squished_cc);
2796 msi_free(comp->FullKeypath);
2797 comp->FullKeypath = resolve_keypath( package, comp );
2799 /* do the refcounting */
2800 ACTION_RefCountComponent( package, comp );
2802 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2803 debugstr_w(comp->Component),
2804 debugstr_w(squished_cc),
2805 debugstr_w(comp->FullKeypath),
2806 comp->RefCount);
2808 * Write the keypath out if the component is to be registered
2809 * and delete the key if the component is to be deregistered
2811 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2813 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2814 if (rc != ERROR_SUCCESS)
2815 continue;
2817 if (!comp->FullKeypath)
2818 continue;
2820 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2822 if (comp->Attributes & msidbComponentAttributesPermanent)
2824 static const WCHAR szPermKey[] =
2825 { '0','0','0','0','0','0','0','0','0','0','0','0',
2826 '0','0','0','0','0','0','0','0','0','0','0','0',
2827 '0','0','0','0','0','0','0','0',0 };
2829 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2832 RegCloseKey(hkey2);
2834 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey2, TRUE);
2835 if (rc != ERROR_SUCCESS)
2836 continue;
2838 msi_reg_set_val_str(hkey2, squished_pc, comp->FullKeypath);
2839 RegCloseKey(hkey2);
2841 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2843 DWORD res;
2845 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2846 if (rc != ERROR_SUCCESS)
2847 continue;
2849 RegDeleteValueW(hkey2,squished_pc);
2851 /* if the key is empty delete it */
2852 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2853 RegCloseKey(hkey2);
2854 if (res == ERROR_NO_MORE_ITEMS)
2855 RegDeleteKeyW(hkey,squished_cc);
2857 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2860 /* UI stuff */
2861 uirow = MSI_CreateRecord(3);
2862 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2863 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2864 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2865 ui_actiondata(package,szProcessComponents,uirow);
2866 msiobj_release( &uirow->hdr );
2868 RegCloseKey(hkey);
2869 return rc;
2872 typedef struct {
2873 CLSID clsid;
2874 LPWSTR source;
2876 LPWSTR path;
2877 ITypeLib *ptLib;
2878 } typelib_struct;
2880 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2881 LPWSTR lpszName, LONG_PTR lParam)
2883 TLIBATTR *attr;
2884 typelib_struct *tl_struct = (typelib_struct*) lParam;
2885 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2886 int sz;
2887 HRESULT res;
2889 if (!IS_INTRESOURCE(lpszName))
2891 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2892 return TRUE;
2895 sz = strlenW(tl_struct->source)+4;
2896 sz *= sizeof(WCHAR);
2898 if ((INT_PTR)lpszName == 1)
2899 tl_struct->path = strdupW(tl_struct->source);
2900 else
2902 tl_struct->path = msi_alloc(sz);
2903 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2906 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2907 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2908 if (!SUCCEEDED(res))
2910 msi_free(tl_struct->path);
2911 tl_struct->path = NULL;
2913 return TRUE;
2916 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2917 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2919 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2920 return FALSE;
2923 msi_free(tl_struct->path);
2924 tl_struct->path = NULL;
2926 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2927 ITypeLib_Release(tl_struct->ptLib);
2929 return TRUE;
2932 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2934 MSIPACKAGE* package = (MSIPACKAGE*)param;
2935 LPCWSTR component;
2936 MSICOMPONENT *comp;
2937 MSIFILE *file;
2938 typelib_struct tl_struct;
2939 HMODULE module;
2940 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2942 component = MSI_RecordGetString(row,3);
2943 comp = get_loaded_component(package,component);
2944 if (!comp)
2945 return ERROR_SUCCESS;
2947 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2949 TRACE("Skipping typelib reg due to disabled component\n");
2951 comp->Action = comp->Installed;
2953 return ERROR_SUCCESS;
2956 comp->Action = INSTALLSTATE_LOCAL;
2958 file = get_loaded_file( package, comp->KeyPath );
2959 if (!file)
2960 return ERROR_SUCCESS;
2962 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2963 if (module)
2965 LPCWSTR guid;
2966 guid = MSI_RecordGetString(row,1);
2967 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2968 tl_struct.source = strdupW( file->TargetPath );
2969 tl_struct.path = NULL;
2971 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2972 (LONG_PTR)&tl_struct);
2974 if (tl_struct.path)
2976 LPWSTR help = NULL;
2977 LPCWSTR helpid;
2978 HRESULT res;
2980 helpid = MSI_RecordGetString(row,6);
2982 if (helpid)
2983 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2984 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2985 msi_free(help);
2987 if (!SUCCEEDED(res))
2988 ERR("Failed to register type library %s\n",
2989 debugstr_w(tl_struct.path));
2990 else
2992 ui_actiondata(package,szRegisterTypeLibraries,row);
2994 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2997 ITypeLib_Release(tl_struct.ptLib);
2998 msi_free(tl_struct.path);
3000 else
3001 ERR("Failed to load type library %s\n",
3002 debugstr_w(tl_struct.source));
3004 FreeLibrary(module);
3005 msi_free(tl_struct.source);
3007 else
3008 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3010 return ERROR_SUCCESS;
3013 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3016 * OK this is a bit confusing.. I am given a _Component key and I believe
3017 * that the file that is being registered as a type library is the "key file
3018 * of that component" which I interpret to mean "The file in the KeyPath of
3019 * that component".
3021 UINT rc;
3022 MSIQUERY * view;
3023 static const WCHAR Query[] =
3024 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3025 '`','T','y','p','e','L','i','b','`',0};
3027 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3028 if (rc != ERROR_SUCCESS)
3029 return ERROR_SUCCESS;
3031 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3032 msiobj_release(&view->hdr);
3033 return rc;
3036 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3038 MSIPACKAGE *package = (MSIPACKAGE*)param;
3039 LPWSTR target_file, target_folder, filename;
3040 LPCWSTR buffer, extension;
3041 MSICOMPONENT *comp;
3042 static const WCHAR szlnk[]={'.','l','n','k',0};
3043 IShellLinkW *sl = NULL;
3044 IPersistFile *pf = NULL;
3045 HRESULT res;
3047 buffer = MSI_RecordGetString(row,4);
3048 comp = get_loaded_component(package,buffer);
3049 if (!comp)
3050 return ERROR_SUCCESS;
3052 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3054 TRACE("Skipping shortcut creation due to disabled component\n");
3056 comp->Action = comp->Installed;
3058 return ERROR_SUCCESS;
3061 comp->Action = INSTALLSTATE_LOCAL;
3063 ui_actiondata(package,szCreateShortcuts,row);
3065 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3066 &IID_IShellLinkW, (LPVOID *) &sl );
3068 if (FAILED( res ))
3070 ERR("CLSID_ShellLink not available\n");
3071 goto err;
3074 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3075 if (FAILED( res ))
3077 ERR("QueryInterface(IID_IPersistFile) failed\n");
3078 goto err;
3081 buffer = MSI_RecordGetString(row,2);
3082 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3084 /* may be needed because of a bug somehwere else */
3085 create_full_pathW(target_folder);
3087 filename = msi_dup_record_field( row, 3 );
3088 reduce_to_longfilename(filename);
3090 extension = strchrW(filename,'.');
3091 if (!extension || strcmpiW(extension,szlnk))
3093 int len = strlenW(filename);
3094 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3095 memcpy(filename + len, szlnk, sizeof(szlnk));
3097 target_file = build_directory_name(2, target_folder, filename);
3098 msi_free(target_folder);
3099 msi_free(filename);
3101 buffer = MSI_RecordGetString(row,5);
3102 if (strchrW(buffer,'['))
3104 LPWSTR deformated;
3105 deformat_string(package,buffer,&deformated);
3106 IShellLinkW_SetPath(sl,deformated);
3107 msi_free(deformated);
3109 else
3111 FIXME("poorly handled shortcut format, advertised shortcut\n");
3112 IShellLinkW_SetPath(sl,comp->FullKeypath);
3115 if (!MSI_RecordIsNull(row,6))
3117 LPWSTR deformated;
3118 buffer = MSI_RecordGetString(row,6);
3119 deformat_string(package,buffer,&deformated);
3120 IShellLinkW_SetArguments(sl,deformated);
3121 msi_free(deformated);
3124 if (!MSI_RecordIsNull(row,7))
3126 buffer = MSI_RecordGetString(row,7);
3127 IShellLinkW_SetDescription(sl,buffer);
3130 if (!MSI_RecordIsNull(row,8))
3131 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3133 if (!MSI_RecordIsNull(row,9))
3135 LPWSTR Path;
3136 INT index;
3138 buffer = MSI_RecordGetString(row,9);
3140 Path = build_icon_path(package,buffer);
3141 index = MSI_RecordGetInteger(row,10);
3143 /* no value means 0 */
3144 if (index == MSI_NULL_INTEGER)
3145 index = 0;
3147 IShellLinkW_SetIconLocation(sl,Path,index);
3148 msi_free(Path);
3151 if (!MSI_RecordIsNull(row,11))
3152 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3154 if (!MSI_RecordIsNull(row,12))
3156 LPWSTR Path;
3157 buffer = MSI_RecordGetString(row,12);
3158 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3159 if (Path)
3160 IShellLinkW_SetWorkingDirectory(sl,Path);
3161 msi_free(Path);
3164 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3165 IPersistFile_Save(pf,target_file,FALSE);
3167 msi_free(target_file);
3169 err:
3170 if (pf)
3171 IPersistFile_Release( pf );
3172 if (sl)
3173 IShellLinkW_Release( sl );
3175 return ERROR_SUCCESS;
3178 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3180 UINT rc;
3181 HRESULT res;
3182 MSIQUERY * view;
3183 static const WCHAR Query[] =
3184 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3185 '`','S','h','o','r','t','c','u','t','`',0};
3187 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3188 if (rc != ERROR_SUCCESS)
3189 return ERROR_SUCCESS;
3191 res = CoInitialize( NULL );
3192 if (FAILED (res))
3194 ERR("CoInitialize failed\n");
3195 return ERROR_FUNCTION_FAILED;
3198 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3199 msiobj_release(&view->hdr);
3201 CoUninitialize();
3203 return rc;
3206 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3208 MSIPACKAGE* package = (MSIPACKAGE*)param;
3209 HANDLE the_file;
3210 LPWSTR FilePath;
3211 LPCWSTR FileName;
3212 CHAR buffer[1024];
3213 DWORD sz;
3214 UINT rc;
3215 MSIRECORD *uirow;
3217 FileName = MSI_RecordGetString(row,1);
3218 if (!FileName)
3220 ERR("Unable to get FileName\n");
3221 return ERROR_SUCCESS;
3224 FilePath = build_icon_path(package,FileName);
3226 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3228 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3229 FILE_ATTRIBUTE_NORMAL, NULL);
3231 if (the_file == INVALID_HANDLE_VALUE)
3233 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3234 msi_free(FilePath);
3235 return ERROR_SUCCESS;
3240 DWORD write;
3241 sz = 1024;
3242 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3243 if (rc != ERROR_SUCCESS)
3245 ERR("Failed to get stream\n");
3246 CloseHandle(the_file);
3247 DeleteFileW(FilePath);
3248 break;
3250 WriteFile(the_file,buffer,sz,&write,NULL);
3251 } while (sz == 1024);
3253 msi_free(FilePath);
3255 CloseHandle(the_file);
3257 uirow = MSI_CreateRecord(1);
3258 MSI_RecordSetStringW(uirow,1,FileName);
3259 ui_actiondata(package,szPublishProduct,uirow);
3260 msiobj_release( &uirow->hdr );
3262 return ERROR_SUCCESS;
3265 static BOOL msi_check_publish(MSIPACKAGE *package)
3267 MSIFEATURE *feature;
3269 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3271 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3272 return TRUE;
3275 return FALSE;
3279 * 99% of the work done here is only done for
3280 * advertised installs. However this is where the
3281 * Icon table is processed and written out
3282 * so that is what I am going to do here.
3284 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3286 UINT rc;
3287 MSIQUERY * view;
3288 MSISOURCELISTINFO *info;
3289 MSIMEDIADISK *disk;
3290 static const WCHAR Query[]=
3291 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3292 '`','I','c','o','n','`',0};
3293 /* for registry stuff */
3294 HKEY hkey=0;
3295 HKEY hukey=0;
3296 HKEY hudkey=0, props=0;
3297 static const WCHAR szProductLanguage[] =
3298 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3299 static const WCHAR szARPProductIcon[] =
3300 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3301 static const WCHAR szProductVersion[] =
3302 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3303 DWORD langid;
3304 LPWSTR buffer;
3305 DWORD size;
3306 MSIHANDLE hDb, hSumInfo;
3308 /* FIXME: also need to publish if the product is in advertise mode */
3309 if (!msi_check_publish(package))
3310 return ERROR_SUCCESS;
3312 /* write out icon files */
3314 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3315 if (rc == ERROR_SUCCESS)
3317 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3318 msiobj_release(&view->hdr);
3321 /* ok there is a lot more done here but i need to figure out what */
3323 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3324 if (rc != ERROR_SUCCESS)
3325 goto end;
3327 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3328 if (rc != ERROR_SUCCESS)
3329 goto end;
3331 rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3332 if (rc != ERROR_SUCCESS)
3333 goto end;
3335 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode,&props,TRUE);
3336 if (rc != ERROR_SUCCESS)
3337 goto end;
3339 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3340 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3341 msi_free(buffer);
3343 langid = msi_get_property_int( package, szProductLanguage, 0 );
3344 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3346 buffer = msi_dup_property( package, szARPProductIcon );
3347 if (buffer)
3349 LPWSTR path = build_icon_path(package,buffer);
3350 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3351 msi_free( path );
3353 msi_free(buffer);
3355 buffer = msi_dup_property( package, szProductVersion );
3356 if (buffer)
3358 DWORD verdword = msi_version_str_to_dword(buffer);
3359 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3361 msi_free(buffer);
3363 /* FIXME: Need to write more keys to the user registry */
3365 hDb= alloc_msihandle( &package->db->hdr );
3366 if (!hDb) {
3367 rc = ERROR_NOT_ENOUGH_MEMORY;
3368 goto end;
3370 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3371 MsiCloseHandle(hDb);
3372 if (rc == ERROR_SUCCESS)
3374 WCHAR guidbuffer[0x200];
3375 size = 0x200;
3376 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3377 guidbuffer, &size);
3378 if (rc == ERROR_SUCCESS)
3380 WCHAR squashed[GUID_SIZE];
3381 /* for now we only care about the first guid */
3382 LPWSTR ptr = strchrW(guidbuffer,';');
3383 if (ptr) *ptr = 0;
3384 squash_guid(guidbuffer,squashed);
3385 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3387 else
3389 ERR("Unable to query Revision_Number...\n");
3390 rc = ERROR_SUCCESS;
3392 MsiCloseHandle(hSumInfo);
3394 else
3396 ERR("Unable to open Summary Information\n");
3397 rc = ERROR_SUCCESS;
3400 /* publish the SourceList info */
3401 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3403 MsiSourceListSetInfoW(package->ProductCode, NULL,
3404 info->context, info->options,
3405 info->property, info->value);
3408 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3410 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3411 disk->context, disk->options,
3412 disk->disk_id, disk->volume_label, disk->disk_prompt);
3415 end:
3416 RegCloseKey(hkey);
3417 RegCloseKey(hukey);
3418 RegCloseKey(hudkey);
3419 RegCloseKey(props);
3421 return rc;
3424 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3426 MSIPACKAGE *package = (MSIPACKAGE*)param;
3427 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3428 LPWSTR deformated_section, deformated_key, deformated_value;
3429 LPWSTR folder, fullname = NULL;
3430 MSIRECORD * uirow;
3431 INT action;
3432 MSICOMPONENT *comp;
3433 static const WCHAR szWindowsFolder[] =
3434 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3436 component = MSI_RecordGetString(row, 8);
3437 comp = get_loaded_component(package,component);
3439 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3441 TRACE("Skipping ini file due to disabled component %s\n",
3442 debugstr_w(component));
3444 comp->Action = comp->Installed;
3446 return ERROR_SUCCESS;
3449 comp->Action = INSTALLSTATE_LOCAL;
3451 identifier = MSI_RecordGetString(row,1);
3452 filename = MSI_RecordGetString(row,2);
3453 dirproperty = MSI_RecordGetString(row,3);
3454 section = MSI_RecordGetString(row,4);
3455 key = MSI_RecordGetString(row,5);
3456 value = MSI_RecordGetString(row,6);
3457 action = MSI_RecordGetInteger(row,7);
3459 deformat_string(package,section,&deformated_section);
3460 deformat_string(package,key,&deformated_key);
3461 deformat_string(package,value,&deformated_value);
3463 if (dirproperty)
3465 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3466 if (!folder)
3467 folder = msi_dup_property( package, dirproperty );
3469 else
3470 folder = msi_dup_property( package, szWindowsFolder );
3472 if (!folder)
3474 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3475 goto cleanup;
3478 fullname = build_directory_name(2, folder, filename);
3480 if (action == 0)
3482 TRACE("Adding value %s to section %s in %s\n",
3483 debugstr_w(deformated_key), debugstr_w(deformated_section),
3484 debugstr_w(fullname));
3485 WritePrivateProfileStringW(deformated_section, deformated_key,
3486 deformated_value, fullname);
3488 else if (action == 1)
3490 WCHAR returned[10];
3491 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3492 returned, 10, fullname);
3493 if (returned[0] == 0)
3495 TRACE("Adding value %s to section %s in %s\n",
3496 debugstr_w(deformated_key), debugstr_w(deformated_section),
3497 debugstr_w(fullname));
3499 WritePrivateProfileStringW(deformated_section, deformated_key,
3500 deformated_value, fullname);
3503 else if (action == 3)
3504 FIXME("Append to existing section not yet implemented\n");
3506 uirow = MSI_CreateRecord(4);
3507 MSI_RecordSetStringW(uirow,1,identifier);
3508 MSI_RecordSetStringW(uirow,2,deformated_section);
3509 MSI_RecordSetStringW(uirow,3,deformated_key);
3510 MSI_RecordSetStringW(uirow,4,deformated_value);
3511 ui_actiondata(package,szWriteIniValues,uirow);
3512 msiobj_release( &uirow->hdr );
3513 cleanup:
3514 msi_free(fullname);
3515 msi_free(folder);
3516 msi_free(deformated_key);
3517 msi_free(deformated_value);
3518 msi_free(deformated_section);
3519 return ERROR_SUCCESS;
3522 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3524 UINT rc;
3525 MSIQUERY * view;
3526 static const WCHAR ExecSeqQuery[] =
3527 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3528 '`','I','n','i','F','i','l','e','`',0};
3530 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3531 if (rc != ERROR_SUCCESS)
3533 TRACE("no IniFile table\n");
3534 return ERROR_SUCCESS;
3537 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3538 msiobj_release(&view->hdr);
3539 return rc;
3542 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3544 MSIPACKAGE *package = (MSIPACKAGE*)param;
3545 LPCWSTR filename;
3546 LPWSTR FullName;
3547 MSIFILE *file;
3548 DWORD len;
3549 static const WCHAR ExeStr[] =
3550 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3551 static const WCHAR close[] = {'\"',0};
3552 STARTUPINFOW si;
3553 PROCESS_INFORMATION info;
3554 BOOL brc;
3555 MSIRECORD *uirow;
3556 LPWSTR uipath, p;
3558 memset(&si,0,sizeof(STARTUPINFOW));
3560 filename = MSI_RecordGetString(row,1);
3561 file = get_loaded_file( package, filename );
3563 if (!file)
3565 ERR("Unable to find file id %s\n",debugstr_w(filename));
3566 return ERROR_SUCCESS;
3569 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3571 FullName = msi_alloc(len*sizeof(WCHAR));
3572 strcpyW(FullName,ExeStr);
3573 strcatW( FullName, file->TargetPath );
3574 strcatW(FullName,close);
3576 TRACE("Registering %s\n",debugstr_w(FullName));
3577 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3578 &si, &info);
3580 if (brc)
3581 msi_dialog_check_messages(info.hProcess);
3583 msi_free(FullName);
3585 /* the UI chunk */
3586 uirow = MSI_CreateRecord( 2 );
3587 uipath = strdupW( file->TargetPath );
3588 p = strrchrW(uipath,'\\');
3589 if (p)
3590 p[0]=0;
3591 MSI_RecordSetStringW( uirow, 1, &p[1] );
3592 MSI_RecordSetStringW( uirow, 2, uipath);
3593 ui_actiondata( package, szSelfRegModules, uirow);
3594 msiobj_release( &uirow->hdr );
3595 msi_free( uipath );
3596 /* FIXME: call ui_progress? */
3598 return ERROR_SUCCESS;
3601 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3603 UINT rc;
3604 MSIQUERY * view;
3605 static const WCHAR ExecSeqQuery[] =
3606 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3607 '`','S','e','l','f','R','e','g','`',0};
3609 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3610 if (rc != ERROR_SUCCESS)
3612 TRACE("no SelfReg table\n");
3613 return ERROR_SUCCESS;
3616 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3617 msiobj_release(&view->hdr);
3619 return ERROR_SUCCESS;
3622 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3624 MSIFEATURE *feature;
3625 UINT rc;
3626 HKEY hkey=0;
3627 HKEY hukey=0;
3628 HKEY userdata=0;
3630 if (!msi_check_publish(package))
3631 return ERROR_SUCCESS;
3633 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3634 if (rc != ERROR_SUCCESS)
3635 goto end;
3637 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3638 if (rc != ERROR_SUCCESS)
3639 goto end;
3641 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &userdata, TRUE);
3642 if (rc != ERROR_SUCCESS)
3643 goto end;
3645 /* here the guids are base 85 encoded */
3646 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3648 ComponentList *cl;
3649 LPWSTR data = NULL;
3650 GUID clsid;
3651 INT size;
3652 BOOL absent = FALSE;
3653 MSIRECORD *uirow;
3655 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3656 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3657 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3658 absent = TRUE;
3660 size = 1;
3661 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3663 size += 21;
3665 if (feature->Feature_Parent)
3666 size += strlenW( feature->Feature_Parent )+2;
3668 data = msi_alloc(size * sizeof(WCHAR));
3670 data[0] = 0;
3671 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3673 MSICOMPONENT* component = cl->component;
3674 WCHAR buf[21];
3676 buf[0] = 0;
3677 if (component->ComponentId)
3679 TRACE("From %s\n",debugstr_w(component->ComponentId));
3680 CLSIDFromString(component->ComponentId, &clsid);
3681 encode_base85_guid(&clsid,buf);
3682 TRACE("to %s\n",debugstr_w(buf));
3683 strcatW(data,buf);
3687 if (feature->Feature_Parent)
3689 static const WCHAR sep[] = {'\2',0};
3690 strcatW(data,sep);
3691 strcatW(data,feature->Feature_Parent);
3694 msi_reg_set_val_str( hkey, feature->Feature, data );
3695 msi_reg_set_val_str( userdata, feature->Feature, data );
3696 msi_free(data);
3698 size = 0;
3699 if (feature->Feature_Parent)
3700 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3701 if (!absent)
3703 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3704 (LPBYTE)feature->Feature_Parent,size);
3706 else
3708 size += 2*sizeof(WCHAR);
3709 data = msi_alloc(size);
3710 data[0] = 0x6;
3711 data[1] = 0;
3712 if (feature->Feature_Parent)
3713 strcpyW( &data[1], feature->Feature_Parent );
3714 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3715 (LPBYTE)data,size);
3716 msi_free(data);
3719 /* the UI chunk */
3720 uirow = MSI_CreateRecord( 1 );
3721 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3722 ui_actiondata( package, szPublishFeatures, uirow);
3723 msiobj_release( &uirow->hdr );
3724 /* FIXME: call ui_progress? */
3727 end:
3728 RegCloseKey(hkey);
3729 RegCloseKey(hukey);
3730 return rc;
3733 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3735 UINT r;
3736 HKEY hkey;
3738 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3740 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3741 if (r == ERROR_SUCCESS)
3743 RegDeleteValueW(hkey, feature->Feature);
3744 RegCloseKey(hkey);
3747 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3748 if (r == ERROR_SUCCESS)
3750 RegDeleteValueW(hkey, feature->Feature);
3751 RegCloseKey(hkey);
3754 return ERROR_SUCCESS;
3757 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3759 MSIFEATURE *feature;
3761 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3763 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3764 return FALSE;
3767 return TRUE;
3770 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3772 MSIFEATURE *feature;
3774 if (!msi_check_unpublish(package))
3775 return ERROR_SUCCESS;
3777 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3779 msi_unpublish_feature(package, feature);
3782 return ERROR_SUCCESS;
3785 static UINT msi_get_local_package_name( LPWSTR path )
3787 static const WCHAR szInstaller[] = {
3788 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3789 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3790 DWORD time, len, i;
3791 HANDLE handle;
3793 time = GetTickCount();
3794 GetWindowsDirectoryW( path, MAX_PATH );
3795 lstrcatW( path, szInstaller );
3796 CreateDirectoryW( path, NULL );
3798 len = lstrlenW(path);
3799 for (i=0; i<0x10000; i++)
3801 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3802 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3803 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3804 if (handle != INVALID_HANDLE_VALUE)
3806 CloseHandle(handle);
3807 break;
3809 if (GetLastError() != ERROR_FILE_EXISTS &&
3810 GetLastError() != ERROR_SHARING_VIOLATION)
3811 return ERROR_FUNCTION_FAILED;
3814 return ERROR_SUCCESS;
3817 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3819 static const WCHAR szOriginalDatabase[] =
3820 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3821 WCHAR packagefile[MAX_PATH];
3822 LPWSTR msiFilePath;
3823 HKEY props;
3824 UINT r;
3826 r = msi_get_local_package_name( packagefile );
3827 if (r != ERROR_SUCCESS)
3828 return r;
3830 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3832 msiFilePath = msi_dup_property( package, szOriginalDatabase );
3833 r = CopyFileW( msiFilePath, packagefile, FALSE);
3835 if (!r)
3837 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3838 debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3839 msi_free( msiFilePath );
3840 return ERROR_FUNCTION_FAILED;
3842 msi_free( msiFilePath );
3844 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3846 r = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3847 if (r != ERROR_SUCCESS)
3848 return r;
3850 msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3851 RegCloseKey(props);
3852 return ERROR_SUCCESS;
3855 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3857 LPWSTR prop, val, key;
3858 static const LPCSTR propval[] = {
3859 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3860 "ARPCONTACT", "Contact",
3861 "ARPCOMMENTS", "Comments",
3862 "ProductName", "DisplayName",
3863 "ProductVersion", "DisplayVersion",
3864 "ARPHELPLINK", "HelpLink",
3865 "ARPHELPTELEPHONE", "HelpTelephone",
3866 "ARPINSTALLLOCATION", "InstallLocation",
3867 "SourceDir", "InstallSource",
3868 "Manufacturer", "Publisher",
3869 "ARPREADME", "Readme",
3870 "ARPSIZE", "Size",
3871 "ARPURLINFOABOUT", "URLInfoAbout",
3872 "ARPURLUPDATEINFO", "URLUpdateInfo",
3873 NULL,
3875 const LPCSTR *p = propval;
3877 while( *p )
3879 prop = strdupAtoW( *p++ );
3880 key = strdupAtoW( *p++ );
3881 val = msi_dup_property( package, prop );
3882 msi_reg_set_val_str( hkey, key, val );
3883 msi_free(val);
3884 msi_free(key);
3885 msi_free(prop);
3887 return ERROR_SUCCESS;
3890 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3892 HKEY hkey=0;
3893 HKEY hudkey=0, props=0;
3894 LPWSTR buffer = NULL;
3895 UINT rc;
3896 DWORD size, langid;
3897 static const WCHAR szWindowsInstaller[] =
3898 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3899 static const WCHAR szUpgradeCode[] =
3900 {'U','p','g','r','a','d','e','C','o','d','e',0};
3901 static const WCHAR modpath_fmt[] =
3902 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3903 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3904 static const WCHAR szModifyPath[] =
3905 {'M','o','d','i','f','y','P','a','t','h',0};
3906 static const WCHAR szUninstallString[] =
3907 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3908 static const WCHAR szEstimatedSize[] =
3909 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3910 static const WCHAR szProductLanguage[] =
3911 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3912 static const WCHAR szProductVersion[] =
3913 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3915 SYSTEMTIME systime;
3916 static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3917 LPWSTR upgrade_code;
3918 WCHAR szDate[9];
3920 /* FIXME: also need to publish if the product is in advertise mode */
3921 if (!msi_check_publish(package))
3922 return ERROR_SUCCESS;
3924 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3925 if (rc != ERROR_SUCCESS)
3926 return rc;
3928 /* dump all the info i can grab */
3929 /* FIXME: Flesh out more information */
3931 msi_write_uninstall_property_vals( package, hkey );
3933 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3935 msi_make_package_local( package, hkey );
3937 /* do ModifyPath and UninstallString */
3938 size = deformat_string(package,modpath_fmt,&buffer);
3939 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3940 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3941 msi_free(buffer);
3943 /* FIXME: Write real Estimated Size when we have it */
3944 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3946 GetLocalTime(&systime);
3947 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3948 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3950 langid = msi_get_property_int( package, szProductLanguage, 0 );
3951 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3953 buffer = msi_dup_property( package, szProductVersion );
3954 if (buffer)
3956 DWORD verdword = msi_version_str_to_dword(buffer);
3958 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3959 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3960 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3962 msi_free(buffer);
3964 /* Handle Upgrade Codes */
3965 upgrade_code = msi_dup_property( package, szUpgradeCode );
3966 if (upgrade_code)
3968 HKEY hkey2;
3969 WCHAR squashed[33];
3970 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3971 squash_guid(package->ProductCode,squashed);
3972 msi_reg_set_val_str( hkey2, squashed, NULL );
3973 RegCloseKey(hkey2);
3974 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3975 squash_guid(package->ProductCode,squashed);
3976 msi_reg_set_val_str( hkey2, squashed, NULL );
3977 RegCloseKey(hkey2);
3979 msi_free(upgrade_code);
3982 RegCloseKey(hkey);
3984 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3985 if (rc != ERROR_SUCCESS)
3986 return rc;
3988 RegCloseKey(hudkey);
3990 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3991 if (rc != ERROR_SUCCESS)
3992 return rc;
3994 msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
3995 RegCloseKey(props);
3997 return ERROR_SUCCESS;
4000 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4002 return execute_script(package,INSTALL_SCRIPT);
4005 static UINT msi_unpublish_product(MSIPACKAGE *package)
4007 LPWSTR remove = NULL;
4008 LPWSTR *features = NULL;
4009 BOOL full_uninstall = TRUE;
4010 MSIFEATURE *feature;
4012 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4013 static const WCHAR szAll[] = {'A','L','L',0};
4015 remove = msi_dup_property(package, szRemove);
4016 if (!remove)
4017 return ERROR_SUCCESS;
4019 features = msi_split_string(remove, ',');
4020 if (!features)
4022 msi_free(remove);
4023 ERR("REMOVE feature list is empty!\n");
4024 return ERROR_FUNCTION_FAILED;
4027 if (!lstrcmpW(features[0], szAll))
4028 full_uninstall = TRUE;
4029 else
4031 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4033 if (feature->Action != INSTALLSTATE_ABSENT)
4034 full_uninstall = FALSE;
4038 if (!full_uninstall)
4039 goto done;
4041 MSIREG_DeleteProductKey(package->ProductCode);
4042 MSIREG_DeleteUserProductKey(package->ProductCode);
4043 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4044 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4046 done:
4047 msi_free(remove);
4048 msi_free(features);
4049 return ERROR_SUCCESS;
4052 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4054 UINT rc;
4056 rc = msi_unpublish_product(package);
4057 if (rc != ERROR_SUCCESS)
4058 return rc;
4060 /* turn off scheduling */
4061 package->script->CurrentlyScripting= FALSE;
4063 /* first do the same as an InstallExecute */
4064 rc = ACTION_InstallExecute(package);
4065 if (rc != ERROR_SUCCESS)
4066 return rc;
4068 /* then handle Commit Actions */
4069 rc = execute_script(package,COMMIT_SCRIPT);
4071 return rc;
4074 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4076 static const WCHAR RunOnce[] = {
4077 'S','o','f','t','w','a','r','e','\\',
4078 'M','i','c','r','o','s','o','f','t','\\',
4079 'W','i','n','d','o','w','s','\\',
4080 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4081 'R','u','n','O','n','c','e',0};
4082 static const WCHAR InstallRunOnce[] = {
4083 'S','o','f','t','w','a','r','e','\\',
4084 'M','i','c','r','o','s','o','f','t','\\',
4085 'W','i','n','d','o','w','s','\\',
4086 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4087 'I','n','s','t','a','l','l','e','r','\\',
4088 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4090 static const WCHAR msiexec_fmt[] = {
4091 '%','s',
4092 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4093 '\"','%','s','\"',0};
4094 static const WCHAR install_fmt[] = {
4095 '/','I',' ','\"','%','s','\"',' ',
4096 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4097 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4098 WCHAR buffer[256], sysdir[MAX_PATH];
4099 HKEY hkey;
4100 WCHAR squished_pc[100];
4102 squash_guid(package->ProductCode,squished_pc);
4104 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4105 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4106 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4107 squished_pc);
4109 msi_reg_set_val_str( hkey, squished_pc, buffer );
4110 RegCloseKey(hkey);
4112 TRACE("Reboot command %s\n",debugstr_w(buffer));
4114 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4115 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4117 msi_reg_set_val_str( hkey, squished_pc, buffer );
4118 RegCloseKey(hkey);
4120 return ERROR_INSTALL_SUSPEND;
4123 static UINT msi_set_sourcedir_props(MSIPACKAGE *package)
4125 LPWSTR p, source;
4126 DWORD len;
4128 p = strrchrW( package->PackagePath, '\\' );
4129 if (!p)
4130 return ERROR_SUCCESS;
4132 len = p - package->PackagePath + 2;
4133 source = msi_alloc( len * sizeof(WCHAR) );
4134 lstrcpynW( source, package->PackagePath, len );
4136 MSI_SetPropertyW( package, cszSourceDir, source );
4137 MSI_SetPropertyW( package, cszSOURCEDIR, source );
4139 msi_free( source );
4141 return ERROR_SUCCESS;
4144 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4146 DWORD attrib;
4147 UINT rc;
4150 * We are currently doing what should be done here in the top level Install
4151 * however for Administrative and uninstalls this step will be needed
4153 if (!package->PackagePath)
4154 return ERROR_SUCCESS;
4156 msi_set_sourcedir_props(package);
4158 attrib = GetFileAttributesW(package->PackagePath);
4159 if (attrib == INVALID_FILE_ATTRIBUTES)
4161 LPWSTR prompt;
4162 LPWSTR msg;
4163 DWORD size = 0;
4165 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4166 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4167 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4168 if (rc == ERROR_MORE_DATA)
4170 prompt = msi_alloc(size * sizeof(WCHAR));
4171 MsiSourceListGetInfoW(package->ProductCode, NULL,
4172 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4173 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4175 else
4176 prompt = strdupW(package->PackagePath);
4178 msg = generate_error_string(package,1302,1,prompt);
4179 while(attrib == INVALID_FILE_ATTRIBUTES)
4181 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4182 if (rc == IDCANCEL)
4184 rc = ERROR_INSTALL_USEREXIT;
4185 break;
4187 attrib = GetFileAttributesW(package->PackagePath);
4189 msi_free(prompt);
4190 rc = ERROR_SUCCESS;
4192 else
4193 return ERROR_SUCCESS;
4195 return rc;
4198 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4200 HKEY hkey=0;
4201 LPWSTR buffer;
4202 LPWSTR productid;
4203 UINT rc,i;
4205 static const WCHAR szPropKeys[][80] =
4207 {'P','r','o','d','u','c','t','I','D',0},
4208 {'U','S','E','R','N','A','M','E',0},
4209 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4210 {0},
4213 static const WCHAR szRegKeys[][80] =
4215 {'P','r','o','d','u','c','t','I','D',0},
4216 {'R','e','g','O','w','n','e','r',0},
4217 {'R','e','g','C','o','m','p','a','n','y',0},
4218 {0},
4221 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4222 if (!productid)
4223 return ERROR_SUCCESS;
4225 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4226 if (rc != ERROR_SUCCESS)
4227 goto end;
4229 for( i = 0; szPropKeys[i][0]; i++ )
4231 buffer = msi_dup_property( package, szPropKeys[i] );
4232 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4233 msi_free( buffer );
4236 end:
4237 msi_free(productid);
4238 RegCloseKey(hkey);
4240 /* FIXME: call ui_actiondata */
4242 return ERROR_SUCCESS;
4246 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4248 UINT rc;
4250 package->script->InWhatSequence |= SEQUENCE_EXEC;
4251 rc = ACTION_ProcessExecSequence(package,FALSE);
4252 return rc;
4256 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4258 MSIPACKAGE *package = (MSIPACKAGE*)param;
4259 LPCWSTR compgroupid=NULL;
4260 LPCWSTR feature=NULL;
4261 LPCWSTR text = NULL;
4262 LPCWSTR qualifier = NULL;
4263 LPCWSTR component = NULL;
4264 LPWSTR advertise = NULL;
4265 LPWSTR output = NULL;
4266 HKEY hkey;
4267 UINT rc = ERROR_SUCCESS;
4268 MSICOMPONENT *comp;
4269 DWORD sz = 0;
4270 MSIRECORD *uirow;
4272 component = MSI_RecordGetString(rec,3);
4273 comp = get_loaded_component(package,component);
4275 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4276 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4277 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4279 TRACE("Skipping: Component %s not scheduled for install\n",
4280 debugstr_w(component));
4282 return ERROR_SUCCESS;
4285 compgroupid = MSI_RecordGetString(rec,1);
4286 qualifier = MSI_RecordGetString(rec,2);
4288 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4289 if (rc != ERROR_SUCCESS)
4290 goto end;
4292 text = MSI_RecordGetString(rec,4);
4293 feature = MSI_RecordGetString(rec,5);
4295 advertise = create_component_advertise_string(package, comp, feature);
4297 sz = strlenW(advertise);
4299 if (text)
4300 sz += lstrlenW(text);
4302 sz+=3;
4303 sz *= sizeof(WCHAR);
4305 output = msi_alloc_zero(sz);
4306 strcpyW(output,advertise);
4307 msi_free(advertise);
4309 if (text)
4310 strcatW(output,text);
4312 msi_reg_set_val_multi_str( hkey, qualifier, output );
4314 end:
4315 RegCloseKey(hkey);
4316 msi_free(output);
4318 /* the UI chunk */
4319 uirow = MSI_CreateRecord( 2 );
4320 MSI_RecordSetStringW( uirow, 1, compgroupid );
4321 MSI_RecordSetStringW( uirow, 2, qualifier);
4322 ui_actiondata( package, szPublishComponents, uirow);
4323 msiobj_release( &uirow->hdr );
4324 /* FIXME: call ui_progress? */
4326 return rc;
4330 * At present I am ignorning the advertised components part of this and only
4331 * focusing on the qualified component sets
4333 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4335 UINT rc;
4336 MSIQUERY * view;
4337 static const WCHAR ExecSeqQuery[] =
4338 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4339 '`','P','u','b','l','i','s','h',
4340 'C','o','m','p','o','n','e','n','t','`',0};
4342 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4343 if (rc != ERROR_SUCCESS)
4344 return ERROR_SUCCESS;
4346 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4347 msiobj_release(&view->hdr);
4349 return rc;
4352 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4354 MSIPACKAGE *package = (MSIPACKAGE*)param;
4355 MSIRECORD *row;
4356 MSIFILE *file;
4357 SC_HANDLE hscm, service = NULL;
4358 LPCWSTR name, disp, comp, depends, pass;
4359 LPCWSTR load_order, serv_name, key;
4360 DWORD serv_type, start_type;
4361 DWORD err_control;
4363 static const WCHAR query[] =
4364 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4365 '`','C','o','m','p','o','n','e','n','t','`',' ',
4366 'W','H','E','R','E',' ',
4367 '`','C','o','m','p','o','n','e','n','t','`',' ',
4368 '=','\'','%','s','\'',0};
4370 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4371 if (!hscm)
4373 ERR("Failed to open the SC Manager!\n");
4374 goto done;
4377 start_type = MSI_RecordGetInteger(rec, 5);
4378 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4379 goto done;
4381 depends = MSI_RecordGetString(rec, 8);
4382 if (depends && *depends)
4383 FIXME("Dependency list unhandled!\n");
4385 name = MSI_RecordGetString(rec, 2);
4386 disp = MSI_RecordGetString(rec, 3);
4387 serv_type = MSI_RecordGetInteger(rec, 4);
4388 err_control = MSI_RecordGetInteger(rec, 6);
4389 load_order = MSI_RecordGetString(rec, 7);
4390 serv_name = MSI_RecordGetString(rec, 9);
4391 pass = MSI_RecordGetString(rec, 10);
4392 comp = MSI_RecordGetString(rec, 12);
4394 /* fetch the service path */
4395 row = MSI_QueryGetRecord(package->db, query, comp);
4396 if (!row)
4398 ERR("Control query failed!\n");
4399 goto done;
4402 key = MSI_RecordGetString(row, 6);
4404 file = get_loaded_file(package, key);
4405 msiobj_release(&row->hdr);
4406 if (!file)
4408 ERR("Failed to load the service file\n");
4409 goto done;
4412 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4413 start_type, err_control, file->TargetPath,
4414 load_order, NULL, NULL, serv_name, pass);
4415 if (!service)
4417 if (GetLastError() != ERROR_SERVICE_EXISTS)
4418 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4421 done:
4422 CloseServiceHandle(service);
4423 CloseServiceHandle(hscm);
4425 return ERROR_SUCCESS;
4428 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4430 UINT rc;
4431 MSIQUERY * view;
4432 static const WCHAR ExecSeqQuery[] =
4433 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4434 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4436 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4437 if (rc != ERROR_SUCCESS)
4438 return ERROR_SUCCESS;
4440 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4441 msiobj_release(&view->hdr);
4443 return rc;
4446 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4447 static LPCWSTR *msi_service_args_to_vector(LPCWSTR name, LPWSTR args, DWORD *numargs)
4449 LPCWSTR *vector;
4450 LPWSTR p, q;
4451 DWORD sep_len;
4453 static const WCHAR separator[] = {'[','~',']',0};
4455 *numargs = 0;
4456 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4458 if (!args)
4459 return NULL;
4461 vector = msi_alloc(sizeof(LPWSTR));
4462 if (!vector)
4463 return NULL;
4465 p = args;
4468 (*numargs)++;
4469 vector[*numargs - 1] = p;
4471 if ((q = strstrW(p, separator)))
4473 *q = '\0';
4475 vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4476 if (!vector)
4477 return NULL;
4479 p = q + sep_len;
4481 } while (q);
4483 return vector;
4486 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4488 MSIPACKAGE *package = (MSIPACKAGE *)param;
4489 MSICOMPONENT *comp;
4490 SC_HANDLE scm, service = NULL;
4491 LPCWSTR name, *vector = NULL;
4492 LPWSTR args;
4493 DWORD event, numargs;
4494 UINT r = ERROR_FUNCTION_FAILED;
4496 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4497 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4498 return ERROR_SUCCESS;
4500 name = MSI_RecordGetString(rec, 2);
4501 event = MSI_RecordGetInteger(rec, 3);
4502 args = strdupW(MSI_RecordGetString(rec, 4));
4504 if (!(event & msidbServiceControlEventStart))
4505 return ERROR_SUCCESS;
4507 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4508 if (!scm)
4510 ERR("Failed to open the service control manager\n");
4511 goto done;
4514 service = OpenServiceW(scm, name, SERVICE_START);
4515 if (!service)
4517 ERR("Failed to open service %s\n", debugstr_w(name));
4518 goto done;
4521 vector = msi_service_args_to_vector(name, args, &numargs);
4523 if (!StartServiceW(service, numargs, vector))
4525 ERR("Failed to start service %s\n", debugstr_w(name));
4526 goto done;
4529 r = ERROR_SUCCESS;
4531 done:
4532 CloseServiceHandle(service);
4533 CloseServiceHandle(scm);
4535 msi_free(args);
4536 msi_free(vector);
4537 return r;
4540 static UINT ACTION_StartServices( MSIPACKAGE *package )
4542 UINT rc;
4543 MSIQUERY *view;
4545 static const WCHAR query[] = {
4546 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4547 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4549 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4550 if (rc != ERROR_SUCCESS)
4551 return ERROR_SUCCESS;
4553 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4554 msiobj_release(&view->hdr);
4556 return rc;
4559 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4561 MSIFILE *file;
4563 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4565 if (!lstrcmpW(file->File, filename))
4566 return file;
4569 return NULL;
4572 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4574 MSIPACKAGE *package = (MSIPACKAGE*)param;
4575 LPWSTR driver, driver_path, ptr;
4576 WCHAR outpath[MAX_PATH];
4577 MSIFILE *driver_file, *setup_file;
4578 LPCWSTR desc;
4579 DWORD len, usage;
4580 UINT r = ERROR_SUCCESS;
4582 static const WCHAR driver_fmt[] = {
4583 'D','r','i','v','e','r','=','%','s',0};
4584 static const WCHAR setup_fmt[] = {
4585 'S','e','t','u','p','=','%','s',0};
4586 static const WCHAR usage_fmt[] = {
4587 'F','i','l','e','U','s','a','g','e','=','1',0};
4589 desc = MSI_RecordGetString(rec, 3);
4591 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4592 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4594 if (!driver_file || !setup_file)
4596 ERR("ODBC Driver entry not found!\n");
4597 return ERROR_FUNCTION_FAILED;
4600 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4601 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4602 lstrlenW(usage_fmt) + 1;
4603 driver = msi_alloc(len * sizeof(WCHAR));
4604 if (!driver)
4605 return ERROR_OUTOFMEMORY;
4607 ptr = driver;
4608 lstrcpyW(ptr, desc);
4609 ptr += lstrlenW(ptr) + 1;
4611 sprintfW(ptr, driver_fmt, driver_file->FileName);
4612 ptr += lstrlenW(ptr) + 1;
4614 sprintfW(ptr, setup_fmt, setup_file->FileName);
4615 ptr += lstrlenW(ptr) + 1;
4617 lstrcpyW(ptr, usage_fmt);
4618 ptr += lstrlenW(ptr) + 1;
4619 *ptr = '\0';
4621 driver_path = strdupW(driver_file->TargetPath);
4622 ptr = strrchrW(driver_path, '\\');
4623 if (ptr) *ptr = '\0';
4625 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4626 NULL, ODBC_INSTALL_COMPLETE, &usage))
4628 ERR("Failed to install SQL driver!\n");
4629 r = ERROR_FUNCTION_FAILED;
4632 msi_free(driver);
4633 msi_free(driver_path);
4635 return r;
4638 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4640 MSIPACKAGE *package = (MSIPACKAGE*)param;
4641 LPWSTR translator, translator_path, ptr;
4642 WCHAR outpath[MAX_PATH];
4643 MSIFILE *translator_file, *setup_file;
4644 LPCWSTR desc;
4645 DWORD len, usage;
4646 UINT r = ERROR_SUCCESS;
4648 static const WCHAR translator_fmt[] = {
4649 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4650 static const WCHAR setup_fmt[] = {
4651 'S','e','t','u','p','=','%','s',0};
4653 desc = MSI_RecordGetString(rec, 3);
4655 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4656 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4658 if (!translator_file || !setup_file)
4660 ERR("ODBC Translator entry not found!\n");
4661 return ERROR_FUNCTION_FAILED;
4664 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4665 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4666 translator = msi_alloc(len * sizeof(WCHAR));
4667 if (!translator)
4668 return ERROR_OUTOFMEMORY;
4670 ptr = translator;
4671 lstrcpyW(ptr, desc);
4672 ptr += lstrlenW(ptr) + 1;
4674 sprintfW(ptr, translator_fmt, translator_file->FileName);
4675 ptr += lstrlenW(ptr) + 1;
4677 sprintfW(ptr, setup_fmt, setup_file->FileName);
4678 ptr += lstrlenW(ptr) + 1;
4679 *ptr = '\0';
4681 translator_path = strdupW(translator_file->TargetPath);
4682 ptr = strrchrW(translator_path, '\\');
4683 if (ptr) *ptr = '\0';
4685 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4686 NULL, ODBC_INSTALL_COMPLETE, &usage))
4688 ERR("Failed to install SQL translator!\n");
4689 r = ERROR_FUNCTION_FAILED;
4692 msi_free(translator);
4693 msi_free(translator_path);
4695 return r;
4698 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4700 LPWSTR attrs;
4701 LPCWSTR desc, driver;
4702 WORD request = ODBC_ADD_SYS_DSN;
4703 INT registration;
4704 DWORD len;
4705 UINT r = ERROR_SUCCESS;
4707 static const WCHAR attrs_fmt[] = {
4708 'D','S','N','=','%','s',0 };
4710 desc = MSI_RecordGetString(rec, 3);
4711 driver = MSI_RecordGetString(rec, 4);
4712 registration = MSI_RecordGetInteger(rec, 5);
4714 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4715 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4717 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4718 attrs = msi_alloc(len * sizeof(WCHAR));
4719 if (!attrs)
4720 return ERROR_OUTOFMEMORY;
4722 sprintfW(attrs, attrs_fmt, desc);
4723 attrs[len - 1] = '\0';
4725 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4727 ERR("Failed to install SQL data source!\n");
4728 r = ERROR_FUNCTION_FAILED;
4731 msi_free(attrs);
4733 return r;
4736 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4738 UINT rc;
4739 MSIQUERY *view;
4741 static const WCHAR driver_query[] = {
4742 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4743 'O','D','B','C','D','r','i','v','e','r',0 };
4745 static const WCHAR translator_query[] = {
4746 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4747 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4749 static const WCHAR source_query[] = {
4750 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4751 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4753 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4754 if (rc != ERROR_SUCCESS)
4755 return ERROR_SUCCESS;
4757 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4758 msiobj_release(&view->hdr);
4760 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4761 if (rc != ERROR_SUCCESS)
4762 return ERROR_SUCCESS;
4764 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4765 msiobj_release(&view->hdr);
4767 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4768 if (rc != ERROR_SUCCESS)
4769 return ERROR_SUCCESS;
4771 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4772 msiobj_release(&view->hdr);
4774 return rc;
4777 #define ENV_ACT_SETALWAYS 0x1
4778 #define ENV_ACT_SETABSENT 0x2
4779 #define ENV_ACT_REMOVE 0x4
4780 #define ENV_ACT_REMOVEMATCH 0x8
4782 #define ENV_MOD_MACHINE 0x20000000
4783 #define ENV_MOD_APPEND 0x40000000
4784 #define ENV_MOD_PREFIX 0x80000000
4785 #define ENV_MOD_MASK 0xC0000000
4787 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4789 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4791 LPCWSTR cptr = *name;
4792 LPCWSTR ptr = *value;
4794 static const WCHAR prefix[] = {'[','~',']',0};
4795 static const int prefix_len = 3;
4797 *flags = 0;
4798 while (*cptr)
4800 if (*cptr == '=')
4801 *flags |= ENV_ACT_SETALWAYS;
4802 else if (*cptr == '+')
4803 *flags |= ENV_ACT_SETABSENT;
4804 else if (*cptr == '-')
4805 *flags |= ENV_ACT_REMOVE;
4806 else if (*cptr == '!')
4807 *flags |= ENV_ACT_REMOVEMATCH;
4808 else if (*cptr == '*')
4809 *flags |= ENV_MOD_MACHINE;
4810 else
4811 break;
4813 cptr++;
4814 (*name)++;
4817 if (!*cptr)
4819 ERR("Missing environment variable\n");
4820 return ERROR_FUNCTION_FAILED;
4823 if (!strncmpW(ptr, prefix, prefix_len))
4825 *flags |= ENV_MOD_APPEND;
4826 *value += lstrlenW(prefix);
4828 else if (lstrlenW(*value) >= prefix_len)
4830 ptr += lstrlenW(ptr) - prefix_len;
4831 if (!lstrcmpW(ptr, prefix))
4833 *flags |= ENV_MOD_PREFIX;
4834 /* the "[~]" will be removed by deformat_string */;
4838 if (!*flags ||
4839 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4840 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4841 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4842 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4844 ERR("Invalid flags: %08x\n", *flags);
4845 return ERROR_FUNCTION_FAILED;
4848 return ERROR_SUCCESS;
4851 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4853 MSIPACKAGE *package = param;
4854 LPCWSTR name, value, comp;
4855 LPWSTR data = NULL, newval = NULL;
4856 LPWSTR deformatted = NULL, ptr;
4857 DWORD flags, type, size;
4858 LONG res;
4859 HKEY env = NULL, root = HKEY_CURRENT_USER;
4861 static const WCHAR environment[] =
4862 {'S','y','s','t','e','m','\\',
4863 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4864 'C','o','n','t','r','o','l','\\',
4865 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4866 'E','n','v','i','r','o','n','m','e','n','t',0};
4867 static const WCHAR semicolon[] = {';',0};
4869 name = MSI_RecordGetString(rec, 2);
4870 value = MSI_RecordGetString(rec, 3);
4871 comp = MSI_RecordGetString(rec, 4);
4873 res = env_set_flags(&name, &value, &flags);
4874 if (res != ERROR_SUCCESS)
4875 goto done;
4877 deformat_string(package, value, &deformatted);
4878 if (!deformatted)
4880 res = ERROR_OUTOFMEMORY;
4881 goto done;
4884 value = deformatted;
4886 if (flags & ENV_MOD_MACHINE)
4887 root = HKEY_LOCAL_MACHINE;
4889 res = RegOpenKeyExW(root, environment, 0, KEY_ALL_ACCESS, &env);
4890 if (res != ERROR_SUCCESS)
4891 goto done;
4893 if (flags & ENV_ACT_REMOVE)
4894 FIXME("Not removing environment variable on uninstall!\n");
4896 size = 0;
4897 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
4898 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
4899 (res == ERROR_SUCCESS && type != REG_SZ))
4900 goto done;
4902 if (res != ERROR_FILE_NOT_FOUND)
4904 if (flags & ENV_ACT_SETABSENT)
4906 res = ERROR_SUCCESS;
4907 goto done;
4910 data = msi_alloc(size);
4911 if (!data)
4913 RegCloseKey(env);
4914 return ERROR_OUTOFMEMORY;
4917 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
4918 if (res != ERROR_SUCCESS)
4919 goto done;
4921 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
4923 res = RegDeleteKeyW(env, name);
4924 goto done;
4927 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
4928 newval = msi_alloc(size);
4929 ptr = newval;
4930 if (!newval)
4932 res = ERROR_OUTOFMEMORY;
4933 goto done;
4936 if (!(flags & ENV_MOD_MASK))
4937 lstrcpyW(newval, value);
4938 else
4940 if (flags & ENV_MOD_PREFIX)
4942 lstrcpyW(newval, value);
4943 lstrcatW(newval, semicolon);
4944 ptr = newval + lstrlenW(value) + 1;
4947 lstrcpyW(ptr, data);
4949 if (flags & ENV_MOD_APPEND)
4951 lstrcatW(newval, semicolon);
4952 lstrcatW(newval, value);
4956 else
4958 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
4959 newval = msi_alloc(size);
4960 if (!newval)
4962 res = ERROR_OUTOFMEMORY;
4963 goto done;
4966 lstrcpyW(newval, value);
4969 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
4970 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
4972 done:
4973 if (env) RegCloseKey(env);
4974 msi_free(deformatted);
4975 msi_free(data);
4976 msi_free(newval);
4977 return res;
4980 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
4982 UINT rc;
4983 MSIQUERY * view;
4984 static const WCHAR ExecSeqQuery[] =
4985 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4986 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
4987 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4988 if (rc != ERROR_SUCCESS)
4989 return ERROR_SUCCESS;
4991 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
4992 msiobj_release(&view->hdr);
4994 return rc;
4997 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
4999 typedef struct
5001 struct list entry;
5002 LPWSTR sourcename;
5003 LPWSTR destname;
5004 LPWSTR source;
5005 LPWSTR dest;
5006 } FILE_LIST;
5008 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5010 BOOL ret;
5012 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5013 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5015 WARN("Source or dest is directory, not moving\n");
5016 return FALSE;
5019 if (options == msidbMoveFileOptionsMove)
5021 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5022 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5023 if (!ret)
5025 WARN("MoveFile failed: %d\n", GetLastError());
5026 return FALSE;
5029 else
5031 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5032 ret = CopyFileW(source, dest, FALSE);
5033 if (!ret)
5035 WARN("CopyFile failed: %d\n", GetLastError());
5036 return FALSE;
5040 return TRUE;
5043 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5045 LPWSTR path, ptr;
5046 DWORD dirlen, pathlen;
5048 ptr = strrchrW(wildcard, '\\');
5049 dirlen = ptr - wildcard + 1;
5051 pathlen = dirlen + lstrlenW(filename) + 1;
5052 path = msi_alloc(pathlen * sizeof(WCHAR));
5054 lstrcpynW(path, wildcard, dirlen + 1);
5055 lstrcatW(path, filename);
5057 return path;
5060 static void free_file_entry(FILE_LIST *file)
5062 msi_free(file->source);
5063 msi_free(file->dest);
5064 msi_free(file);
5067 static void free_list(FILE_LIST *list)
5069 while (!list_empty(&list->entry))
5071 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5073 list_remove(&file->entry);
5074 free_file_entry(file);
5078 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5080 FILE_LIST *new, *file;
5081 LPWSTR ptr, filename;
5082 DWORD size;
5084 new = msi_alloc_zero(sizeof(FILE_LIST));
5085 if (!new)
5086 return FALSE;
5088 new->source = strdupW(source);
5089 ptr = strrchrW(dest, '\\') + 1;
5090 filename = strrchrW(new->source, '\\') + 1;
5092 new->sourcename = filename;
5094 if (*ptr)
5095 new->destname = ptr;
5096 else
5097 new->destname = new->sourcename;
5099 size = (ptr - dest) + lstrlenW(filename) + 1;
5100 new->dest = msi_alloc(size * sizeof(WCHAR));
5101 if (!new->dest)
5103 free_file_entry(new);
5104 return FALSE;
5107 lstrcpynW(new->dest, dest, ptr - dest + 1);
5108 lstrcatW(new->dest, filename);
5110 if (list_empty(&files->entry))
5112 list_add_head(&files->entry, &new->entry);
5113 return TRUE;
5116 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5118 if (lstrcmpW(source, file->source) < 0)
5120 list_add_before(&file->entry, &new->entry);
5121 return TRUE;
5125 list_add_after(&file->entry, &new->entry);
5126 return TRUE;
5129 BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5131 WIN32_FIND_DATAW wfd;
5132 HANDLE hfile;
5133 LPWSTR path;
5134 BOOL res;
5135 FILE_LIST files, *file;
5136 DWORD size;
5138 hfile = FindFirstFileW(source, &wfd);
5139 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5141 list_init(&files.entry);
5143 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5145 if (is_dot_dir(wfd.cFileName)) continue;
5147 path = wildcard_to_file(source, wfd.cFileName);
5148 if (!path)
5150 free_list(&files);
5151 return FALSE;
5154 add_wildcard(&files, path, dest);
5155 msi_free(path);
5158 /* only the first wildcard match gets renamed to dest */
5159 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5160 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5161 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5162 if (!file->dest)
5164 free_list(&files);
5165 return FALSE;
5168 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5170 while (!list_empty(&files.entry))
5172 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5174 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5176 list_remove(&file->entry);
5177 free_file_entry(file);
5180 return TRUE;
5183 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5185 MSIPACKAGE *package = param;
5186 MSICOMPONENT *comp;
5187 LPCWSTR sourcename, destname;
5188 LPWSTR sourcedir = NULL, destdir = NULL;
5189 LPWSTR source = NULL, dest = NULL;
5190 int options;
5191 DWORD size;
5192 BOOL ret, wildcards;
5194 static const WCHAR backslash[] = {'\\',0};
5196 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5197 if (!comp || !comp->Enabled ||
5198 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5200 TRACE("Component not set for install, not moving file\n");
5201 return ERROR_SUCCESS;
5204 sourcename = MSI_RecordGetString(rec, 3);
5205 destname = MSI_RecordGetString(rec, 4);
5206 options = MSI_RecordGetInteger(rec, 7);
5208 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5209 if (!sourcedir)
5210 goto done;
5212 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5213 if (!destdir)
5214 goto done;
5216 if (!sourcename)
5218 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5219 goto done;
5221 source = strdupW(sourcedir);
5222 if (!source)
5223 goto done;
5225 else
5227 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5228 source = msi_alloc(size * sizeof(WCHAR));
5229 if (!source)
5230 goto done;
5232 lstrcpyW(source, sourcedir);
5233 if (source[lstrlenW(source) - 1] != '\\')
5234 lstrcatW(source, backslash);
5235 lstrcatW(source, sourcename);
5238 wildcards = strchrW(source, '*') || strchrW(source, '?');
5240 if (!destname && !wildcards)
5242 destname = strdupW(sourcename);
5243 if (!destname)
5244 goto done;
5247 size = 0;
5248 if (destname)
5249 size = lstrlenW(destname);
5251 size += lstrlenW(destdir) + 2;
5252 dest = msi_alloc(size * sizeof(WCHAR));
5253 if (!dest)
5254 goto done;
5256 lstrcpyW(dest, destdir);
5257 if (dest[lstrlenW(dest) - 1] != '\\')
5258 lstrcatW(dest, backslash);
5260 if (destname)
5261 lstrcatW(dest, destname);
5263 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5265 ret = CreateDirectoryW(destdir, NULL);
5266 if (!ret)
5268 WARN("CreateDirectory failed: %d\n", GetLastError());
5269 return ERROR_SUCCESS;
5273 if (!wildcards)
5274 msi_move_file(source, dest, options);
5275 else
5276 move_files_wildcard(source, dest, options);
5278 done:
5279 msi_free(sourcedir);
5280 msi_free(destdir);
5281 msi_free(source);
5282 msi_free(dest);
5284 return ERROR_SUCCESS;
5287 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5289 UINT rc;
5290 MSIQUERY *view;
5292 static const WCHAR ExecSeqQuery[] =
5293 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5294 '`','M','o','v','e','F','i','l','e','`',0};
5296 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5297 if (rc != ERROR_SUCCESS)
5298 return ERROR_SUCCESS;
5300 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5301 msiobj_release(&view->hdr);
5303 return rc;
5306 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5307 LPCSTR action, LPCWSTR table )
5309 static const WCHAR query[] = {
5310 'S','E','L','E','C','T',' ','*',' ',
5311 'F','R','O','M',' ','`','%','s','`',0 };
5312 MSIQUERY *view = NULL;
5313 DWORD count = 0;
5314 UINT r;
5316 r = MSI_OpenQuery( package->db, &view, query, table );
5317 if (r == ERROR_SUCCESS)
5319 r = MSI_IterateRecords(view, &count, NULL, package);
5320 msiobj_release(&view->hdr);
5323 if (count)
5324 FIXME("%s -> %u ignored %s table values\n",
5325 action, count, debugstr_w(table));
5327 return ERROR_SUCCESS;
5330 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5332 TRACE("%p\n", package);
5333 return ERROR_SUCCESS;
5336 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5338 static const WCHAR table[] =
5339 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5340 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5343 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5345 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5346 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5349 static UINT ACTION_BindImage( MSIPACKAGE *package )
5351 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5352 return msi_unimplemented_action_stub( package, "BindImage", table );
5355 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5357 static const WCHAR table[] = {
5358 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5359 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5362 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5364 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5365 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5368 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5370 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5371 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5374 static UINT ACTION_StopServices( MSIPACKAGE *package )
5376 static const WCHAR table[] = {
5377 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5378 return msi_unimplemented_action_stub( package, "StopServices", table );
5381 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5383 static const WCHAR table[] = {
5384 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5385 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5387 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5389 static const WCHAR table[] = {
5390 'P','r','o','d','u','c','t','I','D',0 };
5391 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5394 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5396 static const WCHAR table[] = {
5397 'E','n','v','i','r','o','n','m','e','n','t',0 };
5398 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5401 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5403 static const WCHAR table[] = {
5404 'M','s','i','A','s','s','e','m','b','l','y',0 };
5405 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5408 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5410 static const WCHAR table[] = {
5411 'M','s','i','A','s','s','e','m','b','l','y',0 };
5412 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5415 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5417 static const WCHAR table[] = { 'F','o','n','t',0 };
5418 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5421 static UINT ACTION_CCPSearch( MSIPACKAGE *package )
5423 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5424 return msi_unimplemented_action_stub( package, "CCPSearch", table );
5427 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5429 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5430 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5433 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5435 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5436 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5439 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5441 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5442 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5445 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5447 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5448 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5451 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5453 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5454 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5457 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5459 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5460 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5463 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5465 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5466 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5469 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5471 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5472 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5475 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5477 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5478 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5481 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5483 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5484 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5487 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5489 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5490 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5493 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5495 static const WCHAR table[] = { 'A','p','p','I','d',0 };
5496 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5499 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5501 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5502 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5505 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5507 static const WCHAR table[] = { 'M','I','M','E',0 };
5508 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5511 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5513 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5514 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5517 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5519 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5520 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5523 static const struct _actions StandardActions[] = {
5524 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5525 { szAppSearch, ACTION_AppSearch },
5526 { szBindImage, ACTION_BindImage },
5527 { szCCPSearch, ACTION_CCPSearch },
5528 { szCostFinalize, ACTION_CostFinalize },
5529 { szCostInitialize, ACTION_CostInitialize },
5530 { szCreateFolders, ACTION_CreateFolders },
5531 { szCreateShortcuts, ACTION_CreateShortcuts },
5532 { szDeleteServices, ACTION_DeleteServices },
5533 { szDisableRollback, NULL },
5534 { szDuplicateFiles, ACTION_DuplicateFiles },
5535 { szExecuteAction, ACTION_ExecuteAction },
5536 { szFileCost, ACTION_FileCost },
5537 { szFindRelatedProducts, ACTION_FindRelatedProducts },
5538 { szForceReboot, ACTION_ForceReboot },
5539 { szInstallAdminPackage, NULL },
5540 { szInstallExecute, ACTION_InstallExecute },
5541 { szInstallExecuteAgain, ACTION_InstallExecute },
5542 { szInstallFiles, ACTION_InstallFiles},
5543 { szInstallFinalize, ACTION_InstallFinalize },
5544 { szInstallInitialize, ACTION_InstallInitialize },
5545 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5546 { szInstallValidate, ACTION_InstallValidate },
5547 { szIsolateComponents, ACTION_IsolateComponents },
5548 { szLaunchConditions, ACTION_LaunchConditions },
5549 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5550 { szMoveFiles, ACTION_MoveFiles },
5551 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5552 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5553 { szInstallODBC, ACTION_InstallODBC },
5554 { szInstallServices, ACTION_InstallServices },
5555 { szPatchFiles, ACTION_PatchFiles },
5556 { szProcessComponents, ACTION_ProcessComponents },
5557 { szPublishComponents, ACTION_PublishComponents },
5558 { szPublishFeatures, ACTION_PublishFeatures },
5559 { szPublishProduct, ACTION_PublishProduct },
5560 { szRegisterClassInfo, ACTION_RegisterClassInfo },
5561 { szRegisterComPlus, ACTION_RegisterComPlus},
5562 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5563 { szRegisterFonts, ACTION_RegisterFonts },
5564 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5565 { szRegisterProduct, ACTION_RegisterProduct },
5566 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5567 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5568 { szRegisterUser, ACTION_RegisterUser },
5569 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5570 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5571 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5572 { szRemoveFiles, ACTION_RemoveFiles },
5573 { szRemoveFolders, ACTION_RemoveFolders },
5574 { szRemoveIniValues, ACTION_RemoveIniValues },
5575 { szRemoveODBC, ACTION_RemoveODBC },
5576 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5577 { szRemoveShortcuts, ACTION_RemoveShortcuts },
5578 { szResolveSource, ACTION_ResolveSource },
5579 { szRMCCPSearch, ACTION_RMCCPSearch },
5580 { szScheduleReboot, NULL },
5581 { szSelfRegModules, ACTION_SelfRegModules },
5582 { szSelfUnregModules, ACTION_SelfUnregModules },
5583 { szSetODBCFolders, NULL },
5584 { szStartServices, ACTION_StartServices },
5585 { szStopServices, ACTION_StopServices },
5586 { szUnpublishComponents, ACTION_UnpublishComponents },
5587 { szUnpublishFeatures, ACTION_UnpublishFeatures },
5588 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5589 { szUnregisterComPlus, ACTION_UnregisterComPlus },
5590 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5591 { szUnregisterFonts, ACTION_UnregisterFonts },
5592 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5593 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5594 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5595 { szValidateProductID, ACTION_ValidateProductID },
5596 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5597 { szWriteIniValues, ACTION_WriteIniValues },
5598 { szWriteRegistryValues, ACTION_WriteRegistryValues },
5599 { NULL, NULL },