msi: Copy the temporary package when storing the installer.
[wine/multimedia.git] / dlls / msi / action.c
blob535f070a16a040baf221cab80683324b58e67e08
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * Pages I need
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
29 #include <stdarg.h>
31 #define COBJMACROS
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winerror.h"
36 #include "winreg.h"
37 #include "winsvc.h"
38 #include "odbcinst.h"
39 #include "wine/debug.h"
40 #include "msidefs.h"
41 #include "msipriv.h"
42 #include "winuser.h"
43 #include "shlobj.h"
44 #include "wine/unicode.h"
45 #include "winver.h"
47 #define REG_PROGRESS_VALUE 13200
48 #define COMPONENT_PROGRESS_VALUE 24000
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
53 * Prototypes
55 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
56 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
57 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
60 * consts and values used
62 static const WCHAR c_colon[] = {'C',':','\\',0};
64 static const WCHAR szCreateFolders[] =
65 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
66 static const WCHAR szCostFinalize[] =
67 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
68 const WCHAR szInstallFiles[] =
69 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
70 const WCHAR szDuplicateFiles[] =
71 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
72 static const WCHAR szWriteRegistryValues[] =
73 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
74 'V','a','l','u','e','s',0};
75 static const WCHAR szCostInitialize[] =
76 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
77 static const WCHAR szFileCost[] =
78 {'F','i','l','e','C','o','s','t',0};
79 static const WCHAR szInstallInitialize[] =
80 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
81 static const WCHAR szInstallValidate[] =
82 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
83 static const WCHAR szLaunchConditions[] =
84 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
85 static const WCHAR szProcessComponents[] =
86 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
87 static const WCHAR szRegisterTypeLibraries[] =
88 {'R','e','g','i','s','t','e','r','T','y','p','e',
89 'L','i','b','r','a','r','i','e','s',0};
90 const WCHAR szRegisterClassInfo[] =
91 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
92 const WCHAR szRegisterProgIdInfo[] =
93 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
94 static const WCHAR szCreateShortcuts[] =
95 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
96 static const WCHAR szPublishProduct[] =
97 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
98 static const WCHAR szWriteIniValues[] =
99 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
100 static const WCHAR szSelfRegModules[] =
101 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
102 static const WCHAR szPublishFeatures[] =
103 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
104 static const WCHAR szRegisterProduct[] =
105 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
106 static const WCHAR szInstallExecute[] =
107 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
108 static const WCHAR szInstallExecuteAgain[] =
109 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
110 'A','g','a','i','n',0};
111 static const WCHAR szInstallFinalize[] =
112 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
113 static const WCHAR szForceReboot[] =
114 {'F','o','r','c','e','R','e','b','o','o','t',0};
115 static const WCHAR szResolveSource[] =
116 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
117 static const WCHAR szAppSearch[] =
118 {'A','p','p','S','e','a','r','c','h',0};
119 static const WCHAR szAllocateRegistrySpace[] =
120 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
121 'S','p','a','c','e',0};
122 static const WCHAR szBindImage[] =
123 {'B','i','n','d','I','m','a','g','e',0};
124 static const WCHAR szCCPSearch[] =
125 {'C','C','P','S','e','a','r','c','h',0};
126 static const WCHAR szDeleteServices[] =
127 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
128 static const WCHAR szDisableRollback[] =
129 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
130 static const WCHAR szExecuteAction[] =
131 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
132 const WCHAR szFindRelatedProducts[] =
133 {'F','i','n','d','R','e','l','a','t','e','d',
134 'P','r','o','d','u','c','t','s',0};
135 static const WCHAR szInstallAdminPackage[] =
136 {'I','n','s','t','a','l','l','A','d','m','i','n',
137 'P','a','c','k','a','g','e',0};
138 static const WCHAR szInstallSFPCatalogFile[] =
139 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
140 'F','i','l','e',0};
141 static const WCHAR szIsolateComponents[] =
142 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
143 const WCHAR szMigrateFeatureStates[] =
144 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
145 'S','t','a','t','e','s',0};
146 const WCHAR szMoveFiles[] =
147 {'M','o','v','e','F','i','l','e','s',0};
148 static const WCHAR szMsiPublishAssemblies[] =
149 {'M','s','i','P','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szMsiUnpublishAssemblies[] =
152 {'M','s','i','U','n','p','u','b','l','i','s','h',
153 'A','s','s','e','m','b','l','i','e','s',0};
154 static const WCHAR szInstallODBC[] =
155 {'I','n','s','t','a','l','l','O','D','B','C',0};
156 static const WCHAR szInstallServices[] =
157 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
158 const WCHAR szPatchFiles[] =
159 {'P','a','t','c','h','F','i','l','e','s',0};
160 static const WCHAR szPublishComponents[] =
161 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
162 static const WCHAR szRegisterComPlus[] =
163 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
164 const WCHAR szRegisterExtensionInfo[] =
165 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
166 'I','n','f','o',0};
167 static const WCHAR szRegisterFonts[] =
168 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
169 const WCHAR szRegisterMIMEInfo[] =
170 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
171 static const WCHAR szRegisterUser[] =
172 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
173 const WCHAR szRemoveDuplicateFiles[] =
174 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
175 'F','i','l','e','s',0};
176 static const WCHAR szRemoveEnvironmentStrings[] =
177 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
178 'S','t','r','i','n','g','s',0};
179 const WCHAR szRemoveExistingProducts[] =
180 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
181 'P','r','o','d','u','c','t','s',0};
182 const WCHAR szRemoveFiles[] =
183 {'R','e','m','o','v','e','F','i','l','e','s',0};
184 static const WCHAR szRemoveFolders[] =
185 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
186 static const WCHAR szRemoveIniValues[] =
187 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
188 static const WCHAR szRemoveODBC[] =
189 {'R','e','m','o','v','e','O','D','B','C',0};
190 static const WCHAR szRemoveRegistryValues[] =
191 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
192 'V','a','l','u','e','s',0};
193 static const WCHAR szRemoveShortcuts[] =
194 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
195 static const WCHAR szRMCCPSearch[] =
196 {'R','M','C','C','P','S','e','a','r','c','h',0};
197 static const WCHAR szScheduleReboot[] =
198 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
199 static const WCHAR szSelfUnregModules[] =
200 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
201 static const WCHAR szSetODBCFolders[] =
202 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
203 static const WCHAR szStartServices[] =
204 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
205 static const WCHAR szStopServices[] =
206 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
207 static const WCHAR szUnpublishComponents[] =
208 {'U','n','p','u','b','l','i','s','h',
209 'C','o','m','p','o','n','e','n','t','s',0};
210 static const WCHAR szUnpublishFeatures[] =
211 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
212 const WCHAR szUnregisterClassInfo[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
214 'I','n','f','o',0};
215 static const WCHAR szUnregisterComPlus[] =
216 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
217 const WCHAR szUnregisterExtensionInfo[] =
218 {'U','n','r','e','g','i','s','t','e','r',
219 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
220 static const WCHAR szUnregisterFonts[] =
221 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
222 const WCHAR szUnregisterMIMEInfo[] =
223 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
224 const WCHAR szUnregisterProgIdInfo[] =
225 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
226 'I','n','f','o',0};
227 static const WCHAR szUnregisterTypeLibraries[] =
228 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
229 'L','i','b','r','a','r','i','e','s',0};
230 static const WCHAR szValidateProductID[] =
231 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
232 static const WCHAR szWriteEnvironmentStrings[] =
233 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
234 'S','t','r','i','n','g','s',0};
236 /* action handlers */
237 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
239 struct _actions {
240 LPCWSTR action;
241 STANDARDACTIONHANDLER handler;
244 static const struct _actions StandardActions[];
247 /********************************************************
248 * helper functions
249 ********************************************************/
251 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
253 static const WCHAR Query_t[] =
254 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
255 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
256 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
257 ' ','\'','%','s','\'',0};
258 MSIRECORD * row;
260 row = MSI_QueryGetRecord( package->db, Query_t, action );
261 if (!row)
262 return;
263 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
264 msiobj_release(&row->hdr);
267 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
268 UINT rc)
270 MSIRECORD * row;
271 static const WCHAR template_s[]=
272 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
273 '%','s', '.',0};
274 static const WCHAR template_e[]=
275 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
276 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
277 '%','i','.',0};
278 static const WCHAR format[] =
279 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
280 WCHAR message[1024];
281 WCHAR timet[0x100];
283 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
284 if (start)
285 sprintfW(message,template_s,timet,action);
286 else
287 sprintfW(message,template_e,timet,action,rc);
289 row = MSI_CreateRecord(1);
290 MSI_RecordSetStringW(row,1,message);
292 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
293 msiobj_release(&row->hdr);
296 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
298 LPCWSTR ptr,ptr2;
299 BOOL quote;
300 DWORD len;
301 LPWSTR prop = NULL, val = NULL;
303 if (!szCommandLine)
304 return ERROR_SUCCESS;
306 ptr = szCommandLine;
308 while (*ptr)
310 if (*ptr==' ')
312 ptr++;
313 continue;
316 TRACE("Looking at %s\n",debugstr_w(ptr));
318 ptr2 = strchrW(ptr,'=');
319 if (!ptr2)
321 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
322 break;
325 quote = FALSE;
327 len = ptr2-ptr;
328 prop = msi_alloc((len+1)*sizeof(WCHAR));
329 memcpy(prop,ptr,len*sizeof(WCHAR));
330 prop[len]=0;
331 ptr2++;
333 len = 0;
334 ptr = ptr2;
335 while (*ptr && (quote || (!quote && *ptr!=' ')))
337 if (*ptr == '"')
338 quote = !quote;
339 ptr++;
340 len++;
343 if (*ptr2=='"')
345 ptr2++;
346 len -= 2;
348 val = msi_alloc((len+1)*sizeof(WCHAR));
349 memcpy(val,ptr2,len*sizeof(WCHAR));
350 val[len] = 0;
352 if (lstrlenW(prop) > 0)
354 TRACE("Found commandline property (%s) = (%s)\n",
355 debugstr_w(prop), debugstr_w(val));
356 MSI_SetPropertyW(package,prop,val);
358 msi_free(val);
359 msi_free(prop);
362 return ERROR_SUCCESS;
366 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
368 LPCWSTR pc;
369 LPWSTR p, *ret = NULL;
370 UINT count = 0;
372 if (!str)
373 return ret;
375 /* count the number of substrings */
376 for ( pc = str, count = 0; pc; count++ )
378 pc = strchrW( pc, sep );
379 if (pc)
380 pc++;
383 /* allocate space for an array of substring pointers and the substrings */
384 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
385 (lstrlenW(str)+1) * sizeof(WCHAR) );
386 if (!ret)
387 return ret;
389 /* copy the string and set the pointers */
390 p = (LPWSTR) &ret[count+1];
391 lstrcpyW( p, str );
392 for( count = 0; (ret[count] = p); count++ )
394 p = strchrW( p, sep );
395 if (p)
396 *p++ = 0;
399 return ret;
402 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
404 WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
405 LPWSTR prod_code, patch_product;
406 UINT ret;
408 prod_code = msi_dup_property( package, szProductCode );
409 patch_product = msi_get_suminfo_product( patch );
411 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
413 if ( strstrW( patch_product, prod_code ) )
414 ret = ERROR_SUCCESS;
415 else
416 ret = ERROR_FUNCTION_FAILED;
418 msi_free( patch_product );
419 msi_free( prod_code );
421 return ret;
424 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
425 MSIDATABASE *patch_db, LPCWSTR name )
427 UINT ret = ERROR_FUNCTION_FAILED;
428 IStorage *stg = NULL;
429 HRESULT r;
431 TRACE("%p %s\n", package, debugstr_w(name) );
433 if (*name++ != ':')
435 ERR("expected a colon in %s\n", debugstr_w(name));
436 return ERROR_FUNCTION_FAILED;
439 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
440 if (SUCCEEDED(r))
442 ret = msi_check_transform_applicable( package, stg );
443 if (ret == ERROR_SUCCESS)
444 msi_table_apply_transform( package->db, stg );
445 else
446 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
447 IStorage_Release( stg );
449 else
450 ERR("failed to open substorage %s\n", debugstr_w(name));
452 return ERROR_SUCCESS;
455 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
457 static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
458 LPWSTR guid_list, *guids, product_code;
459 UINT i, ret = ERROR_FUNCTION_FAILED;
461 product_code = msi_dup_property( package, szProdCode );
462 if (!product_code)
464 /* FIXME: the property ProductCode should be written into the DB somewhere */
465 ERR("no product code to check\n");
466 return ERROR_SUCCESS;
469 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
470 guids = msi_split_string( guid_list, ';' );
471 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
473 if (!lstrcmpW( guids[i], product_code ))
474 ret = ERROR_SUCCESS;
476 msi_free( guids );
477 msi_free( guid_list );
478 msi_free( product_code );
480 return ret;
483 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
485 MSISUMMARYINFO *si;
486 LPWSTR str, *substorage;
487 UINT i, r = ERROR_SUCCESS;
489 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
490 if (!si)
491 return ERROR_FUNCTION_FAILED;
493 msi_check_patch_applicable( package, si );
495 /* enumerate the substorage */
496 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
497 substorage = msi_split_string( str, ';' );
498 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
499 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
500 msi_free( substorage );
501 msi_free( str );
503 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
505 msiobj_release( &si->hdr );
507 return r;
510 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
512 MSIDATABASE *patch_db = NULL;
513 UINT r;
515 TRACE("%p %s\n", package, debugstr_w( file ) );
517 /* FIXME:
518 * We probably want to make sure we only open a patch collection here.
519 * Patch collections (.msp) and databases (.msi) have different GUIDs
520 * but currently MSI_OpenDatabaseW will accept both.
522 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
523 if ( r != ERROR_SUCCESS )
525 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
526 return r;
529 msi_parse_patch_summary( package, patch_db );
532 * There might be a CAB file in the patch package,
533 * so append it to the list of storage to search for streams.
535 append_storage_to_db( package->db, patch_db->storage );
537 msiobj_release( &patch_db->hdr );
539 return ERROR_SUCCESS;
542 /* get the PATCH property, and apply all the patches it specifies */
543 static UINT msi_apply_patches( MSIPACKAGE *package )
545 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
546 LPWSTR patch_list, *patches;
547 UINT i, r = ERROR_SUCCESS;
549 patch_list = msi_dup_property( package, szPatch );
551 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
553 patches = msi_split_string( patch_list, ';' );
554 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
555 r = msi_apply_patch_package( package, patches[i] );
557 msi_free( patches );
558 msi_free( patch_list );
560 return r;
563 static UINT msi_apply_transforms( MSIPACKAGE *package )
565 static const WCHAR szTransforms[] = {
566 'T','R','A','N','S','F','O','R','M','S',0 };
567 LPWSTR xform_list, *xforms;
568 UINT i, r = ERROR_SUCCESS;
570 xform_list = msi_dup_property( package, szTransforms );
571 xforms = msi_split_string( xform_list, ';' );
573 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
575 if (xforms[i][0] == ':')
576 r = msi_apply_substorage_transform( package, package->db, &xforms[i][1] );
577 else
578 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
581 msi_free( xforms );
582 msi_free( xform_list );
584 return r;
587 BOOL ui_sequence_exists( MSIPACKAGE *package )
589 MSIQUERY *view;
590 UINT rc;
592 static const WCHAR ExecSeqQuery [] =
593 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
594 '`','I','n','s','t','a','l','l',
595 'U','I','S','e','q','u','e','n','c','e','`',
596 ' ','W','H','E','R','E',' ',
597 '`','S','e','q','u','e','n','c','e','`',' ',
598 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
599 '`','S','e','q','u','e','n','c','e','`',0};
601 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
602 if (rc == ERROR_SUCCESS)
604 msiobj_release(&view->hdr);
605 return TRUE;
608 return FALSE;
611 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
613 LPWSTR p, db;
614 LPWSTR source, check;
615 DWORD len;
617 static const WCHAR szOriginalDatabase[] =
618 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
620 db = msi_dup_property( package, szOriginalDatabase );
621 if (!db)
622 return ERROR_OUTOFMEMORY;
624 p = strrchrW( db, '\\' );
625 if (!p)
627 p = strrchrW( db, '/' );
628 if (!p)
630 msi_free(db);
631 return ERROR_SUCCESS;
635 len = p - db + 2;
636 source = msi_alloc( len * sizeof(WCHAR) );
637 lstrcpynW( source, db, len );
639 check = msi_dup_property( package, cszSourceDir );
640 if (!check || replace)
641 MSI_SetPropertyW( package, cszSourceDir, source );
643 msi_free( check );
645 check = msi_dup_property( package, cszSOURCEDIR );
646 if (!check || replace)
647 MSI_SetPropertyW( package, cszSOURCEDIR, source );
649 msi_free( check );
650 msi_free( source );
651 msi_free( db );
653 return ERROR_SUCCESS;
656 /****************************************************
657 * TOP level entry points
658 *****************************************************/
660 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
661 LPCWSTR szCommandLine )
663 UINT rc;
664 BOOL ui = FALSE, ui_exists;
665 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
666 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
667 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
669 MSI_SetPropertyW(package, szAction, szInstall);
671 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
673 package->script->InWhatSequence = SEQUENCE_INSTALL;
675 if (szPackagePath)
677 LPWSTR p, dir;
678 LPCWSTR file;
680 dir = strdupW(szPackagePath);
681 p = strrchrW(dir, '\\');
682 if (p)
684 *(++p) = 0;
685 file = szPackagePath + (p - dir);
687 else
689 msi_free(dir);
690 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
691 GetCurrentDirectoryW(MAX_PATH, dir);
692 lstrcatW(dir, cszbs);
693 file = szPackagePath;
696 msi_free( package->PackagePath );
697 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
698 if (!package->PackagePath)
700 msi_free(dir);
701 return ERROR_OUTOFMEMORY;
704 lstrcpyW(package->PackagePath, dir);
705 lstrcatW(package->PackagePath, file);
706 msi_free(dir);
708 msi_set_sourcedir_props(package, FALSE);
711 msi_parse_command_line( package, szCommandLine );
713 msi_apply_transforms( package );
714 msi_apply_patches( package );
716 /* properties may have been added by a transform */
717 msi_clone_properties( package );
719 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
721 package->script->InWhatSequence |= SEQUENCE_UI;
722 rc = ACTION_ProcessUISequence(package);
723 ui = TRUE;
724 ui_exists = ui_sequence_exists(package);
725 if (rc == ERROR_SUCCESS || !ui_exists)
727 package->script->InWhatSequence |= SEQUENCE_EXEC;
728 rc = ACTION_ProcessExecSequence(package,ui_exists);
731 else
732 rc = ACTION_ProcessExecSequence(package,FALSE);
734 package->script->CurrentlyScripting= FALSE;
736 /* process the ending type action */
737 if (rc == ERROR_SUCCESS)
738 ACTION_PerformActionSequence(package,-1,ui);
739 else if (rc == ERROR_INSTALL_USEREXIT)
740 ACTION_PerformActionSequence(package,-2,ui);
741 else if (rc == ERROR_INSTALL_SUSPEND)
742 ACTION_PerformActionSequence(package,-4,ui);
743 else /* failed */
744 ACTION_PerformActionSequence(package,-3,ui);
746 /* finish up running custom actions */
747 ACTION_FinishCustomActions(package);
749 return rc;
752 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
754 UINT rc = ERROR_SUCCESS;
755 MSIRECORD * row = 0;
756 static const WCHAR ExecSeqQuery[] =
757 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
758 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
759 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
760 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
762 static const WCHAR UISeqQuery[] =
763 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
764 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
765 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
766 ' ', '=',' ','%','i',0};
768 if (UI)
769 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
770 else
771 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
773 if (row)
775 LPCWSTR action, cond;
777 TRACE("Running the actions\n");
779 /* check conditions */
780 cond = MSI_RecordGetString(row,2);
782 /* this is a hack to skip errors in the condition code */
783 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
784 goto end;
786 action = MSI_RecordGetString(row,1);
787 if (!action)
789 ERR("failed to fetch action\n");
790 rc = ERROR_FUNCTION_FAILED;
791 goto end;
794 if (UI)
795 rc = ACTION_PerformUIAction(package,action,-1);
796 else
797 rc = ACTION_PerformAction(package,action,-1,FALSE);
798 end:
799 msiobj_release(&row->hdr);
801 else
802 rc = ERROR_SUCCESS;
804 return rc;
807 typedef struct {
808 MSIPACKAGE* package;
809 BOOL UI;
810 } iterate_action_param;
812 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
814 iterate_action_param *iap= (iterate_action_param*)param;
815 UINT rc;
816 LPCWSTR cond, action;
818 action = MSI_RecordGetString(row,1);
819 if (!action)
821 ERR("Error is retrieving action name\n");
822 return ERROR_FUNCTION_FAILED;
825 /* check conditions */
826 cond = MSI_RecordGetString(row,2);
828 /* this is a hack to skip errors in the condition code */
829 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
831 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
832 return ERROR_SUCCESS;
835 if (iap->UI)
836 rc = ACTION_PerformUIAction(iap->package,action,-1);
837 else
838 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
840 msi_dialog_check_messages( NULL );
842 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
843 rc = iap->package->CurrentInstallState;
845 if (rc == ERROR_FUNCTION_NOT_CALLED)
846 rc = ERROR_SUCCESS;
848 if (rc != ERROR_SUCCESS)
849 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
851 return rc;
854 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
856 MSIQUERY * view;
857 UINT r;
858 static const WCHAR query[] =
859 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
860 '`','%','s','`',
861 ' ','W','H','E','R','E',' ',
862 '`','S','e','q','u','e','n','c','e','`',' ',
863 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
864 '`','S','e','q','u','e','n','c','e','`',0};
865 iterate_action_param iap;
868 * FIXME: probably should be checking UILevel in the
869 * ACTION_PerformUIAction/ACTION_PerformAction
870 * rather than saving the UI level here. Those
871 * two functions can be merged too.
873 iap.package = package;
874 iap.UI = TRUE;
876 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
878 r = MSI_OpenQuery( package->db, &view, query, szTable );
879 if (r == ERROR_SUCCESS)
881 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
882 msiobj_release(&view->hdr);
885 return r;
888 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
890 MSIQUERY * view;
891 UINT rc;
892 static const WCHAR ExecSeqQuery[] =
893 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
894 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
895 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
896 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
897 'O','R','D','E','R',' ', 'B','Y',' ',
898 '`','S','e','q','u','e','n','c','e','`',0 };
899 MSIRECORD * row = 0;
900 static const WCHAR IVQuery[] =
901 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
902 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
903 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
904 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
905 ' ','\'', 'I','n','s','t','a','l','l',
906 'V','a','l','i','d','a','t','e','\'', 0};
907 INT seq = 0;
908 iterate_action_param iap;
910 iap.package = package;
911 iap.UI = FALSE;
913 if (package->script->ExecuteSequenceRun)
915 TRACE("Execute Sequence already Run\n");
916 return ERROR_SUCCESS;
919 package->script->ExecuteSequenceRun = TRUE;
921 /* get the sequence number */
922 if (UIran)
924 row = MSI_QueryGetRecord(package->db, IVQuery);
925 if( !row )
926 return ERROR_FUNCTION_FAILED;
927 seq = MSI_RecordGetInteger(row,1);
928 msiobj_release(&row->hdr);
931 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
932 if (rc == ERROR_SUCCESS)
934 TRACE("Running the actions\n");
936 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
937 msiobj_release(&view->hdr);
940 return rc;
943 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
945 MSIQUERY * view;
946 UINT rc;
947 static const WCHAR ExecSeqQuery [] =
948 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
949 '`','I','n','s','t','a','l','l',
950 'U','I','S','e','q','u','e','n','c','e','`',
951 ' ','W','H','E','R','E',' ',
952 '`','S','e','q','u','e','n','c','e','`',' ',
953 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
954 '`','S','e','q','u','e','n','c','e','`',0};
955 iterate_action_param iap;
957 iap.package = package;
958 iap.UI = TRUE;
960 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
962 if (rc == ERROR_SUCCESS)
964 TRACE("Running the actions\n");
966 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
967 msiobj_release(&view->hdr);
970 return rc;
973 /********************************************************
974 * ACTION helper functions and functions that perform the actions
975 *******************************************************/
976 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
977 UINT* rc, BOOL force )
979 BOOL ret = FALSE;
980 BOOL run = force;
981 int i;
983 if (!run && !package->script->CurrentlyScripting)
984 run = TRUE;
986 if (!run)
988 if (strcmpW(action,szInstallFinalize) == 0 ||
989 strcmpW(action,szInstallExecute) == 0 ||
990 strcmpW(action,szInstallExecuteAgain) == 0)
991 run = TRUE;
994 i = 0;
995 while (StandardActions[i].action != NULL)
997 if (strcmpW(StandardActions[i].action, action)==0)
999 if (!run)
1001 ui_actioninfo(package, action, TRUE, 0);
1002 *rc = schedule_action(package,INSTALL_SCRIPT,action);
1003 ui_actioninfo(package, action, FALSE, *rc);
1005 else
1007 ui_actionstart(package, action);
1008 if (StandardActions[i].handler)
1010 *rc = StandardActions[i].handler(package);
1012 else
1014 FIXME("unhandled standard action %s\n",debugstr_w(action));
1015 *rc = ERROR_SUCCESS;
1018 ret = TRUE;
1019 break;
1021 i++;
1023 return ret;
1026 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1027 UINT* rc, UINT script, BOOL force )
1029 BOOL ret=FALSE;
1030 UINT arc;
1032 arc = ACTION_CustomAction(package, action, script, force);
1034 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1036 *rc = arc;
1037 ret = TRUE;
1039 return ret;
1043 * A lot of actions are really important even if they don't do anything
1044 * explicit... Lots of properties are set at the beginning of the installation
1045 * CostFinalize does a bunch of work to translate the directories and such
1047 * But until I get write access to the database that is hard, so I am going to
1048 * hack it to see if I can get something to run.
1050 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1052 UINT rc = ERROR_SUCCESS;
1053 BOOL handled;
1055 TRACE("Performing action (%s)\n",debugstr_w(action));
1057 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1059 if (!handled)
1060 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1062 if (!handled)
1064 FIXME("unhandled msi action %s\n",debugstr_w(action));
1065 rc = ERROR_FUNCTION_NOT_CALLED;
1068 return rc;
1071 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1073 UINT rc = ERROR_SUCCESS;
1074 BOOL handled = FALSE;
1076 TRACE("Performing action (%s)\n",debugstr_w(action));
1078 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1080 if (!handled)
1081 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1083 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1084 handled = TRUE;
1086 if (!handled)
1088 FIXME("unhandled msi action %s\n",debugstr_w(action));
1089 rc = ERROR_FUNCTION_NOT_CALLED;
1092 return rc;
1097 * Actual Action Handlers
1100 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1102 MSIPACKAGE *package = (MSIPACKAGE*)param;
1103 LPCWSTR dir;
1104 LPWSTR full_path;
1105 MSIRECORD *uirow;
1106 MSIFOLDER *folder;
1108 dir = MSI_RecordGetString(row,1);
1109 if (!dir)
1111 ERR("Unable to get folder id\n");
1112 return ERROR_SUCCESS;
1115 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1116 if (!full_path)
1118 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1119 return ERROR_SUCCESS;
1122 TRACE("Folder is %s\n",debugstr_w(full_path));
1124 /* UI stuff */
1125 uirow = MSI_CreateRecord(1);
1126 MSI_RecordSetStringW(uirow,1,full_path);
1127 ui_actiondata(package,szCreateFolders,uirow);
1128 msiobj_release( &uirow->hdr );
1130 if (folder->State == 0)
1131 create_full_pathW(full_path);
1133 folder->State = 3;
1135 msi_free(full_path);
1136 return ERROR_SUCCESS;
1139 /* FIXME: probably should merge this with the above function */
1140 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1142 UINT rc = ERROR_SUCCESS;
1143 MSIFOLDER *folder;
1144 LPWSTR install_path;
1146 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1147 if (!install_path)
1148 return ERROR_FUNCTION_FAILED;
1150 /* create the path */
1151 if (folder->State == 0)
1153 create_full_pathW(install_path);
1154 folder->State = 2;
1156 msi_free(install_path);
1158 return rc;
1161 UINT msi_create_component_directories( MSIPACKAGE *package )
1163 MSICOMPONENT *comp;
1165 /* create all the folders required by the components are going to install */
1166 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1168 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1169 continue;
1170 msi_create_directory( package, comp->Directory );
1173 return ERROR_SUCCESS;
1177 * Also we cannot enable/disable components either, so for now I am just going
1178 * to do all the directories for all the components.
1180 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1182 static const WCHAR ExecSeqQuery[] =
1183 {'S','E','L','E','C','T',' ',
1184 '`','D','i','r','e','c','t','o','r','y','_','`',
1185 ' ','F','R','O','M',' ',
1186 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1187 UINT rc;
1188 MSIQUERY *view;
1190 /* create all the empty folders specified in the CreateFolder table */
1191 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1192 if (rc != ERROR_SUCCESS)
1193 return ERROR_SUCCESS;
1195 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1196 msiobj_release(&view->hdr);
1198 msi_create_component_directories( package );
1200 return rc;
1203 static UINT load_component( MSIRECORD *row, LPVOID param )
1205 MSIPACKAGE *package = param;
1206 MSICOMPONENT *comp;
1208 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1209 if (!comp)
1210 return ERROR_FUNCTION_FAILED;
1212 list_add_tail( &package->components, &comp->entry );
1214 /* fill in the data */
1215 comp->Component = msi_dup_record_field( row, 1 );
1217 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1219 comp->ComponentId = msi_dup_record_field( row, 2 );
1220 comp->Directory = msi_dup_record_field( row, 3 );
1221 comp->Attributes = MSI_RecordGetInteger(row,4);
1222 comp->Condition = msi_dup_record_field( row, 5 );
1223 comp->KeyPath = msi_dup_record_field( row, 6 );
1225 comp->Installed = INSTALLSTATE_UNKNOWN;
1226 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1228 return ERROR_SUCCESS;
1231 static UINT load_all_components( MSIPACKAGE *package )
1233 static const WCHAR query[] = {
1234 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1235 '`','C','o','m','p','o','n','e','n','t','`',0 };
1236 MSIQUERY *view;
1237 UINT r;
1239 if (!list_empty(&package->components))
1240 return ERROR_SUCCESS;
1242 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1243 if (r != ERROR_SUCCESS)
1244 return r;
1246 r = MSI_IterateRecords(view, NULL, load_component, package);
1247 msiobj_release(&view->hdr);
1248 return r;
1251 typedef struct {
1252 MSIPACKAGE *package;
1253 MSIFEATURE *feature;
1254 } _ilfs;
1256 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1258 ComponentList *cl;
1260 cl = msi_alloc( sizeof (*cl) );
1261 if ( !cl )
1262 return ERROR_NOT_ENOUGH_MEMORY;
1263 cl->component = comp;
1264 list_add_tail( &feature->Components, &cl->entry );
1266 return ERROR_SUCCESS;
1269 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1271 FeatureList *fl;
1273 fl = msi_alloc( sizeof(*fl) );
1274 if ( !fl )
1275 return ERROR_NOT_ENOUGH_MEMORY;
1276 fl->feature = child;
1277 list_add_tail( &parent->Children, &fl->entry );
1279 return ERROR_SUCCESS;
1282 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1284 _ilfs* ilfs= (_ilfs*)param;
1285 LPCWSTR component;
1286 MSICOMPONENT *comp;
1288 component = MSI_RecordGetString(row,1);
1290 /* check to see if the component is already loaded */
1291 comp = get_loaded_component( ilfs->package, component );
1292 if (!comp)
1294 ERR("unknown component %s\n", debugstr_w(component));
1295 return ERROR_FUNCTION_FAILED;
1298 add_feature_component( ilfs->feature, comp );
1299 comp->Enabled = TRUE;
1301 return ERROR_SUCCESS;
1304 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1306 MSIFEATURE *feature;
1308 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1310 if ( !lstrcmpW( feature->Feature, name ) )
1311 return feature;
1314 return NULL;
1317 static UINT load_feature(MSIRECORD * row, LPVOID param)
1319 MSIPACKAGE* package = (MSIPACKAGE*)param;
1320 MSIFEATURE* feature;
1321 static const WCHAR Query1[] =
1322 {'S','E','L','E','C','T',' ',
1323 '`','C','o','m','p','o','n','e','n','t','_','`',
1324 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1325 'C','o','m','p','o','n','e','n','t','s','`',' ',
1326 'W','H','E','R','E',' ',
1327 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1328 MSIQUERY * view;
1329 UINT rc;
1330 _ilfs ilfs;
1332 /* fill in the data */
1334 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1335 if (!feature)
1336 return ERROR_NOT_ENOUGH_MEMORY;
1338 list_init( &feature->Children );
1339 list_init( &feature->Components );
1341 feature->Feature = msi_dup_record_field( row, 1 );
1343 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1345 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1346 feature->Title = msi_dup_record_field( row, 3 );
1347 feature->Description = msi_dup_record_field( row, 4 );
1349 if (!MSI_RecordIsNull(row,5))
1350 feature->Display = MSI_RecordGetInteger(row,5);
1352 feature->Level= MSI_RecordGetInteger(row,6);
1353 feature->Directory = msi_dup_record_field( row, 7 );
1354 feature->Attributes = MSI_RecordGetInteger(row,8);
1356 feature->Installed = INSTALLSTATE_UNKNOWN;
1357 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1359 list_add_tail( &package->features, &feature->entry );
1361 /* load feature components */
1363 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1364 if (rc != ERROR_SUCCESS)
1365 return ERROR_SUCCESS;
1367 ilfs.package = package;
1368 ilfs.feature = feature;
1370 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1371 msiobj_release(&view->hdr);
1373 return ERROR_SUCCESS;
1376 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1378 MSIPACKAGE* package = (MSIPACKAGE*)param;
1379 MSIFEATURE *parent, *child;
1381 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1382 if (!child)
1383 return ERROR_FUNCTION_FAILED;
1385 if (!child->Feature_Parent)
1386 return ERROR_SUCCESS;
1388 parent = find_feature_by_name( package, child->Feature_Parent );
1389 if (!parent)
1390 return ERROR_FUNCTION_FAILED;
1392 add_feature_child( parent, child );
1393 return ERROR_SUCCESS;
1396 static UINT load_all_features( MSIPACKAGE *package )
1398 static const WCHAR query[] = {
1399 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1400 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1401 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1402 MSIQUERY *view;
1403 UINT r;
1405 if (!list_empty(&package->features))
1406 return ERROR_SUCCESS;
1408 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1409 if (r != ERROR_SUCCESS)
1410 return r;
1412 r = MSI_IterateRecords( view, NULL, load_feature, package );
1413 if (r != ERROR_SUCCESS)
1414 return r;
1416 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1417 msiobj_release( &view->hdr );
1419 return r;
1422 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1424 if (!p)
1425 return p;
1426 p = strchrW(p, ch);
1427 if (!p)
1428 return p;
1429 *p = 0;
1430 return p+1;
1433 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1435 static const WCHAR query[] = {
1436 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1437 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1438 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1439 MSIQUERY *view = NULL;
1440 MSIRECORD *row = NULL;
1441 UINT r;
1443 TRACE("%s\n", debugstr_w(file->File));
1445 r = MSI_OpenQuery(package->db, &view, query, file->File);
1446 if (r != ERROR_SUCCESS)
1447 goto done;
1449 r = MSI_ViewExecute(view, NULL);
1450 if (r != ERROR_SUCCESS)
1451 goto done;
1453 r = MSI_ViewFetch(view, &row);
1454 if (r != ERROR_SUCCESS)
1455 goto done;
1457 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1458 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1459 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1460 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1461 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1463 done:
1464 if (view) msiobj_release(&view->hdr);
1465 if (row) msiobj_release(&row->hdr);
1466 return r;
1469 static UINT load_file(MSIRECORD *row, LPVOID param)
1471 MSIPACKAGE* package = (MSIPACKAGE*)param;
1472 LPCWSTR component;
1473 MSIFILE *file;
1475 /* fill in the data */
1477 file = msi_alloc_zero( sizeof (MSIFILE) );
1478 if (!file)
1479 return ERROR_NOT_ENOUGH_MEMORY;
1481 file->File = msi_dup_record_field( row, 1 );
1483 component = MSI_RecordGetString( row, 2 );
1484 file->Component = get_loaded_component( package, component );
1486 if (!file->Component)
1487 ERR("Unfound Component %s\n",debugstr_w(component));
1489 file->FileName = msi_dup_record_field( row, 3 );
1490 reduce_to_longfilename( file->FileName );
1492 file->ShortName = msi_dup_record_field( row, 3 );
1493 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1495 file->FileSize = MSI_RecordGetInteger( row, 4 );
1496 file->Version = msi_dup_record_field( row, 5 );
1497 file->Language = msi_dup_record_field( row, 6 );
1498 file->Attributes = MSI_RecordGetInteger( row, 7 );
1499 file->Sequence = MSI_RecordGetInteger( row, 8 );
1501 file->state = msifs_invalid;
1503 /* if the compressed bits are not set in the file attributes,
1504 * then read the information from the package word count property
1506 if (file->Attributes & msidbFileAttributesCompressed)
1508 file->IsCompressed = TRUE;
1510 else if (file->Attributes & msidbFileAttributesNoncompressed)
1512 file->IsCompressed = FALSE;
1514 else
1516 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1519 load_file_hash(package, file);
1521 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1523 list_add_tail( &package->files, &file->entry );
1525 return ERROR_SUCCESS;
1528 static UINT load_all_files(MSIPACKAGE *package)
1530 MSIQUERY * view;
1531 UINT rc;
1532 static const WCHAR Query[] =
1533 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1534 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1535 '`','S','e','q','u','e','n','c','e','`', 0};
1537 if (!list_empty(&package->files))
1538 return ERROR_SUCCESS;
1540 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1541 if (rc != ERROR_SUCCESS)
1542 return ERROR_SUCCESS;
1544 rc = MSI_IterateRecords(view, NULL, load_file, package);
1545 msiobj_release(&view->hdr);
1547 return ERROR_SUCCESS;
1550 static UINT load_folder( MSIRECORD *row, LPVOID param )
1552 MSIPACKAGE *package = param;
1553 static const WCHAR szDot[] = { '.',0 };
1554 static WCHAR szEmpty[] = { 0 };
1555 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1556 MSIFOLDER *folder;
1558 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1559 if (!folder)
1560 return ERROR_NOT_ENOUGH_MEMORY;
1562 folder->Directory = msi_dup_record_field( row, 1 );
1564 TRACE("%s\n", debugstr_w(folder->Directory));
1566 p = msi_dup_record_field(row, 3);
1568 /* split src and target dir */
1569 tgt_short = p;
1570 src_short = folder_split_path( p, ':' );
1572 /* split the long and short paths */
1573 tgt_long = folder_split_path( tgt_short, '|' );
1574 src_long = folder_split_path( src_short, '|' );
1576 /* check for no-op dirs */
1577 if (!lstrcmpW(szDot, tgt_short))
1578 tgt_short = szEmpty;
1579 if (!lstrcmpW(szDot, src_short))
1580 src_short = szEmpty;
1582 if (!tgt_long)
1583 tgt_long = tgt_short;
1585 if (!src_short) {
1586 src_short = tgt_short;
1587 src_long = tgt_long;
1590 if (!src_long)
1591 src_long = src_short;
1593 /* FIXME: use the target short path too */
1594 folder->TargetDefault = strdupW(tgt_long);
1595 folder->SourceShortPath = strdupW(src_short);
1596 folder->SourceLongPath = strdupW(src_long);
1597 msi_free(p);
1599 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1600 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1601 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1603 folder->Parent = msi_dup_record_field( row, 2 );
1605 folder->Property = msi_dup_property( package, folder->Directory );
1607 list_add_tail( &package->folders, &folder->entry );
1609 TRACE("returning %p\n", folder);
1611 return ERROR_SUCCESS;
1614 static UINT load_all_folders( MSIPACKAGE *package )
1616 static const WCHAR query[] = {
1617 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1618 '`','D','i','r','e','c','t','o','r','y','`',0 };
1619 MSIQUERY *view;
1620 UINT r;
1622 if (!list_empty(&package->folders))
1623 return ERROR_SUCCESS;
1625 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1626 if (r != ERROR_SUCCESS)
1627 return r;
1629 r = MSI_IterateRecords(view, NULL, load_folder, package);
1630 msiobj_release(&view->hdr);
1631 return r;
1635 * I am not doing any of the costing functionality yet.
1636 * Mostly looking at doing the Component and Feature loading
1638 * The native MSI does A LOT of modification to tables here. Mostly adding
1639 * a lot of temporary columns to the Feature and Component tables.
1641 * note: Native msi also tracks the short filename. But I am only going to
1642 * track the long ones. Also looking at this directory table
1643 * it appears that the directory table does not get the parents
1644 * resolved base on property only based on their entries in the
1645 * directory table.
1647 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1649 static const WCHAR szCosting[] =
1650 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1651 static const WCHAR szZero[] = { '0', 0 };
1653 MSI_SetPropertyW(package, szCosting, szZero);
1654 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1656 load_all_components( package );
1657 load_all_features( package );
1658 load_all_files( package );
1659 load_all_folders( package );
1661 return ERROR_SUCCESS;
1664 static UINT execute_script(MSIPACKAGE *package, UINT script )
1666 UINT i;
1667 UINT rc = ERROR_SUCCESS;
1669 TRACE("Executing Script %i\n",script);
1671 if (!package->script)
1673 ERR("no script!\n");
1674 return ERROR_FUNCTION_FAILED;
1677 for (i = 0; i < package->script->ActionCount[script]; i++)
1679 LPWSTR action;
1680 action = package->script->Actions[script][i];
1681 ui_actionstart(package, action);
1682 TRACE("Executing Action (%s)\n",debugstr_w(action));
1683 rc = ACTION_PerformAction(package, action, script, TRUE);
1684 if (rc != ERROR_SUCCESS)
1685 break;
1687 msi_free_action_script(package, script);
1688 return rc;
1691 static UINT ACTION_FileCost(MSIPACKAGE *package)
1693 return ERROR_SUCCESS;
1696 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1698 MSICOMPONENT *comp;
1700 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1702 INSTALLSTATE res;
1704 if (!comp->ComponentId)
1705 continue;
1707 res = MsiGetComponentPathW( package->ProductCode,
1708 comp->ComponentId, NULL, NULL);
1709 if (res < 0)
1710 res = INSTALLSTATE_ABSENT;
1711 comp->Installed = res;
1715 /* scan for and update current install states */
1716 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1718 MSICOMPONENT *comp;
1719 MSIFEATURE *feature;
1721 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1723 ComponentList *cl;
1724 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1726 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1728 comp= cl->component;
1730 if (!comp->ComponentId)
1732 res = INSTALLSTATE_ABSENT;
1733 break;
1736 if (res == INSTALLSTATE_ABSENT)
1737 res = comp->Installed;
1738 else
1740 if (res == comp->Installed)
1741 continue;
1743 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1744 res != INSTALLSTATE_SOURCE)
1746 res = INSTALLSTATE_INCOMPLETE;
1750 feature->Installed = res;
1754 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1755 INSTALLSTATE state)
1757 static const WCHAR all[]={'A','L','L',0};
1758 LPWSTR override;
1759 MSIFEATURE *feature;
1761 override = msi_dup_property( package, property );
1762 if (!override)
1763 return FALSE;
1765 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1767 if (strcmpiW(override,all)==0)
1768 msi_feature_set_state( feature, state );
1769 else
1771 LPWSTR ptr = override;
1772 LPWSTR ptr2 = strchrW(override,',');
1774 while (ptr)
1776 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1777 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1779 msi_feature_set_state( feature, state );
1780 break;
1782 if (ptr2)
1784 ptr=ptr2+1;
1785 ptr2 = strchrW(ptr,',');
1787 else
1788 break;
1792 msi_free(override);
1794 return TRUE;
1797 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1799 int install_level;
1800 static const WCHAR szlevel[] =
1801 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1802 static const WCHAR szAddLocal[] =
1803 {'A','D','D','L','O','C','A','L',0};
1804 static const WCHAR szAddSource[] =
1805 {'A','D','D','S','O','U','R','C','E',0};
1806 static const WCHAR szRemove[] =
1807 {'R','E','M','O','V','E',0};
1808 static const WCHAR szReinstall[] =
1809 {'R','E','I','N','S','T','A','L','L',0};
1810 BOOL override = FALSE;
1811 MSICOMPONENT* component;
1812 MSIFEATURE *feature;
1815 /* I do not know if this is where it should happen.. but */
1817 TRACE("Checking Install Level\n");
1819 install_level = msi_get_property_int( package, szlevel, 1 );
1821 /* ok here is the _real_ rub
1822 * all these activation/deactivation things happen in order and things
1823 * later on the list override things earlier on the list.
1824 * 1) INSTALLLEVEL processing
1825 * 2) ADDLOCAL
1826 * 3) REMOVE
1827 * 4) ADDSOURCE
1828 * 5) ADDDEFAULT
1829 * 6) REINSTALL
1830 * 7) COMPADDLOCAL
1831 * 8) COMPADDSOURCE
1832 * 9) FILEADDLOCAL
1833 * 10) FILEADDSOURCE
1834 * 11) FILEADDDEFAULT
1835 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1836 * ignored for all the features. seems strange, especially since it is not
1837 * documented anywhere, but it is how it works.
1839 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1840 * REMOVE are the big ones, since we don't handle administrative installs
1841 * yet anyway.
1843 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1844 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1845 override |= process_state_property(package,szAddSource,INSTALLSTATE_SOURCE);
1846 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1848 if (!override)
1850 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1852 BOOL feature_state = ((feature->Level > 0) &&
1853 (feature->Level <= install_level));
1855 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1857 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1858 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1859 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1860 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1861 else
1862 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1866 /* disable child features of unselected parent features */
1867 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1869 FeatureList *fl;
1871 if (feature->Level > 0 && feature->Level <= install_level)
1872 continue;
1874 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1875 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1878 else
1880 /* set the Preselected Property */
1881 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1882 static const WCHAR szOne[] = { '1', 0 };
1884 MSI_SetPropertyW(package,szPreselected,szOne);
1888 * now we want to enable or disable components base on feature
1891 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1893 ComponentList *cl;
1895 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1896 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1898 /* features with components that have compressed files are made local */
1899 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1901 if (cl->component->Enabled &&
1902 cl->component->ForceLocalState &&
1903 feature->Action == INSTALLSTATE_SOURCE)
1905 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1906 break;
1910 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1912 component = cl->component;
1914 if (!component->Enabled)
1915 continue;
1917 switch (feature->Action)
1919 case INSTALLSTATE_ABSENT:
1920 component->anyAbsent = 1;
1921 break;
1922 case INSTALLSTATE_ADVERTISED:
1923 component->hasAdvertiseFeature = 1;
1924 break;
1925 case INSTALLSTATE_SOURCE:
1926 component->hasSourceFeature = 1;
1927 break;
1928 case INSTALLSTATE_LOCAL:
1929 component->hasLocalFeature = 1;
1930 break;
1931 case INSTALLSTATE_DEFAULT:
1932 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1933 component->hasAdvertiseFeature = 1;
1934 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1935 component->hasSourceFeature = 1;
1936 else
1937 component->hasLocalFeature = 1;
1938 break;
1939 default:
1940 break;
1945 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1947 /* if the component isn't enabled, leave it alone */
1948 if (!component->Enabled)
1949 continue;
1951 /* check if it's local or source */
1952 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1953 (component->hasLocalFeature || component->hasSourceFeature))
1955 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1956 !component->ForceLocalState)
1957 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1958 else
1959 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1960 continue;
1963 /* if any feature is local, the component must be local too */
1964 if (component->hasLocalFeature)
1966 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1967 continue;
1970 if (component->hasSourceFeature)
1972 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1973 continue;
1976 if (component->hasAdvertiseFeature)
1978 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1979 continue;
1982 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1983 if (component->anyAbsent)
1984 msi_component_set_state(component, INSTALLSTATE_ABSENT);
1987 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1989 if (component->Action == INSTALLSTATE_DEFAULT)
1991 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1992 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1995 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1996 debugstr_w(component->Component), component->Installed, component->Action);
2000 return ERROR_SUCCESS;
2003 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
2005 MSIPACKAGE *package = (MSIPACKAGE*)param;
2006 LPCWSTR name;
2007 LPWSTR path;
2008 MSIFOLDER *f;
2010 name = MSI_RecordGetString(row,1);
2012 f = get_loaded_folder(package, name);
2013 if (!f) return ERROR_SUCCESS;
2015 /* reset the ResolvedTarget */
2016 msi_free(f->ResolvedTarget);
2017 f->ResolvedTarget = NULL;
2019 /* This helper function now does ALL the work */
2020 TRACE("Dir %s ...\n",debugstr_w(name));
2021 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2022 TRACE("resolves to %s\n",debugstr_w(path));
2023 msi_free(path);
2025 return ERROR_SUCCESS;
2028 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2030 MSIPACKAGE *package = (MSIPACKAGE*)param;
2031 LPCWSTR name;
2032 MSIFEATURE *feature;
2034 name = MSI_RecordGetString( row, 1 );
2036 feature = get_loaded_feature( package, name );
2037 if (!feature)
2038 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2039 else
2041 LPCWSTR Condition;
2042 Condition = MSI_RecordGetString(row,3);
2044 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2046 int level = MSI_RecordGetInteger(row,2);
2047 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
2048 feature->Level = level;
2051 return ERROR_SUCCESS;
2054 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2056 static const WCHAR name_fmt[] =
2057 {'%','u','.','%','u','.','%','u','.','%','u',0};
2058 static WCHAR name[] = {'\\',0};
2059 VS_FIXEDFILEINFO *lpVer;
2060 WCHAR filever[0x100];
2061 LPVOID version;
2062 DWORD versize;
2063 DWORD handle;
2064 UINT sz;
2066 TRACE("%s\n", debugstr_w(filename));
2068 versize = GetFileVersionInfoSizeW( filename, &handle );
2069 if (!versize)
2070 return NULL;
2072 version = msi_alloc( versize );
2073 GetFileVersionInfoW( filename, 0, versize, version );
2075 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2077 msi_free( version );
2078 return NULL;
2081 sprintfW( filever, name_fmt,
2082 HIWORD(lpVer->dwFileVersionMS),
2083 LOWORD(lpVer->dwFileVersionMS),
2084 HIWORD(lpVer->dwFileVersionLS),
2085 LOWORD(lpVer->dwFileVersionLS));
2087 msi_free( version );
2089 return strdupW( filever );
2092 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2094 LPWSTR file_version;
2095 MSIFILE *file;
2097 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2099 MSICOMPONENT* comp = file->Component;
2100 LPWSTR p;
2102 if (!comp)
2103 continue;
2105 if (file->IsCompressed)
2106 comp->ForceLocalState = TRUE;
2108 /* calculate target */
2109 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2111 msi_free(file->TargetPath);
2113 TRACE("file %s is named %s\n",
2114 debugstr_w(file->File), debugstr_w(file->FileName));
2116 file->TargetPath = build_directory_name(2, p, file->FileName);
2118 msi_free(p);
2120 TRACE("file %s resolves to %s\n",
2121 debugstr_w(file->File), debugstr_w(file->TargetPath));
2123 /* don't check files of components that aren't installed */
2124 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2125 comp->Installed == INSTALLSTATE_ABSENT)
2127 file->state = msifs_missing; /* assume files are missing */
2128 continue;
2131 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2133 file->state = msifs_missing;
2134 comp->Cost += file->FileSize;
2135 comp->Installed = INSTALLSTATE_INCOMPLETE;
2136 continue;
2139 if (file->Version &&
2140 (file_version = msi_get_disk_file_version( file->TargetPath )))
2142 TRACE("new %s old %s\n", debugstr_w(file->Version),
2143 debugstr_w(file_version));
2144 /* FIXME: seems like a bad way to compare version numbers */
2145 if (lstrcmpiW(file_version, file->Version)<0)
2147 file->state = msifs_overwrite;
2148 comp->Cost += file->FileSize;
2149 comp->Installed = INSTALLSTATE_INCOMPLETE;
2151 else
2152 file->state = msifs_present;
2153 msi_free( file_version );
2155 else
2156 file->state = msifs_present;
2159 return ERROR_SUCCESS;
2163 * A lot is done in this function aside from just the costing.
2164 * The costing needs to be implemented at some point but for now I am going
2165 * to focus on the directory building
2168 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2170 static const WCHAR ExecSeqQuery[] =
2171 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2172 '`','D','i','r','e','c','t','o','r','y','`',0};
2173 static const WCHAR ConditionQuery[] =
2174 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2175 '`','C','o','n','d','i','t','i','o','n','`',0};
2176 static const WCHAR szCosting[] =
2177 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2178 static const WCHAR szlevel[] =
2179 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2180 static const WCHAR szOne[] = { '1', 0 };
2181 MSICOMPONENT *comp;
2182 UINT rc;
2183 MSIQUERY * view;
2184 LPWSTR level;
2186 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2187 return ERROR_SUCCESS;
2189 TRACE("Building Directory properties\n");
2191 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2192 if (rc == ERROR_SUCCESS)
2194 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2195 package);
2196 msiobj_release(&view->hdr);
2199 /* read components states from the registry */
2200 ACTION_GetComponentInstallStates(package);
2202 TRACE("File calculations\n");
2203 msi_check_file_install_states( package );
2205 TRACE("Evaluating Condition Table\n");
2207 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2208 if (rc == ERROR_SUCCESS)
2210 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2211 package);
2212 msiobj_release(&view->hdr);
2215 TRACE("Enabling or Disabling Components\n");
2216 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2218 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2220 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2221 comp->Enabled = FALSE;
2225 MSI_SetPropertyW(package,szCosting,szOne);
2226 /* set default run level if not set */
2227 level = msi_dup_property( package, szlevel );
2228 if (!level)
2229 MSI_SetPropertyW(package,szlevel, szOne);
2230 msi_free(level);
2232 ACTION_UpdateFeatureInstallStates(package);
2234 return MSI_SetFeatureStates(package);
2237 /* OK this value is "interpreted" and then formatted based on the
2238 first few characters */
2239 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2240 DWORD *size)
2242 LPSTR data = NULL;
2243 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2245 if (value[1]=='x')
2247 LPWSTR ptr;
2248 CHAR byte[5];
2249 LPWSTR deformated = NULL;
2250 int count;
2252 deformat_string(package, &value[2], &deformated);
2254 /* binary value type */
2255 ptr = deformated;
2256 *type = REG_BINARY;
2257 if (strlenW(ptr)%2)
2258 *size = (strlenW(ptr)/2)+1;
2259 else
2260 *size = strlenW(ptr)/2;
2262 data = msi_alloc(*size);
2264 byte[0] = '0';
2265 byte[1] = 'x';
2266 byte[4] = 0;
2267 count = 0;
2268 /* if uneven pad with a zero in front */
2269 if (strlenW(ptr)%2)
2271 byte[2]= '0';
2272 byte[3]= *ptr;
2273 ptr++;
2274 data[count] = (BYTE)strtol(byte,NULL,0);
2275 count ++;
2276 TRACE("Uneven byte count\n");
2278 while (*ptr)
2280 byte[2]= *ptr;
2281 ptr++;
2282 byte[3]= *ptr;
2283 ptr++;
2284 data[count] = (BYTE)strtol(byte,NULL,0);
2285 count ++;
2287 msi_free(deformated);
2289 TRACE("Data %i bytes(%i)\n",*size,count);
2291 else
2293 LPWSTR deformated;
2294 LPWSTR p;
2295 DWORD d = 0;
2296 deformat_string(package, &value[1], &deformated);
2298 *type=REG_DWORD;
2299 *size = sizeof(DWORD);
2300 data = msi_alloc(*size);
2301 p = deformated;
2302 if (*p == '-')
2303 p++;
2304 while (*p)
2306 if ( (*p < '0') || (*p > '9') )
2307 break;
2308 d *= 10;
2309 d += (*p - '0');
2310 p++;
2312 if (deformated[0] == '-')
2313 d = -d;
2314 *(LPDWORD)data = d;
2315 TRACE("DWORD %i\n",*(LPDWORD)data);
2317 msi_free(deformated);
2320 else
2322 static const WCHAR szMulti[] = {'[','~',']',0};
2323 LPCWSTR ptr;
2324 *type=REG_SZ;
2326 if (value[0]=='#')
2328 if (value[1]=='%')
2330 ptr = &value[2];
2331 *type=REG_EXPAND_SZ;
2333 else
2334 ptr = &value[1];
2336 else
2337 ptr=value;
2339 if (strstrW(value,szMulti))
2340 *type = REG_MULTI_SZ;
2342 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2344 return data;
2347 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2349 MSIPACKAGE *package = (MSIPACKAGE*)param;
2350 static const WCHAR szHCR[] =
2351 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2352 'R','O','O','T','\\',0};
2353 static const WCHAR szHCU[] =
2354 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2355 'U','S','E','R','\\',0};
2356 static const WCHAR szHLM[] =
2357 {'H','K','E','Y','_','L','O','C','A','L','_',
2358 'M','A','C','H','I','N','E','\\',0};
2359 static const WCHAR szHU[] =
2360 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2362 LPSTR value_data = NULL;
2363 HKEY root_key, hkey;
2364 DWORD type,size;
2365 LPWSTR deformated;
2366 LPCWSTR szRoot, component, name, key, value;
2367 MSICOMPONENT *comp;
2368 MSIRECORD * uirow;
2369 LPWSTR uikey;
2370 INT root;
2371 BOOL check_first = FALSE;
2372 UINT rc;
2374 ui_progress(package,2,0,0,0);
2376 value = NULL;
2377 key = NULL;
2378 uikey = NULL;
2379 name = NULL;
2381 component = MSI_RecordGetString(row, 6);
2382 comp = get_loaded_component(package,component);
2383 if (!comp)
2384 return ERROR_SUCCESS;
2386 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2388 TRACE("Skipping write due to disabled component %s\n",
2389 debugstr_w(component));
2391 comp->Action = comp->Installed;
2393 return ERROR_SUCCESS;
2396 comp->Action = INSTALLSTATE_LOCAL;
2398 name = MSI_RecordGetString(row, 4);
2399 if( MSI_RecordIsNull(row,5) && name )
2401 /* null values can have special meanings */
2402 if (name[0]=='-' && name[1] == 0)
2403 return ERROR_SUCCESS;
2404 else if ((name[0]=='+' && name[1] == 0) ||
2405 (name[0] == '*' && name[1] == 0))
2406 name = NULL;
2407 check_first = TRUE;
2410 root = MSI_RecordGetInteger(row,2);
2411 key = MSI_RecordGetString(row, 3);
2413 /* get the root key */
2414 switch (root)
2416 case -1:
2418 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2419 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2420 if (all_users && all_users[0] == '1')
2422 root_key = HKEY_LOCAL_MACHINE;
2423 szRoot = szHLM;
2425 else
2427 root_key = HKEY_CURRENT_USER;
2428 szRoot = szHCU;
2430 msi_free(all_users);
2432 break;
2433 case 0: root_key = HKEY_CLASSES_ROOT;
2434 szRoot = szHCR;
2435 break;
2436 case 1: root_key = HKEY_CURRENT_USER;
2437 szRoot = szHCU;
2438 break;
2439 case 2: root_key = HKEY_LOCAL_MACHINE;
2440 szRoot = szHLM;
2441 break;
2442 case 3: root_key = HKEY_USERS;
2443 szRoot = szHU;
2444 break;
2445 default:
2446 ERR("Unknown root %i\n",root);
2447 root_key=NULL;
2448 szRoot = NULL;
2449 break;
2451 if (!root_key)
2452 return ERROR_SUCCESS;
2454 deformat_string(package, key , &deformated);
2455 size = strlenW(deformated) + strlenW(szRoot) + 1;
2456 uikey = msi_alloc(size*sizeof(WCHAR));
2457 strcpyW(uikey,szRoot);
2458 strcatW(uikey,deformated);
2460 if (RegCreateKeyW( root_key, deformated, &hkey))
2462 ERR("Could not create key %s\n",debugstr_w(deformated));
2463 msi_free(deformated);
2464 msi_free(uikey);
2465 return ERROR_SUCCESS;
2467 msi_free(deformated);
2469 value = MSI_RecordGetString(row,5);
2470 if (value)
2471 value_data = parse_value(package, value, &type, &size);
2472 else
2474 static const WCHAR szEmpty[] = {0};
2475 value_data = (LPSTR)strdupW(szEmpty);
2476 size = 0;
2477 type = REG_SZ;
2480 deformat_string(package, name, &deformated);
2482 /* get the double nulls to terminate SZ_MULTI */
2483 if (type == REG_MULTI_SZ)
2484 size +=sizeof(WCHAR);
2486 if (!check_first)
2488 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2489 debugstr_w(uikey));
2490 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2492 else
2494 DWORD sz = 0;
2495 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2496 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2498 TRACE("value %s of %s checked already exists\n",
2499 debugstr_w(deformated), debugstr_w(uikey));
2501 else
2503 TRACE("Checked and setting value %s of %s\n",
2504 debugstr_w(deformated), debugstr_w(uikey));
2505 if (deformated || size)
2506 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2509 RegCloseKey(hkey);
2511 uirow = MSI_CreateRecord(3);
2512 MSI_RecordSetStringW(uirow,2,deformated);
2513 MSI_RecordSetStringW(uirow,1,uikey);
2515 if (type == REG_SZ)
2516 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2517 else
2518 MSI_RecordSetStringW(uirow,3,value);
2520 ui_actiondata(package,szWriteRegistryValues,uirow);
2521 msiobj_release( &uirow->hdr );
2523 msi_free(value_data);
2524 msi_free(deformated);
2525 msi_free(uikey);
2527 return ERROR_SUCCESS;
2530 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2532 UINT rc;
2533 MSIQUERY * view;
2534 static const WCHAR ExecSeqQuery[] =
2535 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2536 '`','R','e','g','i','s','t','r','y','`',0 };
2538 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2539 if (rc != ERROR_SUCCESS)
2540 return ERROR_SUCCESS;
2542 /* increment progress bar each time action data is sent */
2543 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2545 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2547 msiobj_release(&view->hdr);
2548 return rc;
2551 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2553 package->script->CurrentlyScripting = TRUE;
2555 return ERROR_SUCCESS;
2559 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2561 MSICOMPONENT *comp;
2562 DWORD progress = 0;
2563 DWORD total = 0;
2564 static const WCHAR q1[]=
2565 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2566 '`','R','e','g','i','s','t','r','y','`',0};
2567 UINT rc;
2568 MSIQUERY * view;
2569 MSIFEATURE *feature;
2570 MSIFILE *file;
2572 TRACE("InstallValidate\n");
2574 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2575 if (rc == ERROR_SUCCESS)
2577 MSI_IterateRecords( view, &progress, NULL, package );
2578 msiobj_release( &view->hdr );
2579 total += progress * REG_PROGRESS_VALUE;
2582 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2583 total += COMPONENT_PROGRESS_VALUE;
2585 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2586 total += file->FileSize;
2588 ui_progress(package,0,total,0,0);
2590 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2592 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2593 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2594 feature->ActionRequest);
2597 return ERROR_SUCCESS;
2600 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2602 MSIPACKAGE* package = (MSIPACKAGE*)param;
2603 LPCWSTR cond = NULL;
2604 LPCWSTR message = NULL;
2605 UINT r;
2607 static const WCHAR title[]=
2608 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2610 cond = MSI_RecordGetString(row,1);
2612 r = MSI_EvaluateConditionW(package,cond);
2613 if (r == MSICONDITION_FALSE)
2615 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2617 LPWSTR deformated;
2618 message = MSI_RecordGetString(row,2);
2619 deformat_string(package,message,&deformated);
2620 MessageBoxW(NULL,deformated,title,MB_OK);
2621 msi_free(deformated);
2624 return ERROR_INSTALL_FAILURE;
2627 return ERROR_SUCCESS;
2630 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2632 UINT rc;
2633 MSIQUERY * view = NULL;
2634 static const WCHAR ExecSeqQuery[] =
2635 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2636 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2638 TRACE("Checking launch conditions\n");
2640 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2641 if (rc != ERROR_SUCCESS)
2642 return ERROR_SUCCESS;
2644 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2645 msiobj_release(&view->hdr);
2647 return rc;
2650 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2653 if (!cmp->KeyPath)
2654 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2656 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2658 MSIRECORD * row = 0;
2659 UINT root,len;
2660 LPWSTR deformated,buffer,deformated_name;
2661 LPCWSTR key,name;
2662 static const WCHAR ExecSeqQuery[] =
2663 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2664 '`','R','e','g','i','s','t','r','y','`',' ',
2665 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2666 ' ','=',' ' ,'\'','%','s','\'',0 };
2667 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2668 static const WCHAR fmt2[]=
2669 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2671 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2672 if (!row)
2673 return NULL;
2675 root = MSI_RecordGetInteger(row,2);
2676 key = MSI_RecordGetString(row, 3);
2677 name = MSI_RecordGetString(row, 4);
2678 deformat_string(package, key , &deformated);
2679 deformat_string(package, name, &deformated_name);
2681 len = strlenW(deformated) + 6;
2682 if (deformated_name)
2683 len+=strlenW(deformated_name);
2685 buffer = msi_alloc( len *sizeof(WCHAR));
2687 if (deformated_name)
2688 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2689 else
2690 sprintfW(buffer,fmt,root,deformated);
2692 msi_free(deformated);
2693 msi_free(deformated_name);
2694 msiobj_release(&row->hdr);
2696 return buffer;
2698 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2700 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2701 return NULL;
2703 else
2705 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2707 if (file)
2708 return strdupW( file->TargetPath );
2710 return NULL;
2713 static HKEY openSharedDLLsKey(void)
2715 HKEY hkey=0;
2716 static const WCHAR path[] =
2717 {'S','o','f','t','w','a','r','e','\\',
2718 'M','i','c','r','o','s','o','f','t','\\',
2719 'W','i','n','d','o','w','s','\\',
2720 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2721 'S','h','a','r','e','d','D','L','L','s',0};
2723 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2724 return hkey;
2727 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2729 HKEY hkey;
2730 DWORD count=0;
2731 DWORD type;
2732 DWORD sz = sizeof(count);
2733 DWORD rc;
2735 hkey = openSharedDLLsKey();
2736 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2737 if (rc != ERROR_SUCCESS)
2738 count = 0;
2739 RegCloseKey(hkey);
2740 return count;
2743 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2745 HKEY hkey;
2747 hkey = openSharedDLLsKey();
2748 if (count > 0)
2749 msi_reg_set_val_dword( hkey, path, count );
2750 else
2751 RegDeleteValueW(hkey,path);
2752 RegCloseKey(hkey);
2753 return count;
2757 * Return TRUE if the count should be written out and FALSE if not
2759 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2761 MSIFEATURE *feature;
2762 INT count = 0;
2763 BOOL write = FALSE;
2765 /* only refcount DLLs */
2766 if (comp->KeyPath == NULL ||
2767 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2768 comp->Attributes & msidbComponentAttributesODBCDataSource)
2769 write = FALSE;
2770 else
2772 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2773 write = (count > 0);
2775 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2776 write = TRUE;
2779 /* increment counts */
2780 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2782 ComponentList *cl;
2784 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2785 continue;
2787 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2789 if ( cl->component == comp )
2790 count++;
2794 /* decrement counts */
2795 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2797 ComponentList *cl;
2799 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2800 continue;
2802 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2804 if ( cl->component == comp )
2805 count--;
2809 /* ref count all the files in the component */
2810 if (write)
2812 MSIFILE *file;
2814 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2816 if (file->Component == comp)
2817 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2821 /* add a count for permenent */
2822 if (comp->Attributes & msidbComponentAttributesPermanent)
2823 count ++;
2825 comp->RefCount = count;
2827 if (write)
2828 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2832 * Ok further analysis makes me think that this work is
2833 * actually done in the PublishComponents and PublishFeatures
2834 * step, and not here. It appears like the keypath and all that is
2835 * resolved in this step, however actually written in the Publish steps.
2836 * But we will leave it here for now because it is unclear
2838 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2840 WCHAR squished_pc[GUID_SIZE];
2841 WCHAR squished_cc[GUID_SIZE];
2842 UINT rc;
2843 MSICOMPONENT *comp;
2844 HKEY hkey=0,hkey2=0;
2846 TRACE("\n");
2848 /* writes the Component and Features values to the registry */
2850 rc = MSIREG_OpenComponents(&hkey);
2851 if (rc != ERROR_SUCCESS)
2852 return rc;
2854 squash_guid(package->ProductCode,squished_pc);
2855 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2857 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2859 MSIRECORD * uirow;
2861 ui_progress(package,2,0,0,0);
2862 if (!comp->ComponentId)
2863 continue;
2865 squash_guid(comp->ComponentId,squished_cc);
2867 msi_free(comp->FullKeypath);
2868 comp->FullKeypath = resolve_keypath( package, comp );
2870 /* do the refcounting */
2871 ACTION_RefCountComponent( package, comp );
2873 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2874 debugstr_w(comp->Component),
2875 debugstr_w(squished_cc),
2876 debugstr_w(comp->FullKeypath),
2877 comp->RefCount);
2879 * Write the keypath out if the component is to be registered
2880 * and delete the key if the component is to be deregistered
2882 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2884 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2885 if (rc != ERROR_SUCCESS)
2886 continue;
2888 if (!comp->FullKeypath)
2889 continue;
2891 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2893 if (comp->Attributes & msidbComponentAttributesPermanent)
2895 static const WCHAR szPermKey[] =
2896 { '0','0','0','0','0','0','0','0','0','0','0','0',
2897 '0','0','0','0','0','0','0','0','0','0','0','0',
2898 '0','0','0','0','0','0','0','0',0 };
2900 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2903 RegCloseKey(hkey2);
2905 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey2, TRUE);
2906 if (rc != ERROR_SUCCESS)
2907 continue;
2909 msi_reg_set_val_str(hkey2, squished_pc, comp->FullKeypath);
2910 RegCloseKey(hkey2);
2912 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2914 DWORD res;
2916 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2917 if (rc != ERROR_SUCCESS)
2918 continue;
2920 RegDeleteValueW(hkey2,squished_pc);
2922 /* if the key is empty delete it */
2923 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2924 RegCloseKey(hkey2);
2925 if (res == ERROR_NO_MORE_ITEMS)
2926 RegDeleteKeyW(hkey,squished_cc);
2928 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2931 /* UI stuff */
2932 uirow = MSI_CreateRecord(3);
2933 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2934 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2935 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2936 ui_actiondata(package,szProcessComponents,uirow);
2937 msiobj_release( &uirow->hdr );
2939 RegCloseKey(hkey);
2940 return rc;
2943 typedef struct {
2944 CLSID clsid;
2945 LPWSTR source;
2947 LPWSTR path;
2948 ITypeLib *ptLib;
2949 } typelib_struct;
2951 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2952 LPWSTR lpszName, LONG_PTR lParam)
2954 TLIBATTR *attr;
2955 typelib_struct *tl_struct = (typelib_struct*) lParam;
2956 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2957 int sz;
2958 HRESULT res;
2960 if (!IS_INTRESOURCE(lpszName))
2962 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2963 return TRUE;
2966 sz = strlenW(tl_struct->source)+4;
2967 sz *= sizeof(WCHAR);
2969 if ((INT_PTR)lpszName == 1)
2970 tl_struct->path = strdupW(tl_struct->source);
2971 else
2973 tl_struct->path = msi_alloc(sz);
2974 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2977 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2978 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2979 if (!SUCCEEDED(res))
2981 msi_free(tl_struct->path);
2982 tl_struct->path = NULL;
2984 return TRUE;
2987 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2988 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2990 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2991 return FALSE;
2994 msi_free(tl_struct->path);
2995 tl_struct->path = NULL;
2997 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2998 ITypeLib_Release(tl_struct->ptLib);
3000 return TRUE;
3003 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3005 MSIPACKAGE* package = (MSIPACKAGE*)param;
3006 LPCWSTR component;
3007 MSICOMPONENT *comp;
3008 MSIFILE *file;
3009 typelib_struct tl_struct;
3010 HMODULE module;
3011 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3013 component = MSI_RecordGetString(row,3);
3014 comp = get_loaded_component(package,component);
3015 if (!comp)
3016 return ERROR_SUCCESS;
3018 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3020 TRACE("Skipping typelib reg due to disabled component\n");
3022 comp->Action = comp->Installed;
3024 return ERROR_SUCCESS;
3027 comp->Action = INSTALLSTATE_LOCAL;
3029 file = get_loaded_file( package, comp->KeyPath );
3030 if (!file)
3031 return ERROR_SUCCESS;
3033 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3034 if (module)
3036 LPCWSTR guid;
3037 guid = MSI_RecordGetString(row,1);
3038 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3039 tl_struct.source = strdupW( file->TargetPath );
3040 tl_struct.path = NULL;
3042 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3043 (LONG_PTR)&tl_struct);
3045 if (tl_struct.path)
3047 LPWSTR help = NULL;
3048 LPCWSTR helpid;
3049 HRESULT res;
3051 helpid = MSI_RecordGetString(row,6);
3053 if (helpid)
3054 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3055 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3056 msi_free(help);
3058 if (!SUCCEEDED(res))
3059 ERR("Failed to register type library %s\n",
3060 debugstr_w(tl_struct.path));
3061 else
3063 ui_actiondata(package,szRegisterTypeLibraries,row);
3065 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3068 ITypeLib_Release(tl_struct.ptLib);
3069 msi_free(tl_struct.path);
3071 else
3072 ERR("Failed to load type library %s\n",
3073 debugstr_w(tl_struct.source));
3075 FreeLibrary(module);
3076 msi_free(tl_struct.source);
3078 else
3079 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3081 return ERROR_SUCCESS;
3084 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3087 * OK this is a bit confusing.. I am given a _Component key and I believe
3088 * that the file that is being registered as a type library is the "key file
3089 * of that component" which I interpret to mean "The file in the KeyPath of
3090 * that component".
3092 UINT rc;
3093 MSIQUERY * view;
3094 static const WCHAR Query[] =
3095 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3096 '`','T','y','p','e','L','i','b','`',0};
3098 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3099 if (rc != ERROR_SUCCESS)
3100 return ERROR_SUCCESS;
3102 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3103 msiobj_release(&view->hdr);
3104 return rc;
3107 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3109 MSIPACKAGE *package = (MSIPACKAGE*)param;
3110 LPWSTR target_file, target_folder, filename;
3111 LPCWSTR buffer, extension;
3112 MSICOMPONENT *comp;
3113 static const WCHAR szlnk[]={'.','l','n','k',0};
3114 IShellLinkW *sl = NULL;
3115 IPersistFile *pf = NULL;
3116 HRESULT res;
3118 buffer = MSI_RecordGetString(row,4);
3119 comp = get_loaded_component(package,buffer);
3120 if (!comp)
3121 return ERROR_SUCCESS;
3123 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3125 TRACE("Skipping shortcut creation due to disabled component\n");
3127 comp->Action = comp->Installed;
3129 return ERROR_SUCCESS;
3132 comp->Action = INSTALLSTATE_LOCAL;
3134 ui_actiondata(package,szCreateShortcuts,row);
3136 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3137 &IID_IShellLinkW, (LPVOID *) &sl );
3139 if (FAILED( res ))
3141 ERR("CLSID_ShellLink not available\n");
3142 goto err;
3145 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3146 if (FAILED( res ))
3148 ERR("QueryInterface(IID_IPersistFile) failed\n");
3149 goto err;
3152 buffer = MSI_RecordGetString(row,2);
3153 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3155 /* may be needed because of a bug somehwere else */
3156 create_full_pathW(target_folder);
3158 filename = msi_dup_record_field( row, 3 );
3159 reduce_to_longfilename(filename);
3161 extension = strchrW(filename,'.');
3162 if (!extension || strcmpiW(extension,szlnk))
3164 int len = strlenW(filename);
3165 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3166 memcpy(filename + len, szlnk, sizeof(szlnk));
3168 target_file = build_directory_name(2, target_folder, filename);
3169 msi_free(target_folder);
3170 msi_free(filename);
3172 buffer = MSI_RecordGetString(row,5);
3173 if (strchrW(buffer,'['))
3175 LPWSTR deformated;
3176 deformat_string(package,buffer,&deformated);
3177 IShellLinkW_SetPath(sl,deformated);
3178 msi_free(deformated);
3180 else
3182 FIXME("poorly handled shortcut format, advertised shortcut\n");
3183 IShellLinkW_SetPath(sl,comp->FullKeypath);
3186 if (!MSI_RecordIsNull(row,6))
3188 LPWSTR deformated;
3189 buffer = MSI_RecordGetString(row,6);
3190 deformat_string(package,buffer,&deformated);
3191 IShellLinkW_SetArguments(sl,deformated);
3192 msi_free(deformated);
3195 if (!MSI_RecordIsNull(row,7))
3197 buffer = MSI_RecordGetString(row,7);
3198 IShellLinkW_SetDescription(sl,buffer);
3201 if (!MSI_RecordIsNull(row,8))
3202 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3204 if (!MSI_RecordIsNull(row,9))
3206 LPWSTR Path;
3207 INT index;
3209 buffer = MSI_RecordGetString(row,9);
3211 Path = build_icon_path(package,buffer);
3212 index = MSI_RecordGetInteger(row,10);
3214 /* no value means 0 */
3215 if (index == MSI_NULL_INTEGER)
3216 index = 0;
3218 IShellLinkW_SetIconLocation(sl,Path,index);
3219 msi_free(Path);
3222 if (!MSI_RecordIsNull(row,11))
3223 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3225 if (!MSI_RecordIsNull(row,12))
3227 LPWSTR Path;
3228 buffer = MSI_RecordGetString(row,12);
3229 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3230 if (Path)
3231 IShellLinkW_SetWorkingDirectory(sl,Path);
3232 msi_free(Path);
3235 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3236 IPersistFile_Save(pf,target_file,FALSE);
3238 msi_free(target_file);
3240 err:
3241 if (pf)
3242 IPersistFile_Release( pf );
3243 if (sl)
3244 IShellLinkW_Release( sl );
3246 return ERROR_SUCCESS;
3249 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3251 UINT rc;
3252 HRESULT res;
3253 MSIQUERY * view;
3254 static const WCHAR Query[] =
3255 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3256 '`','S','h','o','r','t','c','u','t','`',0};
3258 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3259 if (rc != ERROR_SUCCESS)
3260 return ERROR_SUCCESS;
3262 res = CoInitialize( NULL );
3263 if (FAILED (res))
3265 ERR("CoInitialize failed\n");
3266 return ERROR_FUNCTION_FAILED;
3269 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3270 msiobj_release(&view->hdr);
3272 CoUninitialize();
3274 return rc;
3277 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3279 MSIPACKAGE* package = (MSIPACKAGE*)param;
3280 HANDLE the_file;
3281 LPWSTR FilePath;
3282 LPCWSTR FileName;
3283 CHAR buffer[1024];
3284 DWORD sz;
3285 UINT rc;
3286 MSIRECORD *uirow;
3288 FileName = MSI_RecordGetString(row,1);
3289 if (!FileName)
3291 ERR("Unable to get FileName\n");
3292 return ERROR_SUCCESS;
3295 FilePath = build_icon_path(package,FileName);
3297 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3299 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3300 FILE_ATTRIBUTE_NORMAL, NULL);
3302 if (the_file == INVALID_HANDLE_VALUE)
3304 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3305 msi_free(FilePath);
3306 return ERROR_SUCCESS;
3311 DWORD write;
3312 sz = 1024;
3313 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3314 if (rc != ERROR_SUCCESS)
3316 ERR("Failed to get stream\n");
3317 CloseHandle(the_file);
3318 DeleteFileW(FilePath);
3319 break;
3321 WriteFile(the_file,buffer,sz,&write,NULL);
3322 } while (sz == 1024);
3324 msi_free(FilePath);
3326 CloseHandle(the_file);
3328 uirow = MSI_CreateRecord(1);
3329 MSI_RecordSetStringW(uirow,1,FileName);
3330 ui_actiondata(package,szPublishProduct,uirow);
3331 msiobj_release( &uirow->hdr );
3333 return ERROR_SUCCESS;
3336 static BOOL msi_check_publish(MSIPACKAGE *package)
3338 MSIFEATURE *feature;
3340 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3342 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3343 return TRUE;
3346 return FALSE;
3350 * 99% of the work done here is only done for
3351 * advertised installs. However this is where the
3352 * Icon table is processed and written out
3353 * so that is what I am going to do here.
3355 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3357 UINT rc;
3358 MSIQUERY * view;
3359 MSISOURCELISTINFO *info;
3360 MSIMEDIADISK *disk;
3361 static const WCHAR Query[]=
3362 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3363 '`','I','c','o','n','`',0};
3364 /* for registry stuff */
3365 HKEY hkey=0;
3366 HKEY hukey=0;
3367 HKEY hudkey=0, props=0;
3368 static const WCHAR szProductLanguage[] =
3369 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3370 static const WCHAR szARPProductIcon[] =
3371 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3372 static const WCHAR szProductVersion[] =
3373 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3374 DWORD langid;
3375 LPWSTR buffer;
3376 DWORD size;
3377 MSIHANDLE hDb, hSumInfo;
3379 /* FIXME: also need to publish if the product is in advertise mode */
3380 if (!msi_check_publish(package))
3381 return ERROR_SUCCESS;
3383 /* write out icon files */
3385 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3386 if (rc == ERROR_SUCCESS)
3388 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3389 msiobj_release(&view->hdr);
3392 /* ok there is a lot more done here but i need to figure out what */
3394 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3395 if (rc != ERROR_SUCCESS)
3396 goto end;
3398 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3399 if (rc != ERROR_SUCCESS)
3400 goto end;
3402 rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3403 if (rc != ERROR_SUCCESS)
3404 goto end;
3406 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode,&props,TRUE);
3407 if (rc != ERROR_SUCCESS)
3408 goto end;
3410 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3411 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3412 msi_free(buffer);
3414 langid = msi_get_property_int( package, szProductLanguage, 0 );
3415 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3417 buffer = msi_dup_property( package, szARPProductIcon );
3418 if (buffer)
3420 LPWSTR path = build_icon_path(package,buffer);
3421 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3422 msi_free( path );
3424 msi_free(buffer);
3426 buffer = msi_dup_property( package, szProductVersion );
3427 if (buffer)
3429 DWORD verdword = msi_version_str_to_dword(buffer);
3430 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3432 msi_free(buffer);
3434 /* FIXME: Need to write more keys to the user registry */
3436 hDb= alloc_msihandle( &package->db->hdr );
3437 if (!hDb) {
3438 rc = ERROR_NOT_ENOUGH_MEMORY;
3439 goto end;
3441 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3442 MsiCloseHandle(hDb);
3443 if (rc == ERROR_SUCCESS)
3445 WCHAR guidbuffer[0x200];
3446 size = 0x200;
3447 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3448 guidbuffer, &size);
3449 if (rc == ERROR_SUCCESS)
3451 WCHAR squashed[GUID_SIZE];
3452 /* for now we only care about the first guid */
3453 LPWSTR ptr = strchrW(guidbuffer,';');
3454 if (ptr) *ptr = 0;
3455 squash_guid(guidbuffer,squashed);
3456 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3458 else
3460 ERR("Unable to query Revision_Number...\n");
3461 rc = ERROR_SUCCESS;
3463 MsiCloseHandle(hSumInfo);
3465 else
3467 ERR("Unable to open Summary Information\n");
3468 rc = ERROR_SUCCESS;
3471 /* publish the SourceList info */
3472 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3474 MsiSourceListSetInfoW(package->ProductCode, NULL,
3475 info->context, info->options,
3476 info->property, info->value);
3479 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3481 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3482 disk->context, disk->options,
3483 disk->disk_id, disk->volume_label, disk->disk_prompt);
3486 end:
3487 RegCloseKey(hkey);
3488 RegCloseKey(hukey);
3489 RegCloseKey(hudkey);
3490 RegCloseKey(props);
3492 return rc;
3495 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3497 MSIPACKAGE *package = (MSIPACKAGE*)param;
3498 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3499 LPWSTR deformated_section, deformated_key, deformated_value;
3500 LPWSTR folder, fullname = NULL;
3501 MSIRECORD * uirow;
3502 INT action;
3503 MSICOMPONENT *comp;
3504 static const WCHAR szWindowsFolder[] =
3505 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3507 component = MSI_RecordGetString(row, 8);
3508 comp = get_loaded_component(package,component);
3510 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3512 TRACE("Skipping ini file due to disabled component %s\n",
3513 debugstr_w(component));
3515 comp->Action = comp->Installed;
3517 return ERROR_SUCCESS;
3520 comp->Action = INSTALLSTATE_LOCAL;
3522 identifier = MSI_RecordGetString(row,1);
3523 filename = MSI_RecordGetString(row,2);
3524 dirproperty = MSI_RecordGetString(row,3);
3525 section = MSI_RecordGetString(row,4);
3526 key = MSI_RecordGetString(row,5);
3527 value = MSI_RecordGetString(row,6);
3528 action = MSI_RecordGetInteger(row,7);
3530 deformat_string(package,section,&deformated_section);
3531 deformat_string(package,key,&deformated_key);
3532 deformat_string(package,value,&deformated_value);
3534 if (dirproperty)
3536 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3537 if (!folder)
3538 folder = msi_dup_property( package, dirproperty );
3540 else
3541 folder = msi_dup_property( package, szWindowsFolder );
3543 if (!folder)
3545 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3546 goto cleanup;
3549 fullname = build_directory_name(2, folder, filename);
3551 if (action == 0)
3553 TRACE("Adding value %s to section %s in %s\n",
3554 debugstr_w(deformated_key), debugstr_w(deformated_section),
3555 debugstr_w(fullname));
3556 WritePrivateProfileStringW(deformated_section, deformated_key,
3557 deformated_value, fullname);
3559 else if (action == 1)
3561 WCHAR returned[10];
3562 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3563 returned, 10, fullname);
3564 if (returned[0] == 0)
3566 TRACE("Adding value %s to section %s in %s\n",
3567 debugstr_w(deformated_key), debugstr_w(deformated_section),
3568 debugstr_w(fullname));
3570 WritePrivateProfileStringW(deformated_section, deformated_key,
3571 deformated_value, fullname);
3574 else if (action == 3)
3575 FIXME("Append to existing section not yet implemented\n");
3577 uirow = MSI_CreateRecord(4);
3578 MSI_RecordSetStringW(uirow,1,identifier);
3579 MSI_RecordSetStringW(uirow,2,deformated_section);
3580 MSI_RecordSetStringW(uirow,3,deformated_key);
3581 MSI_RecordSetStringW(uirow,4,deformated_value);
3582 ui_actiondata(package,szWriteIniValues,uirow);
3583 msiobj_release( &uirow->hdr );
3584 cleanup:
3585 msi_free(fullname);
3586 msi_free(folder);
3587 msi_free(deformated_key);
3588 msi_free(deformated_value);
3589 msi_free(deformated_section);
3590 return ERROR_SUCCESS;
3593 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3595 UINT rc;
3596 MSIQUERY * view;
3597 static const WCHAR ExecSeqQuery[] =
3598 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3599 '`','I','n','i','F','i','l','e','`',0};
3601 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3602 if (rc != ERROR_SUCCESS)
3604 TRACE("no IniFile table\n");
3605 return ERROR_SUCCESS;
3608 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3609 msiobj_release(&view->hdr);
3610 return rc;
3613 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3615 MSIPACKAGE *package = (MSIPACKAGE*)param;
3616 LPCWSTR filename;
3617 LPWSTR FullName;
3618 MSIFILE *file;
3619 DWORD len;
3620 static const WCHAR ExeStr[] =
3621 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3622 static const WCHAR close[] = {'\"',0};
3623 STARTUPINFOW si;
3624 PROCESS_INFORMATION info;
3625 BOOL brc;
3626 MSIRECORD *uirow;
3627 LPWSTR uipath, p;
3629 memset(&si,0,sizeof(STARTUPINFOW));
3631 filename = MSI_RecordGetString(row,1);
3632 file = get_loaded_file( package, filename );
3634 if (!file)
3636 ERR("Unable to find file id %s\n",debugstr_w(filename));
3637 return ERROR_SUCCESS;
3640 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3642 FullName = msi_alloc(len*sizeof(WCHAR));
3643 strcpyW(FullName,ExeStr);
3644 strcatW( FullName, file->TargetPath );
3645 strcatW(FullName,close);
3647 TRACE("Registering %s\n",debugstr_w(FullName));
3648 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3649 &si, &info);
3651 if (brc)
3652 msi_dialog_check_messages(info.hProcess);
3654 msi_free(FullName);
3656 /* the UI chunk */
3657 uirow = MSI_CreateRecord( 2 );
3658 uipath = strdupW( file->TargetPath );
3659 p = strrchrW(uipath,'\\');
3660 if (p)
3661 p[0]=0;
3662 MSI_RecordSetStringW( uirow, 1, &p[1] );
3663 MSI_RecordSetStringW( uirow, 2, uipath);
3664 ui_actiondata( package, szSelfRegModules, uirow);
3665 msiobj_release( &uirow->hdr );
3666 msi_free( uipath );
3667 /* FIXME: call ui_progress? */
3669 return ERROR_SUCCESS;
3672 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3674 UINT rc;
3675 MSIQUERY * view;
3676 static const WCHAR ExecSeqQuery[] =
3677 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3678 '`','S','e','l','f','R','e','g','`',0};
3680 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3681 if (rc != ERROR_SUCCESS)
3683 TRACE("no SelfReg table\n");
3684 return ERROR_SUCCESS;
3687 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3688 msiobj_release(&view->hdr);
3690 return ERROR_SUCCESS;
3693 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3695 MSIFEATURE *feature;
3696 UINT rc;
3697 HKEY hkey=0;
3698 HKEY hukey=0;
3699 HKEY userdata=0;
3701 if (!msi_check_publish(package))
3702 return ERROR_SUCCESS;
3704 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3705 if (rc != ERROR_SUCCESS)
3706 goto end;
3708 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3709 if (rc != ERROR_SUCCESS)
3710 goto end;
3712 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &userdata, TRUE);
3713 if (rc != ERROR_SUCCESS)
3714 goto end;
3716 /* here the guids are base 85 encoded */
3717 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3719 ComponentList *cl;
3720 LPWSTR data = NULL;
3721 GUID clsid;
3722 INT size;
3723 BOOL absent = FALSE;
3724 MSIRECORD *uirow;
3726 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3727 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3728 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3729 absent = TRUE;
3731 size = 1;
3732 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3734 size += 21;
3736 if (feature->Feature_Parent)
3737 size += strlenW( feature->Feature_Parent )+2;
3739 data = msi_alloc(size * sizeof(WCHAR));
3741 data[0] = 0;
3742 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3744 MSICOMPONENT* component = cl->component;
3745 WCHAR buf[21];
3747 buf[0] = 0;
3748 if (component->ComponentId)
3750 TRACE("From %s\n",debugstr_w(component->ComponentId));
3751 CLSIDFromString(component->ComponentId, &clsid);
3752 encode_base85_guid(&clsid,buf);
3753 TRACE("to %s\n",debugstr_w(buf));
3754 strcatW(data,buf);
3758 if (feature->Feature_Parent)
3760 static const WCHAR sep[] = {'\2',0};
3761 strcatW(data,sep);
3762 strcatW(data,feature->Feature_Parent);
3765 msi_reg_set_val_str( hkey, feature->Feature, data );
3766 msi_reg_set_val_str( userdata, feature->Feature, data );
3767 msi_free(data);
3769 size = 0;
3770 if (feature->Feature_Parent)
3771 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3772 if (!absent)
3774 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3775 (LPBYTE)feature->Feature_Parent,size);
3777 else
3779 size += 2*sizeof(WCHAR);
3780 data = msi_alloc(size);
3781 data[0] = 0x6;
3782 data[1] = 0;
3783 if (feature->Feature_Parent)
3784 strcpyW( &data[1], feature->Feature_Parent );
3785 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3786 (LPBYTE)data,size);
3787 msi_free(data);
3790 /* the UI chunk */
3791 uirow = MSI_CreateRecord( 1 );
3792 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3793 ui_actiondata( package, szPublishFeatures, uirow);
3794 msiobj_release( &uirow->hdr );
3795 /* FIXME: call ui_progress? */
3798 end:
3799 RegCloseKey(hkey);
3800 RegCloseKey(hukey);
3801 return rc;
3804 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3806 UINT r;
3807 HKEY hkey;
3809 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3811 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3812 if (r == ERROR_SUCCESS)
3814 RegDeleteValueW(hkey, feature->Feature);
3815 RegCloseKey(hkey);
3818 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3819 if (r == ERROR_SUCCESS)
3821 RegDeleteValueW(hkey, feature->Feature);
3822 RegCloseKey(hkey);
3825 return ERROR_SUCCESS;
3828 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3830 MSIFEATURE *feature;
3832 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3834 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3835 return FALSE;
3838 return TRUE;
3841 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3843 MSIFEATURE *feature;
3845 if (!msi_check_unpublish(package))
3846 return ERROR_SUCCESS;
3848 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3850 msi_unpublish_feature(package, feature);
3853 return ERROR_SUCCESS;
3856 static UINT msi_get_local_package_name( LPWSTR path )
3858 static const WCHAR szInstaller[] = {
3859 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3860 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3861 DWORD time, len, i;
3862 HANDLE handle;
3864 time = GetTickCount();
3865 GetWindowsDirectoryW( path, MAX_PATH );
3866 lstrcatW( path, szInstaller );
3867 CreateDirectoryW( path, NULL );
3869 len = lstrlenW(path);
3870 for (i=0; i<0x10000; i++)
3872 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3873 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3874 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3875 if (handle != INVALID_HANDLE_VALUE)
3877 CloseHandle(handle);
3878 break;
3880 if (GetLastError() != ERROR_FILE_EXISTS &&
3881 GetLastError() != ERROR_SHARING_VIOLATION)
3882 return ERROR_FUNCTION_FAILED;
3885 return ERROR_SUCCESS;
3888 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3890 WCHAR packagefile[MAX_PATH];
3891 HKEY props;
3892 UINT r;
3894 r = msi_get_local_package_name( packagefile );
3895 if (r != ERROR_SUCCESS)
3896 return r;
3898 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3900 r = CopyFileW( package->db->path, packagefile, FALSE);
3902 if (!r)
3904 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3905 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
3906 return ERROR_FUNCTION_FAILED;
3909 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3911 r = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3912 if (r != ERROR_SUCCESS)
3913 return r;
3915 msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3916 RegCloseKey(props);
3917 return ERROR_SUCCESS;
3920 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3922 LPWSTR prop, val, key;
3923 static const LPCSTR propval[] = {
3924 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3925 "ARPCONTACT", "Contact",
3926 "ARPCOMMENTS", "Comments",
3927 "ProductName", "DisplayName",
3928 "ProductVersion", "DisplayVersion",
3929 "ARPHELPLINK", "HelpLink",
3930 "ARPHELPTELEPHONE", "HelpTelephone",
3931 "ARPINSTALLLOCATION", "InstallLocation",
3932 "SourceDir", "InstallSource",
3933 "Manufacturer", "Publisher",
3934 "ARPREADME", "Readme",
3935 "ARPSIZE", "Size",
3936 "ARPURLINFOABOUT", "URLInfoAbout",
3937 "ARPURLUPDATEINFO", "URLUpdateInfo",
3938 NULL,
3940 const LPCSTR *p = propval;
3942 while( *p )
3944 prop = strdupAtoW( *p++ );
3945 key = strdupAtoW( *p++ );
3946 val = msi_dup_property( package, prop );
3947 msi_reg_set_val_str( hkey, key, val );
3948 msi_free(val);
3949 msi_free(key);
3950 msi_free(prop);
3952 return ERROR_SUCCESS;
3955 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3957 HKEY hkey=0;
3958 HKEY hudkey=0, props=0;
3959 LPWSTR buffer = NULL;
3960 UINT rc;
3961 DWORD size, langid;
3962 static const WCHAR szWindowsInstaller[] =
3963 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3964 static const WCHAR szUpgradeCode[] =
3965 {'U','p','g','r','a','d','e','C','o','d','e',0};
3966 static const WCHAR modpath_fmt[] =
3967 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3968 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3969 static const WCHAR szModifyPath[] =
3970 {'M','o','d','i','f','y','P','a','t','h',0};
3971 static const WCHAR szUninstallString[] =
3972 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3973 static const WCHAR szEstimatedSize[] =
3974 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3975 static const WCHAR szProductLanguage[] =
3976 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3977 static const WCHAR szProductVersion[] =
3978 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3980 SYSTEMTIME systime;
3981 static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3982 LPWSTR upgrade_code;
3983 WCHAR szDate[9];
3985 /* FIXME: also need to publish if the product is in advertise mode */
3986 if (!msi_check_publish(package))
3987 return ERROR_SUCCESS;
3989 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3990 if (rc != ERROR_SUCCESS)
3991 return rc;
3993 /* dump all the info i can grab */
3994 /* FIXME: Flesh out more information */
3996 msi_write_uninstall_property_vals( package, hkey );
3998 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
4000 msi_make_package_local( package, hkey );
4002 /* do ModifyPath and UninstallString */
4003 size = deformat_string(package,modpath_fmt,&buffer);
4004 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4005 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4006 msi_free(buffer);
4008 /* FIXME: Write real Estimated Size when we have it */
4009 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
4011 GetLocalTime(&systime);
4012 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
4013 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
4015 langid = msi_get_property_int( package, szProductLanguage, 0 );
4016 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
4018 buffer = msi_dup_property( package, szProductVersion );
4019 if (buffer)
4021 DWORD verdword = msi_version_str_to_dword(buffer);
4023 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
4024 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
4025 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
4027 msi_free(buffer);
4029 /* Handle Upgrade Codes */
4030 upgrade_code = msi_dup_property( package, szUpgradeCode );
4031 if (upgrade_code)
4033 HKEY hkey2;
4034 WCHAR squashed[33];
4035 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4036 squash_guid(package->ProductCode,squashed);
4037 msi_reg_set_val_str( hkey2, squashed, NULL );
4038 RegCloseKey(hkey2);
4039 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4040 squash_guid(package->ProductCode,squashed);
4041 msi_reg_set_val_str( hkey2, squashed, NULL );
4042 RegCloseKey(hkey2);
4044 msi_free(upgrade_code);
4047 RegCloseKey(hkey);
4049 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
4050 if (rc != ERROR_SUCCESS)
4051 return rc;
4053 RegCloseKey(hudkey);
4055 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
4056 if (rc != ERROR_SUCCESS)
4057 return rc;
4059 msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
4060 RegCloseKey(props);
4062 return ERROR_SUCCESS;
4065 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4067 return execute_script(package,INSTALL_SCRIPT);
4070 static UINT msi_unpublish_product(MSIPACKAGE *package)
4072 LPWSTR remove = NULL;
4073 LPWSTR *features = NULL;
4074 BOOL full_uninstall = TRUE;
4075 MSIFEATURE *feature;
4077 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4078 static const WCHAR szAll[] = {'A','L','L',0};
4080 remove = msi_dup_property(package, szRemove);
4081 if (!remove)
4082 return ERROR_SUCCESS;
4084 features = msi_split_string(remove, ',');
4085 if (!features)
4087 msi_free(remove);
4088 ERR("REMOVE feature list is empty!\n");
4089 return ERROR_FUNCTION_FAILED;
4092 if (!lstrcmpW(features[0], szAll))
4093 full_uninstall = TRUE;
4094 else
4096 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4098 if (feature->Action != INSTALLSTATE_ABSENT)
4099 full_uninstall = FALSE;
4103 if (!full_uninstall)
4104 goto done;
4106 MSIREG_DeleteProductKey(package->ProductCode);
4107 MSIREG_DeleteUserProductKey(package->ProductCode);
4108 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4109 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4110 MSIREG_DeleteUninstallKey(package->ProductCode);
4112 done:
4113 msi_free(remove);
4114 msi_free(features);
4115 return ERROR_SUCCESS;
4118 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4120 UINT rc;
4122 rc = msi_unpublish_product(package);
4123 if (rc != ERROR_SUCCESS)
4124 return rc;
4126 /* turn off scheduling */
4127 package->script->CurrentlyScripting= FALSE;
4129 /* first do the same as an InstallExecute */
4130 rc = ACTION_InstallExecute(package);
4131 if (rc != ERROR_SUCCESS)
4132 return rc;
4134 /* then handle Commit Actions */
4135 rc = execute_script(package,COMMIT_SCRIPT);
4137 return rc;
4140 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4142 static const WCHAR RunOnce[] = {
4143 'S','o','f','t','w','a','r','e','\\',
4144 'M','i','c','r','o','s','o','f','t','\\',
4145 'W','i','n','d','o','w','s','\\',
4146 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4147 'R','u','n','O','n','c','e',0};
4148 static const WCHAR InstallRunOnce[] = {
4149 'S','o','f','t','w','a','r','e','\\',
4150 'M','i','c','r','o','s','o','f','t','\\',
4151 'W','i','n','d','o','w','s','\\',
4152 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4153 'I','n','s','t','a','l','l','e','r','\\',
4154 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4156 static const WCHAR msiexec_fmt[] = {
4157 '%','s',
4158 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4159 '\"','%','s','\"',0};
4160 static const WCHAR install_fmt[] = {
4161 '/','I',' ','\"','%','s','\"',' ',
4162 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4163 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4164 WCHAR buffer[256], sysdir[MAX_PATH];
4165 HKEY hkey;
4166 WCHAR squished_pc[100];
4168 squash_guid(package->ProductCode,squished_pc);
4170 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4171 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4172 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4173 squished_pc);
4175 msi_reg_set_val_str( hkey, squished_pc, buffer );
4176 RegCloseKey(hkey);
4178 TRACE("Reboot command %s\n",debugstr_w(buffer));
4180 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4181 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4183 msi_reg_set_val_str( hkey, squished_pc, buffer );
4184 RegCloseKey(hkey);
4186 return ERROR_INSTALL_SUSPEND;
4189 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4191 DWORD attrib;
4192 UINT rc;
4195 * We are currently doing what should be done here in the top level Install
4196 * however for Administrative and uninstalls this step will be needed
4198 if (!package->PackagePath)
4199 return ERROR_SUCCESS;
4201 msi_set_sourcedir_props(package, TRUE);
4203 attrib = GetFileAttributesW(package->db->path);
4204 if (attrib == INVALID_FILE_ATTRIBUTES)
4206 LPWSTR prompt;
4207 LPWSTR msg;
4208 DWORD size = 0;
4210 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4211 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4212 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4213 if (rc == ERROR_MORE_DATA)
4215 prompt = msi_alloc(size * sizeof(WCHAR));
4216 MsiSourceListGetInfoW(package->ProductCode, NULL,
4217 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4218 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4220 else
4221 prompt = strdupW(package->db->path);
4223 msg = generate_error_string(package,1302,1,prompt);
4224 while(attrib == INVALID_FILE_ATTRIBUTES)
4226 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4227 if (rc == IDCANCEL)
4229 rc = ERROR_INSTALL_USEREXIT;
4230 break;
4232 attrib = GetFileAttributesW(package->db->path);
4234 msi_free(prompt);
4235 rc = ERROR_SUCCESS;
4237 else
4238 return ERROR_SUCCESS;
4240 return rc;
4243 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4245 HKEY hkey=0;
4246 LPWSTR buffer;
4247 LPWSTR productid;
4248 UINT rc,i;
4250 static const WCHAR szPropKeys[][80] =
4252 {'P','r','o','d','u','c','t','I','D',0},
4253 {'U','S','E','R','N','A','M','E',0},
4254 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4255 {0},
4258 static const WCHAR szRegKeys[][80] =
4260 {'P','r','o','d','u','c','t','I','D',0},
4261 {'R','e','g','O','w','n','e','r',0},
4262 {'R','e','g','C','o','m','p','a','n','y',0},
4263 {0},
4266 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4267 if (!productid)
4268 return ERROR_SUCCESS;
4270 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4271 if (rc != ERROR_SUCCESS)
4272 goto end;
4274 for( i = 0; szPropKeys[i][0]; i++ )
4276 buffer = msi_dup_property( package, szPropKeys[i] );
4277 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4278 msi_free( buffer );
4281 end:
4282 msi_free(productid);
4283 RegCloseKey(hkey);
4285 /* FIXME: call ui_actiondata */
4287 return ERROR_SUCCESS;
4291 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4293 UINT rc;
4295 package->script->InWhatSequence |= SEQUENCE_EXEC;
4296 rc = ACTION_ProcessExecSequence(package,FALSE);
4297 return rc;
4301 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4303 MSIPACKAGE *package = (MSIPACKAGE*)param;
4304 LPCWSTR compgroupid=NULL;
4305 LPCWSTR feature=NULL;
4306 LPCWSTR text = NULL;
4307 LPCWSTR qualifier = NULL;
4308 LPCWSTR component = NULL;
4309 LPWSTR advertise = NULL;
4310 LPWSTR output = NULL;
4311 HKEY hkey;
4312 UINT rc = ERROR_SUCCESS;
4313 MSICOMPONENT *comp;
4314 DWORD sz = 0;
4315 MSIRECORD *uirow;
4317 component = MSI_RecordGetString(rec,3);
4318 comp = get_loaded_component(package,component);
4320 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4321 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4322 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4324 TRACE("Skipping: Component %s not scheduled for install\n",
4325 debugstr_w(component));
4327 return ERROR_SUCCESS;
4330 compgroupid = MSI_RecordGetString(rec,1);
4331 qualifier = MSI_RecordGetString(rec,2);
4333 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4334 if (rc != ERROR_SUCCESS)
4335 goto end;
4337 text = MSI_RecordGetString(rec,4);
4338 feature = MSI_RecordGetString(rec,5);
4340 advertise = create_component_advertise_string(package, comp, feature);
4342 sz = strlenW(advertise);
4344 if (text)
4345 sz += lstrlenW(text);
4347 sz+=3;
4348 sz *= sizeof(WCHAR);
4350 output = msi_alloc_zero(sz);
4351 strcpyW(output,advertise);
4352 msi_free(advertise);
4354 if (text)
4355 strcatW(output,text);
4357 msi_reg_set_val_multi_str( hkey, qualifier, output );
4359 end:
4360 RegCloseKey(hkey);
4361 msi_free(output);
4363 /* the UI chunk */
4364 uirow = MSI_CreateRecord( 2 );
4365 MSI_RecordSetStringW( uirow, 1, compgroupid );
4366 MSI_RecordSetStringW( uirow, 2, qualifier);
4367 ui_actiondata( package, szPublishComponents, uirow);
4368 msiobj_release( &uirow->hdr );
4369 /* FIXME: call ui_progress? */
4371 return rc;
4375 * At present I am ignorning the advertised components part of this and only
4376 * focusing on the qualified component sets
4378 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4380 UINT rc;
4381 MSIQUERY * view;
4382 static const WCHAR ExecSeqQuery[] =
4383 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4384 '`','P','u','b','l','i','s','h',
4385 'C','o','m','p','o','n','e','n','t','`',0};
4387 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4388 if (rc != ERROR_SUCCESS)
4389 return ERROR_SUCCESS;
4391 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4392 msiobj_release(&view->hdr);
4394 return rc;
4397 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4399 MSIPACKAGE *package = (MSIPACKAGE*)param;
4400 MSIRECORD *row;
4401 MSIFILE *file;
4402 SC_HANDLE hscm, service = NULL;
4403 LPCWSTR name, disp, comp, depends, pass;
4404 LPCWSTR load_order, serv_name, key;
4405 DWORD serv_type, start_type;
4406 DWORD err_control;
4408 static const WCHAR query[] =
4409 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4410 '`','C','o','m','p','o','n','e','n','t','`',' ',
4411 'W','H','E','R','E',' ',
4412 '`','C','o','m','p','o','n','e','n','t','`',' ',
4413 '=','\'','%','s','\'',0};
4415 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4416 if (!hscm)
4418 ERR("Failed to open the SC Manager!\n");
4419 goto done;
4422 start_type = MSI_RecordGetInteger(rec, 5);
4423 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4424 goto done;
4426 depends = MSI_RecordGetString(rec, 8);
4427 if (depends && *depends)
4428 FIXME("Dependency list unhandled!\n");
4430 name = MSI_RecordGetString(rec, 2);
4431 disp = MSI_RecordGetString(rec, 3);
4432 serv_type = MSI_RecordGetInteger(rec, 4);
4433 err_control = MSI_RecordGetInteger(rec, 6);
4434 load_order = MSI_RecordGetString(rec, 7);
4435 serv_name = MSI_RecordGetString(rec, 9);
4436 pass = MSI_RecordGetString(rec, 10);
4437 comp = MSI_RecordGetString(rec, 12);
4439 /* fetch the service path */
4440 row = MSI_QueryGetRecord(package->db, query, comp);
4441 if (!row)
4443 ERR("Control query failed!\n");
4444 goto done;
4447 key = MSI_RecordGetString(row, 6);
4449 file = get_loaded_file(package, key);
4450 msiobj_release(&row->hdr);
4451 if (!file)
4453 ERR("Failed to load the service file\n");
4454 goto done;
4457 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4458 start_type, err_control, file->TargetPath,
4459 load_order, NULL, NULL, serv_name, pass);
4460 if (!service)
4462 if (GetLastError() != ERROR_SERVICE_EXISTS)
4463 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4466 done:
4467 CloseServiceHandle(service);
4468 CloseServiceHandle(hscm);
4470 return ERROR_SUCCESS;
4473 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4475 UINT rc;
4476 MSIQUERY * view;
4477 static const WCHAR ExecSeqQuery[] =
4478 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4479 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4481 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4482 if (rc != ERROR_SUCCESS)
4483 return ERROR_SUCCESS;
4485 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4486 msiobj_release(&view->hdr);
4488 return rc;
4491 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4492 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4494 LPCWSTR *vector, *temp_vector;
4495 LPWSTR p, q;
4496 DWORD sep_len;
4498 static const WCHAR separator[] = {'[','~',']',0};
4500 *numargs = 0;
4501 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4503 if (!args)
4504 return NULL;
4506 vector = msi_alloc(sizeof(LPWSTR));
4507 if (!vector)
4508 return NULL;
4510 p = args;
4513 (*numargs)++;
4514 vector[*numargs - 1] = p;
4516 if ((q = strstrW(p, separator)))
4518 *q = '\0';
4520 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4521 if (!temp_vector)
4523 msi_free(vector);
4524 return NULL;
4526 vector = temp_vector;
4528 p = q + sep_len;
4530 } while (q);
4532 return vector;
4535 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4537 MSIPACKAGE *package = (MSIPACKAGE *)param;
4538 MSICOMPONENT *comp;
4539 SC_HANDLE scm, service = NULL;
4540 LPCWSTR name, *vector = NULL;
4541 LPWSTR args;
4542 DWORD event, numargs;
4543 UINT r = ERROR_FUNCTION_FAILED;
4545 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4546 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4547 return ERROR_SUCCESS;
4549 name = MSI_RecordGetString(rec, 2);
4550 event = MSI_RecordGetInteger(rec, 3);
4551 args = strdupW(MSI_RecordGetString(rec, 4));
4553 if (!(event & msidbServiceControlEventStart))
4554 return ERROR_SUCCESS;
4556 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4557 if (!scm)
4559 ERR("Failed to open the service control manager\n");
4560 goto done;
4563 service = OpenServiceW(scm, name, SERVICE_START);
4564 if (!service)
4566 ERR("Failed to open service %s\n", debugstr_w(name));
4567 goto done;
4570 vector = msi_service_args_to_vector(args, &numargs);
4572 if (!StartServiceW(service, numargs, vector))
4574 ERR("Failed to start service %s\n", debugstr_w(name));
4575 goto done;
4578 r = ERROR_SUCCESS;
4580 done:
4581 CloseServiceHandle(service);
4582 CloseServiceHandle(scm);
4584 msi_free(args);
4585 msi_free(vector);
4586 return r;
4589 static UINT ACTION_StartServices( MSIPACKAGE *package )
4591 UINT rc;
4592 MSIQUERY *view;
4594 static const WCHAR query[] = {
4595 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4596 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4598 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4599 if (rc != ERROR_SUCCESS)
4600 return ERROR_SUCCESS;
4602 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4603 msiobj_release(&view->hdr);
4605 return rc;
4608 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4610 MSIFILE *file;
4612 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4614 if (!lstrcmpW(file->File, filename))
4615 return file;
4618 return NULL;
4621 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4623 MSIPACKAGE *package = (MSIPACKAGE*)param;
4624 LPWSTR driver, driver_path, ptr;
4625 WCHAR outpath[MAX_PATH];
4626 MSIFILE *driver_file, *setup_file;
4627 LPCWSTR desc;
4628 DWORD len, usage;
4629 UINT r = ERROR_SUCCESS;
4631 static const WCHAR driver_fmt[] = {
4632 'D','r','i','v','e','r','=','%','s',0};
4633 static const WCHAR setup_fmt[] = {
4634 'S','e','t','u','p','=','%','s',0};
4635 static const WCHAR usage_fmt[] = {
4636 'F','i','l','e','U','s','a','g','e','=','1',0};
4638 desc = MSI_RecordGetString(rec, 3);
4640 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4641 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4643 if (!driver_file || !setup_file)
4645 ERR("ODBC Driver entry not found!\n");
4646 return ERROR_FUNCTION_FAILED;
4649 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4650 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4651 lstrlenW(usage_fmt) + 1;
4652 driver = msi_alloc(len * sizeof(WCHAR));
4653 if (!driver)
4654 return ERROR_OUTOFMEMORY;
4656 ptr = driver;
4657 lstrcpyW(ptr, desc);
4658 ptr += lstrlenW(ptr) + 1;
4660 sprintfW(ptr, driver_fmt, driver_file->FileName);
4661 ptr += lstrlenW(ptr) + 1;
4663 sprintfW(ptr, setup_fmt, setup_file->FileName);
4664 ptr += lstrlenW(ptr) + 1;
4666 lstrcpyW(ptr, usage_fmt);
4667 ptr += lstrlenW(ptr) + 1;
4668 *ptr = '\0';
4670 driver_path = strdupW(driver_file->TargetPath);
4671 ptr = strrchrW(driver_path, '\\');
4672 if (ptr) *ptr = '\0';
4674 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4675 NULL, ODBC_INSTALL_COMPLETE, &usage))
4677 ERR("Failed to install SQL driver!\n");
4678 r = ERROR_FUNCTION_FAILED;
4681 msi_free(driver);
4682 msi_free(driver_path);
4684 return r;
4687 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4689 MSIPACKAGE *package = (MSIPACKAGE*)param;
4690 LPWSTR translator, translator_path, ptr;
4691 WCHAR outpath[MAX_PATH];
4692 MSIFILE *translator_file, *setup_file;
4693 LPCWSTR desc;
4694 DWORD len, usage;
4695 UINT r = ERROR_SUCCESS;
4697 static const WCHAR translator_fmt[] = {
4698 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4699 static const WCHAR setup_fmt[] = {
4700 'S','e','t','u','p','=','%','s',0};
4702 desc = MSI_RecordGetString(rec, 3);
4704 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4705 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4707 if (!translator_file || !setup_file)
4709 ERR("ODBC Translator entry not found!\n");
4710 return ERROR_FUNCTION_FAILED;
4713 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4714 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4715 translator = msi_alloc(len * sizeof(WCHAR));
4716 if (!translator)
4717 return ERROR_OUTOFMEMORY;
4719 ptr = translator;
4720 lstrcpyW(ptr, desc);
4721 ptr += lstrlenW(ptr) + 1;
4723 sprintfW(ptr, translator_fmt, translator_file->FileName);
4724 ptr += lstrlenW(ptr) + 1;
4726 sprintfW(ptr, setup_fmt, setup_file->FileName);
4727 ptr += lstrlenW(ptr) + 1;
4728 *ptr = '\0';
4730 translator_path = strdupW(translator_file->TargetPath);
4731 ptr = strrchrW(translator_path, '\\');
4732 if (ptr) *ptr = '\0';
4734 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4735 NULL, ODBC_INSTALL_COMPLETE, &usage))
4737 ERR("Failed to install SQL translator!\n");
4738 r = ERROR_FUNCTION_FAILED;
4741 msi_free(translator);
4742 msi_free(translator_path);
4744 return r;
4747 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4749 LPWSTR attrs;
4750 LPCWSTR desc, driver;
4751 WORD request = ODBC_ADD_SYS_DSN;
4752 INT registration;
4753 DWORD len;
4754 UINT r = ERROR_SUCCESS;
4756 static const WCHAR attrs_fmt[] = {
4757 'D','S','N','=','%','s',0 };
4759 desc = MSI_RecordGetString(rec, 3);
4760 driver = MSI_RecordGetString(rec, 4);
4761 registration = MSI_RecordGetInteger(rec, 5);
4763 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4764 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4766 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4767 attrs = msi_alloc(len * sizeof(WCHAR));
4768 if (!attrs)
4769 return ERROR_OUTOFMEMORY;
4771 sprintfW(attrs, attrs_fmt, desc);
4772 attrs[len - 1] = '\0';
4774 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4776 ERR("Failed to install SQL data source!\n");
4777 r = ERROR_FUNCTION_FAILED;
4780 msi_free(attrs);
4782 return r;
4785 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4787 UINT rc;
4788 MSIQUERY *view;
4790 static const WCHAR driver_query[] = {
4791 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4792 'O','D','B','C','D','r','i','v','e','r',0 };
4794 static const WCHAR translator_query[] = {
4795 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4796 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4798 static const WCHAR source_query[] = {
4799 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4800 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4802 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4803 if (rc != ERROR_SUCCESS)
4804 return ERROR_SUCCESS;
4806 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4807 msiobj_release(&view->hdr);
4809 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4810 if (rc != ERROR_SUCCESS)
4811 return ERROR_SUCCESS;
4813 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4814 msiobj_release(&view->hdr);
4816 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4817 if (rc != ERROR_SUCCESS)
4818 return ERROR_SUCCESS;
4820 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4821 msiobj_release(&view->hdr);
4823 return rc;
4826 #define ENV_ACT_SETALWAYS 0x1
4827 #define ENV_ACT_SETABSENT 0x2
4828 #define ENV_ACT_REMOVE 0x4
4829 #define ENV_ACT_REMOVEMATCH 0x8
4831 #define ENV_MOD_MACHINE 0x20000000
4832 #define ENV_MOD_APPEND 0x40000000
4833 #define ENV_MOD_PREFIX 0x80000000
4834 #define ENV_MOD_MASK 0xC0000000
4836 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4838 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4840 LPCWSTR cptr = *name;
4841 LPCWSTR ptr = *value;
4843 static const WCHAR prefix[] = {'[','~',']',0};
4844 static const int prefix_len = 3;
4846 *flags = 0;
4847 while (*cptr)
4849 if (*cptr == '=')
4850 *flags |= ENV_ACT_SETALWAYS;
4851 else if (*cptr == '+')
4852 *flags |= ENV_ACT_SETABSENT;
4853 else if (*cptr == '-')
4854 *flags |= ENV_ACT_REMOVE;
4855 else if (*cptr == '!')
4856 *flags |= ENV_ACT_REMOVEMATCH;
4857 else if (*cptr == '*')
4858 *flags |= ENV_MOD_MACHINE;
4859 else
4860 break;
4862 cptr++;
4863 (*name)++;
4866 if (!*cptr)
4868 ERR("Missing environment variable\n");
4869 return ERROR_FUNCTION_FAILED;
4872 if (!strncmpW(ptr, prefix, prefix_len))
4874 *flags |= ENV_MOD_APPEND;
4875 *value += lstrlenW(prefix);
4877 else if (lstrlenW(*value) >= prefix_len)
4879 ptr += lstrlenW(ptr) - prefix_len;
4880 if (!lstrcmpW(ptr, prefix))
4882 *flags |= ENV_MOD_PREFIX;
4883 /* the "[~]" will be removed by deformat_string */;
4887 if (!*flags ||
4888 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4889 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4890 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4891 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4893 ERR("Invalid flags: %08x\n", *flags);
4894 return ERROR_FUNCTION_FAILED;
4897 return ERROR_SUCCESS;
4900 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4902 MSIPACKAGE *package = param;
4903 LPCWSTR name, value, comp;
4904 LPWSTR data = NULL, newval = NULL;
4905 LPWSTR deformatted = NULL, ptr;
4906 DWORD flags, type, size;
4907 LONG res;
4908 HKEY env = NULL, root;
4909 LPCWSTR environment;
4911 static const WCHAR user_env[] =
4912 {'E','n','v','i','r','o','n','m','e','n','t',0};
4913 static const WCHAR machine_env[] =
4914 {'S','y','s','t','e','m','\\',
4915 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4916 'C','o','n','t','r','o','l','\\',
4917 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4918 'E','n','v','i','r','o','n','m','e','n','t',0};
4919 static const WCHAR semicolon[] = {';',0};
4921 name = MSI_RecordGetString(rec, 2);
4922 value = MSI_RecordGetString(rec, 3);
4923 comp = MSI_RecordGetString(rec, 4);
4925 res = env_set_flags(&name, &value, &flags);
4926 if (res != ERROR_SUCCESS)
4927 goto done;
4929 deformat_string(package, value, &deformatted);
4930 if (!deformatted)
4932 res = ERROR_OUTOFMEMORY;
4933 goto done;
4936 value = deformatted;
4938 if (flags & ENV_MOD_MACHINE)
4940 environment = machine_env;
4941 root = HKEY_LOCAL_MACHINE;
4943 else
4945 environment = user_env;
4946 root = HKEY_CURRENT_USER;
4949 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
4950 KEY_ALL_ACCESS, NULL, &env, NULL);
4951 if (res != ERROR_SUCCESS)
4952 goto done;
4954 if (flags & ENV_ACT_REMOVE)
4955 FIXME("Not removing environment variable on uninstall!\n");
4957 size = 0;
4958 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
4959 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
4960 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
4961 goto done;
4963 if (res != ERROR_FILE_NOT_FOUND)
4965 if (flags & ENV_ACT_SETABSENT)
4967 res = ERROR_SUCCESS;
4968 goto done;
4971 data = msi_alloc(size);
4972 if (!data)
4974 RegCloseKey(env);
4975 return ERROR_OUTOFMEMORY;
4978 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
4979 if (res != ERROR_SUCCESS)
4980 goto done;
4982 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
4984 res = RegDeleteKeyW(env, name);
4985 goto done;
4988 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
4989 newval = msi_alloc(size);
4990 ptr = newval;
4991 if (!newval)
4993 res = ERROR_OUTOFMEMORY;
4994 goto done;
4997 if (!(flags & ENV_MOD_MASK))
4998 lstrcpyW(newval, value);
4999 else
5001 if (flags & ENV_MOD_PREFIX)
5003 lstrcpyW(newval, value);
5004 lstrcatW(newval, semicolon);
5005 ptr = newval + lstrlenW(value) + 1;
5008 lstrcpyW(ptr, data);
5010 if (flags & ENV_MOD_APPEND)
5012 lstrcatW(newval, semicolon);
5013 lstrcatW(newval, value);
5017 else
5019 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5020 newval = msi_alloc(size);
5021 if (!newval)
5023 res = ERROR_OUTOFMEMORY;
5024 goto done;
5027 lstrcpyW(newval, value);
5030 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5031 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5033 done:
5034 if (env) RegCloseKey(env);
5035 msi_free(deformatted);
5036 msi_free(data);
5037 msi_free(newval);
5038 return res;
5041 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5043 UINT rc;
5044 MSIQUERY * view;
5045 static const WCHAR ExecSeqQuery[] =
5046 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5047 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5048 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5049 if (rc != ERROR_SUCCESS)
5050 return ERROR_SUCCESS;
5052 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5053 msiobj_release(&view->hdr);
5055 return rc;
5058 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5060 typedef struct
5062 struct list entry;
5063 LPWSTR sourcename;
5064 LPWSTR destname;
5065 LPWSTR source;
5066 LPWSTR dest;
5067 } FILE_LIST;
5069 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5071 BOOL ret;
5073 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5074 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5076 WARN("Source or dest is directory, not moving\n");
5077 return FALSE;
5080 if (options == msidbMoveFileOptionsMove)
5082 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5083 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5084 if (!ret)
5086 WARN("MoveFile failed: %d\n", GetLastError());
5087 return FALSE;
5090 else
5092 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5093 ret = CopyFileW(source, dest, FALSE);
5094 if (!ret)
5096 WARN("CopyFile failed: %d\n", GetLastError());
5097 return FALSE;
5101 return TRUE;
5104 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5106 LPWSTR path, ptr;
5107 DWORD dirlen, pathlen;
5109 ptr = strrchrW(wildcard, '\\');
5110 dirlen = ptr - wildcard + 1;
5112 pathlen = dirlen + lstrlenW(filename) + 1;
5113 path = msi_alloc(pathlen * sizeof(WCHAR));
5115 lstrcpynW(path, wildcard, dirlen + 1);
5116 lstrcatW(path, filename);
5118 return path;
5121 static void free_file_entry(FILE_LIST *file)
5123 msi_free(file->source);
5124 msi_free(file->dest);
5125 msi_free(file);
5128 static void free_list(FILE_LIST *list)
5130 while (!list_empty(&list->entry))
5132 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5134 list_remove(&file->entry);
5135 free_file_entry(file);
5139 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5141 FILE_LIST *new, *file;
5142 LPWSTR ptr, filename;
5143 DWORD size;
5145 new = msi_alloc_zero(sizeof(FILE_LIST));
5146 if (!new)
5147 return FALSE;
5149 new->source = strdupW(source);
5150 ptr = strrchrW(dest, '\\') + 1;
5151 filename = strrchrW(new->source, '\\') + 1;
5153 new->sourcename = filename;
5155 if (*ptr)
5156 new->destname = ptr;
5157 else
5158 new->destname = new->sourcename;
5160 size = (ptr - dest) + lstrlenW(filename) + 1;
5161 new->dest = msi_alloc(size * sizeof(WCHAR));
5162 if (!new->dest)
5164 free_file_entry(new);
5165 return FALSE;
5168 lstrcpynW(new->dest, dest, ptr - dest + 1);
5169 lstrcatW(new->dest, filename);
5171 if (list_empty(&files->entry))
5173 list_add_head(&files->entry, &new->entry);
5174 return TRUE;
5177 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5179 if (lstrcmpW(source, file->source) < 0)
5181 list_add_before(&file->entry, &new->entry);
5182 return TRUE;
5186 list_add_after(&file->entry, &new->entry);
5187 return TRUE;
5190 BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5192 WIN32_FIND_DATAW wfd;
5193 HANDLE hfile;
5194 LPWSTR path;
5195 BOOL res;
5196 FILE_LIST files, *file;
5197 DWORD size;
5199 hfile = FindFirstFileW(source, &wfd);
5200 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5202 list_init(&files.entry);
5204 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5206 if (is_dot_dir(wfd.cFileName)) continue;
5208 path = wildcard_to_file(source, wfd.cFileName);
5209 if (!path)
5211 res = FALSE;
5212 goto done;
5215 add_wildcard(&files, path, dest);
5216 msi_free(path);
5219 /* only the first wildcard match gets renamed to dest */
5220 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5221 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5222 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5223 if (!file->dest)
5225 res = FALSE;
5226 goto done;
5229 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5231 while (!list_empty(&files.entry))
5233 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5235 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5237 list_remove(&file->entry);
5238 free_file_entry(file);
5241 res = TRUE;
5243 done:
5244 free_list(&files);
5245 FindClose(hfile);
5246 return res;
5249 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5251 MSIPACKAGE *package = param;
5252 MSICOMPONENT *comp;
5253 LPCWSTR sourcename, destname;
5254 LPWSTR sourcedir = NULL, destdir = NULL;
5255 LPWSTR source = NULL, dest = NULL;
5256 int options;
5257 DWORD size;
5258 BOOL ret, wildcards;
5260 static const WCHAR backslash[] = {'\\',0};
5262 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5263 if (!comp || !comp->Enabled ||
5264 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5266 TRACE("Component not set for install, not moving file\n");
5267 return ERROR_SUCCESS;
5270 sourcename = MSI_RecordGetString(rec, 3);
5271 destname = MSI_RecordGetString(rec, 4);
5272 options = MSI_RecordGetInteger(rec, 7);
5274 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5275 if (!sourcedir)
5276 goto done;
5278 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5279 if (!destdir)
5280 goto done;
5282 if (!sourcename)
5284 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5285 goto done;
5287 source = strdupW(sourcedir);
5288 if (!source)
5289 goto done;
5291 else
5293 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5294 source = msi_alloc(size * sizeof(WCHAR));
5295 if (!source)
5296 goto done;
5298 lstrcpyW(source, sourcedir);
5299 if (source[lstrlenW(source) - 1] != '\\')
5300 lstrcatW(source, backslash);
5301 lstrcatW(source, sourcename);
5304 wildcards = strchrW(source, '*') || strchrW(source, '?');
5306 if (!destname && !wildcards)
5308 destname = strdupW(sourcename);
5309 if (!destname)
5310 goto done;
5313 size = 0;
5314 if (destname)
5315 size = lstrlenW(destname);
5317 size += lstrlenW(destdir) + 2;
5318 dest = msi_alloc(size * sizeof(WCHAR));
5319 if (!dest)
5320 goto done;
5322 lstrcpyW(dest, destdir);
5323 if (dest[lstrlenW(dest) - 1] != '\\')
5324 lstrcatW(dest, backslash);
5326 if (destname)
5327 lstrcatW(dest, destname);
5329 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5331 ret = CreateDirectoryW(destdir, NULL);
5332 if (!ret)
5334 WARN("CreateDirectory failed: %d\n", GetLastError());
5335 return ERROR_SUCCESS;
5339 if (!wildcards)
5340 msi_move_file(source, dest, options);
5341 else
5342 move_files_wildcard(source, dest, options);
5344 done:
5345 msi_free(sourcedir);
5346 msi_free(destdir);
5347 msi_free(source);
5348 msi_free(dest);
5350 return ERROR_SUCCESS;
5353 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5355 UINT rc;
5356 MSIQUERY *view;
5358 static const WCHAR ExecSeqQuery[] =
5359 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5360 '`','M','o','v','e','F','i','l','e','`',0};
5362 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5363 if (rc != ERROR_SUCCESS)
5364 return ERROR_SUCCESS;
5366 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5367 msiobj_release(&view->hdr);
5369 return rc;
5372 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5373 LPCSTR action, LPCWSTR table )
5375 static const WCHAR query[] = {
5376 'S','E','L','E','C','T',' ','*',' ',
5377 'F','R','O','M',' ','`','%','s','`',0 };
5378 MSIQUERY *view = NULL;
5379 DWORD count = 0;
5380 UINT r;
5382 r = MSI_OpenQuery( package->db, &view, query, table );
5383 if (r == ERROR_SUCCESS)
5385 r = MSI_IterateRecords(view, &count, NULL, package);
5386 msiobj_release(&view->hdr);
5389 if (count)
5390 FIXME("%s -> %u ignored %s table values\n",
5391 action, count, debugstr_w(table));
5393 return ERROR_SUCCESS;
5396 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5398 TRACE("%p\n", package);
5399 return ERROR_SUCCESS;
5402 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5404 static const WCHAR table[] =
5405 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5406 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5409 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5411 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5412 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5415 static UINT ACTION_BindImage( MSIPACKAGE *package )
5417 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5418 return msi_unimplemented_action_stub( package, "BindImage", table );
5421 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5423 static const WCHAR table[] = {
5424 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5425 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5428 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5430 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5431 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5434 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5436 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5437 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5440 static UINT ACTION_StopServices( MSIPACKAGE *package )
5442 static const WCHAR table[] = {
5443 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5444 return msi_unimplemented_action_stub( package, "StopServices", table );
5447 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5449 static const WCHAR table[] = {
5450 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5451 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5453 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5455 static const WCHAR table[] = {
5456 'P','r','o','d','u','c','t','I','D',0 };
5457 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5460 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5462 static const WCHAR table[] = {
5463 'E','n','v','i','r','o','n','m','e','n','t',0 };
5464 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5467 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5469 static const WCHAR table[] = {
5470 'M','s','i','A','s','s','e','m','b','l','y',0 };
5471 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5474 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5476 static const WCHAR table[] = {
5477 'M','s','i','A','s','s','e','m','b','l','y',0 };
5478 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5481 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5483 static const WCHAR table[] = { 'F','o','n','t',0 };
5484 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5487 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5489 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5490 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5493 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5495 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5496 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5499 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5501 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5502 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5505 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5507 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5508 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5511 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5513 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5514 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5517 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5519 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5520 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5523 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5525 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5526 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5529 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5531 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5532 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5535 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5537 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5538 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5541 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5543 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5544 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5547 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5549 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5550 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5553 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5555 static const WCHAR table[] = { 'A','p','p','I','d',0 };
5556 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5559 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5561 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5562 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5565 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5567 static const WCHAR table[] = { 'M','I','M','E',0 };
5568 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5571 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5573 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5574 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5577 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5579 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5580 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5583 static const struct _actions StandardActions[] = {
5584 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5585 { szAppSearch, ACTION_AppSearch },
5586 { szBindImage, ACTION_BindImage },
5587 { szCCPSearch, ACTION_CCPSearch },
5588 { szCostFinalize, ACTION_CostFinalize },
5589 { szCostInitialize, ACTION_CostInitialize },
5590 { szCreateFolders, ACTION_CreateFolders },
5591 { szCreateShortcuts, ACTION_CreateShortcuts },
5592 { szDeleteServices, ACTION_DeleteServices },
5593 { szDisableRollback, NULL },
5594 { szDuplicateFiles, ACTION_DuplicateFiles },
5595 { szExecuteAction, ACTION_ExecuteAction },
5596 { szFileCost, ACTION_FileCost },
5597 { szFindRelatedProducts, ACTION_FindRelatedProducts },
5598 { szForceReboot, ACTION_ForceReboot },
5599 { szInstallAdminPackage, NULL },
5600 { szInstallExecute, ACTION_InstallExecute },
5601 { szInstallExecuteAgain, ACTION_InstallExecute },
5602 { szInstallFiles, ACTION_InstallFiles},
5603 { szInstallFinalize, ACTION_InstallFinalize },
5604 { szInstallInitialize, ACTION_InstallInitialize },
5605 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5606 { szInstallValidate, ACTION_InstallValidate },
5607 { szIsolateComponents, ACTION_IsolateComponents },
5608 { szLaunchConditions, ACTION_LaunchConditions },
5609 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5610 { szMoveFiles, ACTION_MoveFiles },
5611 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5612 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5613 { szInstallODBC, ACTION_InstallODBC },
5614 { szInstallServices, ACTION_InstallServices },
5615 { szPatchFiles, ACTION_PatchFiles },
5616 { szProcessComponents, ACTION_ProcessComponents },
5617 { szPublishComponents, ACTION_PublishComponents },
5618 { szPublishFeatures, ACTION_PublishFeatures },
5619 { szPublishProduct, ACTION_PublishProduct },
5620 { szRegisterClassInfo, ACTION_RegisterClassInfo },
5621 { szRegisterComPlus, ACTION_RegisterComPlus},
5622 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5623 { szRegisterFonts, ACTION_RegisterFonts },
5624 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5625 { szRegisterProduct, ACTION_RegisterProduct },
5626 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5627 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5628 { szRegisterUser, ACTION_RegisterUser },
5629 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5630 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5631 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5632 { szRemoveFiles, ACTION_RemoveFiles },
5633 { szRemoveFolders, ACTION_RemoveFolders },
5634 { szRemoveIniValues, ACTION_RemoveIniValues },
5635 { szRemoveODBC, ACTION_RemoveODBC },
5636 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5637 { szRemoveShortcuts, ACTION_RemoveShortcuts },
5638 { szResolveSource, ACTION_ResolveSource },
5639 { szRMCCPSearch, ACTION_RMCCPSearch },
5640 { szScheduleReboot, NULL },
5641 { szSelfRegModules, ACTION_SelfRegModules },
5642 { szSelfUnregModules, ACTION_SelfUnregModules },
5643 { szSetODBCFolders, NULL },
5644 { szStartServices, ACTION_StartServices },
5645 { szStopServices, ACTION_StopServices },
5646 { szUnpublishComponents, ACTION_UnpublishComponents },
5647 { szUnpublishFeatures, ACTION_UnpublishFeatures },
5648 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5649 { szUnregisterComPlus, ACTION_UnregisterComPlus },
5650 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5651 { szUnregisterFonts, ACTION_UnregisterFonts },
5652 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5653 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5654 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5655 { szValidateProductID, ACTION_ValidateProductID },
5656 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5657 { szWriteIniValues, ACTION_WriteIniValues },
5658 { szWriteRegistryValues, ACTION_WriteRegistryValues },
5659 { NULL, NULL },