push 9ed7f32abbb93ea3a813fd6b1650e5bcfc506606
[wine/hacks.git] / dlls / msi / action.c
blob80caaac13ecc7fad00eaa0b25fed356c4af8bc34
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * Pages I need
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
26 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
29 #include <stdarg.h>
31 #define COBJMACROS
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winerror.h"
36 #include "winreg.h"
37 #include "winsvc.h"
38 #include "odbcinst.h"
39 #include "wine/debug.h"
40 #include "msidefs.h"
41 #include "msipriv.h"
42 #include "winuser.h"
43 #include "shlobj.h"
44 #include "wine/unicode.h"
45 #include "winver.h"
47 #define REG_PROGRESS_VALUE 13200
48 #define COMPONENT_PROGRESS_VALUE 24000
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
53 * Prototypes
55 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
56 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
57 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
60 * consts and values used
62 static const WCHAR c_colon[] = {'C',':','\\',0};
64 static const WCHAR szCreateFolders[] =
65 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
66 static const WCHAR szCostFinalize[] =
67 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
68 const WCHAR szInstallFiles[] =
69 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
70 const WCHAR szDuplicateFiles[] =
71 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
72 static const WCHAR szWriteRegistryValues[] =
73 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
74 'V','a','l','u','e','s',0};
75 static const WCHAR szCostInitialize[] =
76 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
77 static const WCHAR szFileCost[] =
78 {'F','i','l','e','C','o','s','t',0};
79 static const WCHAR szInstallInitialize[] =
80 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
81 static const WCHAR szInstallValidate[] =
82 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
83 static const WCHAR szLaunchConditions[] =
84 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
85 static const WCHAR szProcessComponents[] =
86 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
87 static const WCHAR szRegisterTypeLibraries[] =
88 {'R','e','g','i','s','t','e','r','T','y','p','e',
89 'L','i','b','r','a','r','i','e','s',0};
90 const WCHAR szRegisterClassInfo[] =
91 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
92 const WCHAR szRegisterProgIdInfo[] =
93 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
94 static const WCHAR szCreateShortcuts[] =
95 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
96 static const WCHAR szPublishProduct[] =
97 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
98 static const WCHAR szWriteIniValues[] =
99 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
100 static const WCHAR szSelfRegModules[] =
101 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
102 static const WCHAR szPublishFeatures[] =
103 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
104 static const WCHAR szRegisterProduct[] =
105 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
106 static const WCHAR szInstallExecute[] =
107 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
108 static const WCHAR szInstallExecuteAgain[] =
109 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
110 'A','g','a','i','n',0};
111 static const WCHAR szInstallFinalize[] =
112 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
113 static const WCHAR szForceReboot[] =
114 {'F','o','r','c','e','R','e','b','o','o','t',0};
115 static const WCHAR szResolveSource[] =
116 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
117 static const WCHAR szAppSearch[] =
118 {'A','p','p','S','e','a','r','c','h',0};
119 static const WCHAR szAllocateRegistrySpace[] =
120 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
121 'S','p','a','c','e',0};
122 static const WCHAR szBindImage[] =
123 {'B','i','n','d','I','m','a','g','e',0};
124 static const WCHAR szCCPSearch[] =
125 {'C','C','P','S','e','a','r','c','h',0};
126 static const WCHAR szDeleteServices[] =
127 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
128 static const WCHAR szDisableRollback[] =
129 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
130 static const WCHAR szExecuteAction[] =
131 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
132 const WCHAR szFindRelatedProducts[] =
133 {'F','i','n','d','R','e','l','a','t','e','d',
134 'P','r','o','d','u','c','t','s',0};
135 static const WCHAR szInstallAdminPackage[] =
136 {'I','n','s','t','a','l','l','A','d','m','i','n',
137 'P','a','c','k','a','g','e',0};
138 static const WCHAR szInstallSFPCatalogFile[] =
139 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
140 'F','i','l','e',0};
141 static const WCHAR szIsolateComponents[] =
142 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
143 const WCHAR szMigrateFeatureStates[] =
144 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
145 'S','t','a','t','e','s',0};
146 const WCHAR szMoveFiles[] =
147 {'M','o','v','e','F','i','l','e','s',0};
148 static const WCHAR szMsiPublishAssemblies[] =
149 {'M','s','i','P','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szMsiUnpublishAssemblies[] =
152 {'M','s','i','U','n','p','u','b','l','i','s','h',
153 'A','s','s','e','m','b','l','i','e','s',0};
154 static const WCHAR szInstallODBC[] =
155 {'I','n','s','t','a','l','l','O','D','B','C',0};
156 static const WCHAR szInstallServices[] =
157 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
158 const WCHAR szPatchFiles[] =
159 {'P','a','t','c','h','F','i','l','e','s',0};
160 static const WCHAR szPublishComponents[] =
161 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
162 static const WCHAR szRegisterComPlus[] =
163 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
164 const WCHAR szRegisterExtensionInfo[] =
165 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
166 'I','n','f','o',0};
167 static const WCHAR szRegisterFonts[] =
168 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
169 const WCHAR szRegisterMIMEInfo[] =
170 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
171 static const WCHAR szRegisterUser[] =
172 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
173 const WCHAR szRemoveDuplicateFiles[] =
174 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
175 'F','i','l','e','s',0};
176 static const WCHAR szRemoveEnvironmentStrings[] =
177 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
178 'S','t','r','i','n','g','s',0};
179 const WCHAR szRemoveExistingProducts[] =
180 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
181 'P','r','o','d','u','c','t','s',0};
182 const WCHAR szRemoveFiles[] =
183 {'R','e','m','o','v','e','F','i','l','e','s',0};
184 static const WCHAR szRemoveFolders[] =
185 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
186 static const WCHAR szRemoveIniValues[] =
187 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
188 static const WCHAR szRemoveODBC[] =
189 {'R','e','m','o','v','e','O','D','B','C',0};
190 static const WCHAR szRemoveRegistryValues[] =
191 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
192 'V','a','l','u','e','s',0};
193 static const WCHAR szRemoveShortcuts[] =
194 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
195 static const WCHAR szRMCCPSearch[] =
196 {'R','M','C','C','P','S','e','a','r','c','h',0};
197 static const WCHAR szScheduleReboot[] =
198 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
199 static const WCHAR szSelfUnregModules[] =
200 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
201 static const WCHAR szSetODBCFolders[] =
202 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
203 static const WCHAR szStartServices[] =
204 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
205 static const WCHAR szStopServices[] =
206 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
207 static const WCHAR szUnpublishComponents[] =
208 {'U','n','p','u','b','l','i','s','h',
209 'C','o','m','p','o','n','e','n','t','s',0};
210 static const WCHAR szUnpublishFeatures[] =
211 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
212 const WCHAR szUnregisterClassInfo[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
214 'I','n','f','o',0};
215 static const WCHAR szUnregisterComPlus[] =
216 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
217 const WCHAR szUnregisterExtensionInfo[] =
218 {'U','n','r','e','g','i','s','t','e','r',
219 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
220 static const WCHAR szUnregisterFonts[] =
221 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
222 const WCHAR szUnregisterMIMEInfo[] =
223 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
224 const WCHAR szUnregisterProgIdInfo[] =
225 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
226 'I','n','f','o',0};
227 static const WCHAR szUnregisterTypeLibraries[] =
228 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
229 'L','i','b','r','a','r','i','e','s',0};
230 static const WCHAR szValidateProductID[] =
231 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
232 static const WCHAR szWriteEnvironmentStrings[] =
233 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
234 'S','t','r','i','n','g','s',0};
236 /* action handlers */
237 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
239 struct _actions {
240 LPCWSTR action;
241 STANDARDACTIONHANDLER handler;
244 static const struct _actions StandardActions[];
247 /********************************************************
248 * helper functions
249 ********************************************************/
251 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
253 static const WCHAR Query_t[] =
254 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
255 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
256 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
257 ' ','\'','%','s','\'',0};
258 MSIRECORD * row;
260 row = MSI_QueryGetRecord( package->db, Query_t, action );
261 if (!row)
262 return;
263 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
264 msiobj_release(&row->hdr);
267 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
268 UINT rc)
270 MSIRECORD * row;
271 static const WCHAR template_s[]=
272 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
273 '%','s', '.',0};
274 static const WCHAR template_e[]=
275 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
276 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
277 '%','i','.',0};
278 static const WCHAR format[] =
279 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
280 WCHAR message[1024];
281 WCHAR timet[0x100];
283 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
284 if (start)
285 sprintfW(message,template_s,timet,action);
286 else
287 sprintfW(message,template_e,timet,action,rc);
289 row = MSI_CreateRecord(1);
290 MSI_RecordSetStringW(row,1,message);
292 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
293 msiobj_release(&row->hdr);
296 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
298 LPCWSTR ptr,ptr2;
299 BOOL quote;
300 DWORD len;
301 LPWSTR prop = NULL, val = NULL;
303 if (!szCommandLine)
304 return ERROR_SUCCESS;
306 ptr = szCommandLine;
308 while (*ptr)
310 if (*ptr==' ')
312 ptr++;
313 continue;
316 TRACE("Looking at %s\n",debugstr_w(ptr));
318 ptr2 = strchrW(ptr,'=');
319 if (!ptr2)
321 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
322 break;
325 quote = FALSE;
327 len = ptr2-ptr;
328 prop = msi_alloc((len+1)*sizeof(WCHAR));
329 memcpy(prop,ptr,len*sizeof(WCHAR));
330 prop[len]=0;
331 ptr2++;
333 len = 0;
334 ptr = ptr2;
335 while (*ptr && (quote || (!quote && *ptr!=' ')))
337 if (*ptr == '"')
338 quote = !quote;
339 ptr++;
340 len++;
343 if (*ptr2=='"')
345 ptr2++;
346 len -= 2;
348 val = msi_alloc((len+1)*sizeof(WCHAR));
349 memcpy(val,ptr2,len*sizeof(WCHAR));
350 val[len] = 0;
352 if (lstrlenW(prop) > 0)
354 TRACE("Found commandline property (%s) = (%s)\n",
355 debugstr_w(prop), debugstr_w(val));
356 MSI_SetPropertyW(package,prop,val);
358 msi_free(val);
359 msi_free(prop);
362 return ERROR_SUCCESS;
366 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
368 LPCWSTR pc;
369 LPWSTR p, *ret = NULL;
370 UINT count = 0;
372 if (!str)
373 return ret;
375 /* count the number of substrings */
376 for ( pc = str, count = 0; pc; count++ )
378 pc = strchrW( pc, sep );
379 if (pc)
380 pc++;
383 /* allocate space for an array of substring pointers and the substrings */
384 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
385 (lstrlenW(str)+1) * sizeof(WCHAR) );
386 if (!ret)
387 return ret;
389 /* copy the string and set the pointers */
390 p = (LPWSTR) &ret[count+1];
391 lstrcpyW( p, str );
392 for( count = 0; (ret[count] = p); count++ )
394 p = strchrW( p, sep );
395 if (p)
396 *p++ = 0;
399 return ret;
402 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
404 WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
405 LPWSTR prod_code, patch_product;
406 UINT ret;
408 prod_code = msi_dup_property( package, szProductCode );
409 patch_product = msi_get_suminfo_product( patch );
411 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
413 if ( strstrW( patch_product, prod_code ) )
414 ret = ERROR_SUCCESS;
415 else
416 ret = ERROR_FUNCTION_FAILED;
418 msi_free( patch_product );
419 msi_free( prod_code );
421 return ret;
424 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
425 MSIDATABASE *patch_db, LPCWSTR name )
427 UINT ret = ERROR_FUNCTION_FAILED;
428 IStorage *stg = NULL;
429 HRESULT r;
431 TRACE("%p %s\n", package, debugstr_w(name) );
433 if (*name++ != ':')
435 ERR("expected a colon in %s\n", debugstr_w(name));
436 return ERROR_FUNCTION_FAILED;
439 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
440 if (SUCCEEDED(r))
442 ret = msi_check_transform_applicable( package, stg );
443 if (ret == ERROR_SUCCESS)
444 msi_table_apply_transform( package->db, stg );
445 else
446 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
447 IStorage_Release( stg );
449 else
450 ERR("failed to open substorage %s\n", debugstr_w(name));
452 return ERROR_SUCCESS;
455 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
457 static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
458 LPWSTR guid_list, *guids, product_code;
459 UINT i, ret = ERROR_FUNCTION_FAILED;
461 product_code = msi_dup_property( package, szProdCode );
462 if (!product_code)
464 /* FIXME: the property ProductCode should be written into the DB somewhere */
465 ERR("no product code to check\n");
466 return ERROR_SUCCESS;
469 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
470 guids = msi_split_string( guid_list, ';' );
471 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
473 if (!lstrcmpW( guids[i], product_code ))
474 ret = ERROR_SUCCESS;
476 msi_free( guids );
477 msi_free( guid_list );
478 msi_free( product_code );
480 return ret;
483 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
485 MSISUMMARYINFO *si;
486 LPWSTR str, *substorage;
487 UINT i, r = ERROR_SUCCESS;
489 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
490 if (!si)
491 return ERROR_FUNCTION_FAILED;
493 msi_check_patch_applicable( package, si );
495 /* enumerate the substorage */
496 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
497 substorage = msi_split_string( str, ';' );
498 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
499 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
500 msi_free( substorage );
501 msi_free( str );
503 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
505 msiobj_release( &si->hdr );
507 return r;
510 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
512 MSIDATABASE *patch_db = NULL;
513 UINT r;
515 TRACE("%p %s\n", package, debugstr_w( file ) );
517 /* FIXME:
518 * We probably want to make sure we only open a patch collection here.
519 * Patch collections (.msp) and databases (.msi) have different GUIDs
520 * but currently MSI_OpenDatabaseW will accept both.
522 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
523 if ( r != ERROR_SUCCESS )
525 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
526 return r;
529 msi_parse_patch_summary( package, patch_db );
532 * There might be a CAB file in the patch package,
533 * so append it to the list of storage to search for streams.
535 append_storage_to_db( package->db, patch_db->storage );
537 msiobj_release( &patch_db->hdr );
539 return ERROR_SUCCESS;
542 /* get the PATCH property, and apply all the patches it specifies */
543 static UINT msi_apply_patches( MSIPACKAGE *package )
545 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
546 LPWSTR patch_list, *patches;
547 UINT i, r = ERROR_SUCCESS;
549 patch_list = msi_dup_property( package, szPatch );
551 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
553 patches = msi_split_string( patch_list, ';' );
554 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
555 r = msi_apply_patch_package( package, patches[i] );
557 msi_free( patches );
558 msi_free( patch_list );
560 return r;
563 static UINT msi_apply_transforms( MSIPACKAGE *package )
565 static const WCHAR szTransforms[] = {
566 'T','R','A','N','S','F','O','R','M','S',0 };
567 LPWSTR xform_list, *xforms;
568 UINT i, r = ERROR_SUCCESS;
570 xform_list = msi_dup_property( package, szTransforms );
571 xforms = msi_split_string( xform_list, ';' );
573 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
575 if (xforms[i][0] == ':')
576 r = msi_apply_substorage_transform( package, package->db, &xforms[i][1] );
577 else
578 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
581 msi_free( xforms );
582 msi_free( xform_list );
584 return r;
587 BOOL ui_sequence_exists( MSIPACKAGE *package )
589 MSIQUERY *view;
590 UINT rc;
592 static const WCHAR ExecSeqQuery [] =
593 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
594 '`','I','n','s','t','a','l','l',
595 'U','I','S','e','q','u','e','n','c','e','`',
596 ' ','W','H','E','R','E',' ',
597 '`','S','e','q','u','e','n','c','e','`',' ',
598 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
599 '`','S','e','q','u','e','n','c','e','`',0};
601 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
602 if (rc == ERROR_SUCCESS)
604 msiobj_release(&view->hdr);
605 return TRUE;
608 return FALSE;
611 /****************************************************
612 * TOP level entry points
613 *****************************************************/
615 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
616 LPCWSTR szCommandLine )
618 UINT rc;
619 BOOL ui = FALSE, ui_exists;
620 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
621 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
622 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
624 MSI_SetPropertyW(package, szAction, szInstall);
626 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
628 package->script->InWhatSequence = SEQUENCE_INSTALL;
630 if (szPackagePath)
632 LPWSTR p, check, dir;
633 LPCWSTR file;
635 dir = strdupW(szPackagePath);
636 p = strrchrW(dir, '\\');
637 if (p)
639 *(++p) = 0;
640 file = szPackagePath + (p - dir);
642 else
644 msi_free(dir);
645 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
646 GetCurrentDirectoryW(MAX_PATH, dir);
647 lstrcatW(dir, cszbs);
648 file = szPackagePath;
651 msi_free( package->PackagePath );
652 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
653 if (!package->PackagePath)
655 msi_free(dir);
656 return ERROR_OUTOFMEMORY;
659 lstrcpyW(package->PackagePath, dir);
660 lstrcatW(package->PackagePath, file);
662 check = msi_dup_property( package, cszSourceDir );
663 if (!check)
664 MSI_SetPropertyW(package, cszSourceDir, dir);
665 msi_free(check);
667 check = msi_dup_property( package, cszSOURCEDIR );
668 if (!check)
669 MSI_SetPropertyW(package, cszSOURCEDIR, dir);
671 msi_free(dir);
672 msi_free(check);
675 msi_parse_command_line( package, szCommandLine );
677 msi_apply_transforms( package );
678 msi_apply_patches( package );
680 /* properties may have been added by a transform */
681 msi_clone_properties( package );
683 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
685 package->script->InWhatSequence |= SEQUENCE_UI;
686 rc = ACTION_ProcessUISequence(package);
687 ui = TRUE;
688 ui_exists = ui_sequence_exists(package);
689 if (rc == ERROR_SUCCESS || !ui_exists)
691 package->script->InWhatSequence |= SEQUENCE_EXEC;
692 rc = ACTION_ProcessExecSequence(package,ui_exists);
695 else
696 rc = ACTION_ProcessExecSequence(package,FALSE);
698 if (rc == -1)
700 /* install was halted but should be considered a success */
701 rc = ERROR_SUCCESS;
704 package->script->CurrentlyScripting= FALSE;
706 /* process the ending type action */
707 if (rc == ERROR_SUCCESS)
708 ACTION_PerformActionSequence(package,-1,ui);
709 else if (rc == ERROR_INSTALL_USEREXIT)
710 ACTION_PerformActionSequence(package,-2,ui);
711 else if (rc == ERROR_INSTALL_SUSPEND)
712 ACTION_PerformActionSequence(package,-4,ui);
713 else /* failed */
714 ACTION_PerformActionSequence(package,-3,ui);
716 /* finish up running custom actions */
717 ACTION_FinishCustomActions(package);
719 return rc;
722 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
724 UINT rc = ERROR_SUCCESS;
725 MSIRECORD * row = 0;
726 static const WCHAR ExecSeqQuery[] =
727 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
728 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
729 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
730 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
732 static const WCHAR UISeqQuery[] =
733 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
734 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
735 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
736 ' ', '=',' ','%','i',0};
738 if (UI)
739 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
740 else
741 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
743 if (row)
745 LPCWSTR action, cond;
747 TRACE("Running the actions\n");
749 /* check conditions */
750 cond = MSI_RecordGetString(row,2);
752 /* this is a hack to skip errors in the condition code */
753 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
754 goto end;
756 action = MSI_RecordGetString(row,1);
757 if (!action)
759 ERR("failed to fetch action\n");
760 rc = ERROR_FUNCTION_FAILED;
761 goto end;
764 if (UI)
765 rc = ACTION_PerformUIAction(package,action,-1);
766 else
767 rc = ACTION_PerformAction(package,action,-1,FALSE);
768 end:
769 msiobj_release(&row->hdr);
771 else
772 rc = ERROR_SUCCESS;
774 return rc;
777 typedef struct {
778 MSIPACKAGE* package;
779 BOOL UI;
780 } iterate_action_param;
782 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
784 iterate_action_param *iap= (iterate_action_param*)param;
785 UINT rc;
786 LPCWSTR cond, action;
788 action = MSI_RecordGetString(row,1);
789 if (!action)
791 ERR("Error is retrieving action name\n");
792 return ERROR_FUNCTION_FAILED;
795 /* check conditions */
796 cond = MSI_RecordGetString(row,2);
798 /* this is a hack to skip errors in the condition code */
799 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
801 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
802 return ERROR_SUCCESS;
805 if (iap->UI)
806 rc = ACTION_PerformUIAction(iap->package,action,-1);
807 else
808 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
810 msi_dialog_check_messages( NULL );
812 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
813 rc = iap->package->CurrentInstallState;
815 if (rc == ERROR_FUNCTION_NOT_CALLED)
816 rc = ERROR_SUCCESS;
818 if (rc != ERROR_SUCCESS)
819 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
821 return rc;
824 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
826 MSIQUERY * view;
827 UINT r;
828 static const WCHAR query[] =
829 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
830 '`','%','s','`',
831 ' ','W','H','E','R','E',' ',
832 '`','S','e','q','u','e','n','c','e','`',' ',
833 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
834 '`','S','e','q','u','e','n','c','e','`',0};
835 iterate_action_param iap;
838 * FIXME: probably should be checking UILevel in the
839 * ACTION_PerformUIAction/ACTION_PerformAction
840 * rather than saving the UI level here. Those
841 * two functions can be merged too.
843 iap.package = package;
844 iap.UI = TRUE;
846 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
848 r = MSI_OpenQuery( package->db, &view, query, szTable );
849 if (r == ERROR_SUCCESS)
851 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
852 msiobj_release(&view->hdr);
855 return r;
858 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
860 MSIQUERY * view;
861 UINT rc;
862 static const WCHAR ExecSeqQuery[] =
863 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
864 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
865 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
866 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
867 'O','R','D','E','R',' ', 'B','Y',' ',
868 '`','S','e','q','u','e','n','c','e','`',0 };
869 MSIRECORD * row = 0;
870 static const WCHAR IVQuery[] =
871 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
872 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
873 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
874 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
875 ' ','\'', 'I','n','s','t','a','l','l',
876 'V','a','l','i','d','a','t','e','\'', 0};
877 INT seq = 0;
878 iterate_action_param iap;
880 iap.package = package;
881 iap.UI = FALSE;
883 if (package->script->ExecuteSequenceRun)
885 TRACE("Execute Sequence already Run\n");
886 return ERROR_SUCCESS;
889 package->script->ExecuteSequenceRun = TRUE;
891 /* get the sequence number */
892 if (UIran)
894 row = MSI_QueryGetRecord(package->db, IVQuery);
895 if( !row )
896 return ERROR_FUNCTION_FAILED;
897 seq = MSI_RecordGetInteger(row,1);
898 msiobj_release(&row->hdr);
901 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
902 if (rc == ERROR_SUCCESS)
904 TRACE("Running the actions\n");
906 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
907 msiobj_release(&view->hdr);
910 return rc;
913 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
915 MSIQUERY * view;
916 UINT rc;
917 static const WCHAR ExecSeqQuery [] =
918 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
919 '`','I','n','s','t','a','l','l',
920 'U','I','S','e','q','u','e','n','c','e','`',
921 ' ','W','H','E','R','E',' ',
922 '`','S','e','q','u','e','n','c','e','`',' ',
923 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
924 '`','S','e','q','u','e','n','c','e','`',0};
925 iterate_action_param iap;
927 iap.package = package;
928 iap.UI = TRUE;
930 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
932 if (rc == ERROR_SUCCESS)
934 TRACE("Running the actions\n");
936 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
937 msiobj_release(&view->hdr);
940 return rc;
943 /********************************************************
944 * ACTION helper functions and functions that perform the actions
945 *******************************************************/
946 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
947 UINT* rc, BOOL force )
949 BOOL ret = FALSE;
950 BOOL run = force;
951 int i;
953 if (!run && !package->script->CurrentlyScripting)
954 run = TRUE;
956 if (!run)
958 if (strcmpW(action,szInstallFinalize) == 0 ||
959 strcmpW(action,szInstallExecute) == 0 ||
960 strcmpW(action,szInstallExecuteAgain) == 0)
961 run = TRUE;
964 i = 0;
965 while (StandardActions[i].action != NULL)
967 if (strcmpW(StandardActions[i].action, action)==0)
969 if (!run)
971 ui_actioninfo(package, action, TRUE, 0);
972 *rc = schedule_action(package,INSTALL_SCRIPT,action);
973 ui_actioninfo(package, action, FALSE, *rc);
975 else
977 ui_actionstart(package, action);
978 if (StandardActions[i].handler)
980 *rc = StandardActions[i].handler(package);
982 else
984 FIXME("unhandled standard action %s\n",debugstr_w(action));
985 *rc = ERROR_SUCCESS;
988 ret = TRUE;
989 break;
991 i++;
993 return ret;
996 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
997 UINT* rc, UINT script, BOOL force )
999 BOOL ret=FALSE;
1000 UINT arc;
1002 arc = ACTION_CustomAction(package, action, script, force);
1004 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1006 *rc = arc;
1007 ret = TRUE;
1009 return ret;
1013 * A lot of actions are really important even if they don't do anything
1014 * explicit... Lots of properties are set at the beginning of the installation
1015 * CostFinalize does a bunch of work to translate the directories and such
1017 * But until I get write access to the database that is hard, so I am going to
1018 * hack it to see if I can get something to run.
1020 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1022 UINT rc = ERROR_SUCCESS;
1023 BOOL handled;
1025 TRACE("Performing action (%s)\n",debugstr_w(action));
1027 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1029 if (!handled)
1030 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1032 if (!handled)
1034 FIXME("unhandled msi action %s\n",debugstr_w(action));
1035 rc = ERROR_FUNCTION_NOT_CALLED;
1038 return rc;
1041 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1043 UINT rc = ERROR_SUCCESS;
1044 BOOL handled = FALSE;
1046 TRACE("Performing action (%s)\n",debugstr_w(action));
1048 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1050 if (!handled)
1051 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1053 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1054 handled = TRUE;
1056 if (!handled)
1058 FIXME("unhandled msi action %s\n",debugstr_w(action));
1059 rc = ERROR_FUNCTION_NOT_CALLED;
1062 return rc;
1067 * Actual Action Handlers
1070 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1072 MSIPACKAGE *package = (MSIPACKAGE*)param;
1073 LPCWSTR dir;
1074 LPWSTR full_path;
1075 MSIRECORD *uirow;
1076 MSIFOLDER *folder;
1078 dir = MSI_RecordGetString(row,1);
1079 if (!dir)
1081 ERR("Unable to get folder id\n");
1082 return ERROR_SUCCESS;
1085 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1086 if (!full_path)
1088 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1089 return ERROR_SUCCESS;
1092 TRACE("Folder is %s\n",debugstr_w(full_path));
1094 /* UI stuff */
1095 uirow = MSI_CreateRecord(1);
1096 MSI_RecordSetStringW(uirow,1,full_path);
1097 ui_actiondata(package,szCreateFolders,uirow);
1098 msiobj_release( &uirow->hdr );
1100 if (folder->State == 0)
1101 create_full_pathW(full_path);
1103 folder->State = 3;
1105 msi_free(full_path);
1106 return ERROR_SUCCESS;
1109 /* FIXME: probably should merge this with the above function */
1110 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1112 UINT rc = ERROR_SUCCESS;
1113 MSIFOLDER *folder;
1114 LPWSTR install_path;
1116 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1117 if (!install_path)
1118 return ERROR_FUNCTION_FAILED;
1120 /* create the path */
1121 if (folder->State == 0)
1123 create_full_pathW(install_path);
1124 folder->State = 2;
1126 msi_free(install_path);
1128 return rc;
1131 UINT msi_create_component_directories( MSIPACKAGE *package )
1133 MSICOMPONENT *comp;
1135 /* create all the folders required by the components are going to install */
1136 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1138 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1139 continue;
1140 msi_create_directory( package, comp->Directory );
1143 return ERROR_SUCCESS;
1147 * Also we cannot enable/disable components either, so for now I am just going
1148 * to do all the directories for all the components.
1150 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1152 static const WCHAR ExecSeqQuery[] =
1153 {'S','E','L','E','C','T',' ',
1154 '`','D','i','r','e','c','t','o','r','y','_','`',
1155 ' ','F','R','O','M',' ',
1156 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1157 UINT rc;
1158 MSIQUERY *view;
1160 /* create all the empty folders specified in the CreateFolder table */
1161 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1162 if (rc != ERROR_SUCCESS)
1163 return ERROR_SUCCESS;
1165 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1166 msiobj_release(&view->hdr);
1168 msi_create_component_directories( package );
1170 return rc;
1173 static UINT load_component( MSIRECORD *row, LPVOID param )
1175 MSIPACKAGE *package = param;
1176 MSICOMPONENT *comp;
1178 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1179 if (!comp)
1180 return ERROR_FUNCTION_FAILED;
1182 list_add_tail( &package->components, &comp->entry );
1184 /* fill in the data */
1185 comp->Component = msi_dup_record_field( row, 1 );
1187 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1189 comp->ComponentId = msi_dup_record_field( row, 2 );
1190 comp->Directory = msi_dup_record_field( row, 3 );
1191 comp->Attributes = MSI_RecordGetInteger(row,4);
1192 comp->Condition = msi_dup_record_field( row, 5 );
1193 comp->KeyPath = msi_dup_record_field( row, 6 );
1195 comp->Installed = INSTALLSTATE_UNKNOWN;
1196 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1198 return ERROR_SUCCESS;
1201 static UINT load_all_components( MSIPACKAGE *package )
1203 static const WCHAR query[] = {
1204 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1205 '`','C','o','m','p','o','n','e','n','t','`',0 };
1206 MSIQUERY *view;
1207 UINT r;
1209 if (!list_empty(&package->components))
1210 return ERROR_SUCCESS;
1212 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1213 if (r != ERROR_SUCCESS)
1214 return r;
1216 r = MSI_IterateRecords(view, NULL, load_component, package);
1217 msiobj_release(&view->hdr);
1218 return r;
1221 typedef struct {
1222 MSIPACKAGE *package;
1223 MSIFEATURE *feature;
1224 } _ilfs;
1226 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1228 ComponentList *cl;
1230 cl = msi_alloc( sizeof (*cl) );
1231 if ( !cl )
1232 return ERROR_NOT_ENOUGH_MEMORY;
1233 cl->component = comp;
1234 list_add_tail( &feature->Components, &cl->entry );
1236 return ERROR_SUCCESS;
1239 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1241 FeatureList *fl;
1243 fl = msi_alloc( sizeof(*fl) );
1244 if ( !fl )
1245 return ERROR_NOT_ENOUGH_MEMORY;
1246 fl->feature = child;
1247 list_add_tail( &parent->Children, &fl->entry );
1249 return ERROR_SUCCESS;
1252 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1254 _ilfs* ilfs= (_ilfs*)param;
1255 LPCWSTR component;
1256 MSICOMPONENT *comp;
1258 component = MSI_RecordGetString(row,1);
1260 /* check to see if the component is already loaded */
1261 comp = get_loaded_component( ilfs->package, component );
1262 if (!comp)
1264 ERR("unknown component %s\n", debugstr_w(component));
1265 return ERROR_FUNCTION_FAILED;
1268 add_feature_component( ilfs->feature, comp );
1269 comp->Enabled = TRUE;
1271 return ERROR_SUCCESS;
1274 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1276 MSIFEATURE *feature;
1278 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1280 if ( !lstrcmpW( feature->Feature, name ) )
1281 return feature;
1284 return NULL;
1287 static UINT load_feature(MSIRECORD * row, LPVOID param)
1289 MSIPACKAGE* package = (MSIPACKAGE*)param;
1290 MSIFEATURE* feature;
1291 static const WCHAR Query1[] =
1292 {'S','E','L','E','C','T',' ',
1293 '`','C','o','m','p','o','n','e','n','t','_','`',
1294 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1295 'C','o','m','p','o','n','e','n','t','s','`',' ',
1296 'W','H','E','R','E',' ',
1297 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1298 MSIQUERY * view;
1299 UINT rc;
1300 _ilfs ilfs;
1302 /* fill in the data */
1304 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1305 if (!feature)
1306 return ERROR_NOT_ENOUGH_MEMORY;
1308 list_init( &feature->Children );
1309 list_init( &feature->Components );
1311 feature->Feature = msi_dup_record_field( row, 1 );
1313 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1315 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1316 feature->Title = msi_dup_record_field( row, 3 );
1317 feature->Description = msi_dup_record_field( row, 4 );
1319 if (!MSI_RecordIsNull(row,5))
1320 feature->Display = MSI_RecordGetInteger(row,5);
1322 feature->Level= MSI_RecordGetInteger(row,6);
1323 feature->Directory = msi_dup_record_field( row, 7 );
1324 feature->Attributes = MSI_RecordGetInteger(row,8);
1326 feature->Installed = INSTALLSTATE_UNKNOWN;
1327 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1329 list_add_tail( &package->features, &feature->entry );
1331 /* load feature components */
1333 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1334 if (rc != ERROR_SUCCESS)
1335 return ERROR_SUCCESS;
1337 ilfs.package = package;
1338 ilfs.feature = feature;
1340 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1341 msiobj_release(&view->hdr);
1343 return ERROR_SUCCESS;
1346 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1348 MSIPACKAGE* package = (MSIPACKAGE*)param;
1349 MSIFEATURE *parent, *child;
1351 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1352 if (!child)
1353 return ERROR_FUNCTION_FAILED;
1355 if (!child->Feature_Parent)
1356 return ERROR_SUCCESS;
1358 parent = find_feature_by_name( package, child->Feature_Parent );
1359 if (!parent)
1360 return ERROR_FUNCTION_FAILED;
1362 add_feature_child( parent, child );
1363 return ERROR_SUCCESS;
1366 static UINT load_all_features( MSIPACKAGE *package )
1368 static const WCHAR query[] = {
1369 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1370 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1371 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1372 MSIQUERY *view;
1373 UINT r;
1375 if (!list_empty(&package->features))
1376 return ERROR_SUCCESS;
1378 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1379 if (r != ERROR_SUCCESS)
1380 return r;
1382 r = MSI_IterateRecords( view, NULL, load_feature, package );
1383 if (r != ERROR_SUCCESS)
1384 return r;
1386 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1387 msiobj_release( &view->hdr );
1389 return r;
1392 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1394 if (!p)
1395 return p;
1396 p = strchrW(p, ch);
1397 if (!p)
1398 return p;
1399 *p = 0;
1400 return p+1;
1403 static UINT load_file(MSIRECORD *row, LPVOID param)
1405 MSIPACKAGE* package = (MSIPACKAGE*)param;
1406 LPCWSTR component;
1407 MSIFILE *file;
1409 /* fill in the data */
1411 file = msi_alloc_zero( sizeof (MSIFILE) );
1412 if (!file)
1413 return ERROR_NOT_ENOUGH_MEMORY;
1415 file->File = msi_dup_record_field( row, 1 );
1417 component = MSI_RecordGetString( row, 2 );
1418 file->Component = get_loaded_component( package, component );
1420 if (!file->Component)
1421 ERR("Unfound Component %s\n",debugstr_w(component));
1423 file->FileName = msi_dup_record_field( row, 3 );
1424 reduce_to_longfilename( file->FileName );
1426 file->ShortName = msi_dup_record_field( row, 3 );
1427 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1429 file->FileSize = MSI_RecordGetInteger( row, 4 );
1430 file->Version = msi_dup_record_field( row, 5 );
1431 file->Language = msi_dup_record_field( row, 6 );
1432 file->Attributes = MSI_RecordGetInteger( row, 7 );
1433 file->Sequence = MSI_RecordGetInteger( row, 8 );
1435 file->state = msifs_invalid;
1437 /* if the compressed bits are not set in the file attributes,
1438 * then read the information from the package word count property
1440 if (file->Attributes & msidbFileAttributesCompressed)
1442 file->IsCompressed = TRUE;
1444 else if (file->Attributes & msidbFileAttributesNoncompressed)
1446 file->IsCompressed = FALSE;
1448 else
1450 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1453 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1455 list_add_tail( &package->files, &file->entry );
1457 return ERROR_SUCCESS;
1460 static UINT load_all_files(MSIPACKAGE *package)
1462 MSIQUERY * view;
1463 UINT rc;
1464 static const WCHAR Query[] =
1465 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1466 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1467 '`','S','e','q','u','e','n','c','e','`', 0};
1469 if (!list_empty(&package->files))
1470 return ERROR_SUCCESS;
1472 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1473 if (rc != ERROR_SUCCESS)
1474 return ERROR_SUCCESS;
1476 rc = MSI_IterateRecords(view, NULL, load_file, package);
1477 msiobj_release(&view->hdr);
1479 return ERROR_SUCCESS;
1482 static UINT load_folder( MSIRECORD *row, LPVOID param )
1484 MSIPACKAGE *package = param;
1485 static const WCHAR szDot[] = { '.',0 };
1486 static WCHAR szEmpty[] = { 0 };
1487 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1488 MSIFOLDER *folder;
1490 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1491 if (!folder)
1492 return ERROR_NOT_ENOUGH_MEMORY;
1494 folder->Directory = msi_dup_record_field( row, 1 );
1496 TRACE("%s\n", debugstr_w(folder->Directory));
1498 p = msi_dup_record_field(row, 3);
1500 /* split src and target dir */
1501 tgt_short = p;
1502 src_short = folder_split_path( p, ':' );
1504 /* split the long and short paths */
1505 tgt_long = folder_split_path( tgt_short, '|' );
1506 src_long = folder_split_path( src_short, '|' );
1508 /* check for no-op dirs */
1509 if (!lstrcmpW(szDot, tgt_short))
1510 tgt_short = szEmpty;
1511 if (!lstrcmpW(szDot, src_short))
1512 src_short = szEmpty;
1514 if (!tgt_long)
1515 tgt_long = tgt_short;
1517 if (!src_short) {
1518 src_short = tgt_short;
1519 src_long = tgt_long;
1522 if (!src_long)
1523 src_long = src_short;
1525 /* FIXME: use the target short path too */
1526 folder->TargetDefault = strdupW(tgt_long);
1527 folder->SourceShortPath = strdupW(src_short);
1528 folder->SourceLongPath = strdupW(src_long);
1529 msi_free(p);
1531 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1532 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1533 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1535 folder->Parent = msi_dup_record_field( row, 2 );
1537 folder->Property = msi_dup_property( package, folder->Directory );
1539 list_add_tail( &package->folders, &folder->entry );
1541 TRACE("returning %p\n", folder);
1543 return ERROR_SUCCESS;
1546 static UINT load_all_folders( MSIPACKAGE *package )
1548 static const WCHAR query[] = {
1549 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1550 '`','D','i','r','e','c','t','o','r','y','`',0 };
1551 MSIQUERY *view;
1552 UINT r;
1554 if (!list_empty(&package->folders))
1555 return ERROR_SUCCESS;
1557 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1558 if (r != ERROR_SUCCESS)
1559 return r;
1561 r = MSI_IterateRecords(view, NULL, load_folder, package);
1562 msiobj_release(&view->hdr);
1563 return r;
1567 * I am not doing any of the costing functionality yet.
1568 * Mostly looking at doing the Component and Feature loading
1570 * The native MSI does A LOT of modification to tables here. Mostly adding
1571 * a lot of temporary columns to the Feature and Component tables.
1573 * note: Native msi also tracks the short filename. But I am only going to
1574 * track the long ones. Also looking at this directory table
1575 * it appears that the directory table does not get the parents
1576 * resolved base on property only based on their entries in the
1577 * directory table.
1579 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1581 static const WCHAR szCosting[] =
1582 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1583 static const WCHAR szZero[] = { '0', 0 };
1585 MSI_SetPropertyW(package, szCosting, szZero);
1586 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1588 load_all_components( package );
1589 load_all_features( package );
1590 load_all_files( package );
1591 load_all_folders( package );
1593 return ERROR_SUCCESS;
1596 static UINT execute_script(MSIPACKAGE *package, UINT script )
1598 int i;
1599 UINT rc = ERROR_SUCCESS;
1601 TRACE("Executing Script %i\n",script);
1603 if (!package->script)
1605 ERR("no script!\n");
1606 return ERROR_FUNCTION_FAILED;
1609 for (i = 0; i < package->script->ActionCount[script]; i++)
1611 LPWSTR action;
1612 action = package->script->Actions[script][i];
1613 ui_actionstart(package, action);
1614 TRACE("Executing Action (%s)\n",debugstr_w(action));
1615 rc = ACTION_PerformAction(package, action, script, TRUE);
1616 if (rc != ERROR_SUCCESS)
1617 break;
1619 msi_free_action_script(package, script);
1620 return rc;
1623 static UINT ACTION_FileCost(MSIPACKAGE *package)
1625 return ERROR_SUCCESS;
1628 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1630 MSICOMPONENT *comp;
1632 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1634 INSTALLSTATE res;
1636 if (!comp->ComponentId)
1637 continue;
1639 res = MsiGetComponentPathW( package->ProductCode,
1640 comp->ComponentId, NULL, NULL);
1641 if (res < 0)
1642 res = INSTALLSTATE_ABSENT;
1643 comp->Installed = res;
1647 /* scan for and update current install states */
1648 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1650 MSICOMPONENT *comp;
1651 MSIFEATURE *feature;
1653 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1655 ComponentList *cl;
1656 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1658 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1660 comp= cl->component;
1662 if (!comp->ComponentId)
1664 res = INSTALLSTATE_ABSENT;
1665 break;
1668 if (res == INSTALLSTATE_ABSENT)
1669 res = comp->Installed;
1670 else
1672 if (res == comp->Installed)
1673 continue;
1675 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1676 res != INSTALLSTATE_SOURCE)
1678 res = INSTALLSTATE_INCOMPLETE;
1682 feature->Installed = res;
1686 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1687 INSTALLSTATE state)
1689 static const WCHAR all[]={'A','L','L',0};
1690 LPWSTR override;
1691 MSIFEATURE *feature;
1693 override = msi_dup_property( package, property );
1694 if (!override)
1695 return FALSE;
1697 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1699 if (strcmpiW(override,all)==0)
1700 msi_feature_set_state( feature, state );
1701 else
1703 LPWSTR ptr = override;
1704 LPWSTR ptr2 = strchrW(override,',');
1706 while (ptr)
1708 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1709 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1711 msi_feature_set_state( feature, state );
1712 break;
1714 if (ptr2)
1716 ptr=ptr2+1;
1717 ptr2 = strchrW(ptr,',');
1719 else
1720 break;
1724 msi_free(override);
1726 return TRUE;
1729 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1731 int install_level;
1732 static const WCHAR szlevel[] =
1733 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1734 static const WCHAR szAddLocal[] =
1735 {'A','D','D','L','O','C','A','L',0};
1736 static const WCHAR szRemove[] =
1737 {'R','E','M','O','V','E',0};
1738 static const WCHAR szReinstall[] =
1739 {'R','E','I','N','S','T','A','L','L',0};
1740 BOOL override = FALSE;
1741 MSICOMPONENT* component;
1742 MSIFEATURE *feature;
1745 /* I do not know if this is where it should happen.. but */
1747 TRACE("Checking Install Level\n");
1749 install_level = msi_get_property_int( package, szlevel, 1 );
1751 /* ok here is the _real_ rub
1752 * all these activation/deactivation things happen in order and things
1753 * later on the list override things earlier on the list.
1754 * 1) INSTALLLEVEL processing
1755 * 2) ADDLOCAL
1756 * 3) REMOVE
1757 * 4) ADDSOURCE
1758 * 5) ADDDEFAULT
1759 * 6) REINSTALL
1760 * 7) COMPADDLOCAL
1761 * 8) COMPADDSOURCE
1762 * 9) FILEADDLOCAL
1763 * 10) FILEADDSOURCE
1764 * 11) FILEADDDEFAULT
1765 * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
1766 * ignored for all the features. seems strange, especially since it is not
1767 * documented anywhere, but it is how it works.
1769 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1770 * REMOVE are the big ones, since we don't handle administrative installs
1771 * yet anyway.
1773 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1774 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1775 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1777 if (!override)
1779 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1781 BOOL feature_state = ((feature->Level > 0) &&
1782 (feature->Level <= install_level));
1784 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1786 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1787 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1788 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1789 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1790 else
1791 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1795 /* disable child features of unselected parent features */
1796 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1798 FeatureList *fl;
1800 if (feature->Level > 0 && feature->Level <= install_level)
1801 continue;
1803 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1804 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1807 else
1809 /* set the Preselected Property */
1810 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1811 static const WCHAR szOne[] = { '1', 0 };
1813 MSI_SetPropertyW(package,szPreselected,szOne);
1817 * now we want to enable or disable components base on feature
1820 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1822 ComponentList *cl;
1824 TRACE("Examining Feature %s (Installed %i, Action %i)\n",
1825 debugstr_w(feature->Feature), feature->Installed, feature->Action);
1827 /* features with components that have compressed files are made local */
1828 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1830 if (cl->component->Enabled &&
1831 cl->component->ForceLocalState &&
1832 feature->Action == INSTALLSTATE_SOURCE)
1834 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1835 break;
1839 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1841 component = cl->component;
1843 if (!component->Enabled)
1844 continue;
1846 switch (feature->Action)
1848 case INSTALLSTATE_ADVERTISED:
1849 component->hasAdvertiseFeature = 1;
1850 break;
1851 case INSTALLSTATE_SOURCE:
1852 component->hasSourceFeature = 1;
1853 break;
1854 case INSTALLSTATE_LOCAL:
1855 component->hasLocalFeature = 1;
1856 break;
1857 case INSTALLSTATE_DEFAULT:
1858 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1859 component->hasAdvertiseFeature = 1;
1860 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1861 component->hasSourceFeature = 1;
1862 else
1863 component->hasLocalFeature = 1;
1864 break;
1865 default:
1866 break;
1871 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1873 /* if the component isn't enabled, leave it alone */
1874 if (!component->Enabled)
1875 continue;
1877 /* check if it's local or source */
1878 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1879 (component->hasLocalFeature || component->hasSourceFeature))
1881 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1882 !component->ForceLocalState)
1883 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1884 else
1885 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1886 continue;
1889 /* if any feature is local, the component must be local too */
1890 if (component->hasLocalFeature)
1892 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1893 continue;
1896 if (component->hasSourceFeature)
1898 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1899 continue;
1902 if (component->hasAdvertiseFeature)
1904 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1905 continue;
1908 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1911 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1913 if (component->Action == INSTALLSTATE_DEFAULT)
1915 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1916 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1919 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1920 debugstr_w(component->Component), component->Installed, component->Action);
1924 return ERROR_SUCCESS;
1927 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1929 MSIPACKAGE *package = (MSIPACKAGE*)param;
1930 LPCWSTR name;
1931 LPWSTR path;
1932 MSIFOLDER *f;
1934 name = MSI_RecordGetString(row,1);
1936 f = get_loaded_folder(package, name);
1937 if (!f) return ERROR_SUCCESS;
1939 /* reset the ResolvedTarget */
1940 msi_free(f->ResolvedTarget);
1941 f->ResolvedTarget = NULL;
1943 /* This helper function now does ALL the work */
1944 TRACE("Dir %s ...\n",debugstr_w(name));
1945 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1946 TRACE("resolves to %s\n",debugstr_w(path));
1947 msi_free(path);
1949 return ERROR_SUCCESS;
1952 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1954 MSIPACKAGE *package = (MSIPACKAGE*)param;
1955 LPCWSTR name;
1956 MSIFEATURE *feature;
1958 name = MSI_RecordGetString( row, 1 );
1960 feature = get_loaded_feature( package, name );
1961 if (!feature)
1962 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1963 else
1965 LPCWSTR Condition;
1966 Condition = MSI_RecordGetString(row,3);
1968 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1970 int level = MSI_RecordGetInteger(row,2);
1971 TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
1972 feature->Level = level;
1975 return ERROR_SUCCESS;
1978 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1980 static const WCHAR name_fmt[] =
1981 {'%','u','.','%','u','.','%','u','.','%','u',0};
1982 static WCHAR name[] = {'\\',0};
1983 VS_FIXEDFILEINFO *lpVer;
1984 WCHAR filever[0x100];
1985 LPVOID version;
1986 DWORD versize;
1987 DWORD handle;
1988 UINT sz;
1990 TRACE("%s\n", debugstr_w(filename));
1992 versize = GetFileVersionInfoSizeW( filename, &handle );
1993 if (!versize)
1994 return NULL;
1996 version = msi_alloc( versize );
1997 GetFileVersionInfoW( filename, 0, versize, version );
1999 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2001 msi_free( version );
2002 return NULL;
2005 sprintfW( filever, name_fmt,
2006 HIWORD(lpVer->dwFileVersionMS),
2007 LOWORD(lpVer->dwFileVersionMS),
2008 HIWORD(lpVer->dwFileVersionLS),
2009 LOWORD(lpVer->dwFileVersionLS));
2011 msi_free( version );
2013 return strdupW( filever );
2016 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2018 LPWSTR file_version;
2019 MSIFILE *file;
2021 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2023 MSICOMPONENT* comp = file->Component;
2024 LPWSTR p;
2026 if (!comp)
2027 continue;
2029 if (file->IsCompressed)
2030 comp->ForceLocalState = TRUE;
2032 /* calculate target */
2033 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2035 msi_free(file->TargetPath);
2037 TRACE("file %s is named %s\n",
2038 debugstr_w(file->File), debugstr_w(file->FileName));
2040 file->TargetPath = build_directory_name(2, p, file->FileName);
2042 msi_free(p);
2044 TRACE("file %s resolves to %s\n",
2045 debugstr_w(file->File), debugstr_w(file->TargetPath));
2047 /* don't check files of components that aren't installed */
2048 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2049 comp->Installed == INSTALLSTATE_ABSENT)
2051 file->state = msifs_missing; /* assume files are missing */
2052 continue;
2055 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2057 file->state = msifs_missing;
2058 comp->Cost += file->FileSize;
2059 comp->Installed = INSTALLSTATE_INCOMPLETE;
2060 continue;
2063 if (file->Version &&
2064 (file_version = msi_get_disk_file_version( file->TargetPath )))
2066 TRACE("new %s old %s\n", debugstr_w(file->Version),
2067 debugstr_w(file_version));
2068 /* FIXME: seems like a bad way to compare version numbers */
2069 if (lstrcmpiW(file_version, file->Version)<0)
2071 file->state = msifs_overwrite;
2072 comp->Cost += file->FileSize;
2073 comp->Installed = INSTALLSTATE_INCOMPLETE;
2075 else
2076 file->state = msifs_present;
2077 msi_free( file_version );
2079 else
2080 file->state = msifs_present;
2083 return ERROR_SUCCESS;
2087 * A lot is done in this function aside from just the costing.
2088 * The costing needs to be implemented at some point but for now I am going
2089 * to focus on the directory building
2092 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2094 static const WCHAR ExecSeqQuery[] =
2095 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2096 '`','D','i','r','e','c','t','o','r','y','`',0};
2097 static const WCHAR ConditionQuery[] =
2098 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2099 '`','C','o','n','d','i','t','i','o','n','`',0};
2100 static const WCHAR szCosting[] =
2101 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2102 static const WCHAR szlevel[] =
2103 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2104 static const WCHAR szOne[] = { '1', 0 };
2105 MSICOMPONENT *comp;
2106 UINT rc;
2107 MSIQUERY * view;
2108 LPWSTR level;
2110 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2111 return ERROR_SUCCESS;
2113 TRACE("Building Directory properties\n");
2115 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2116 if (rc == ERROR_SUCCESS)
2118 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2119 package);
2120 msiobj_release(&view->hdr);
2123 /* read components states from the registry */
2124 ACTION_GetComponentInstallStates(package);
2126 TRACE("File calculations\n");
2127 msi_check_file_install_states( package );
2129 TRACE("Evaluating Condition Table\n");
2131 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2132 if (rc == ERROR_SUCCESS)
2134 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2135 package);
2136 msiobj_release(&view->hdr);
2139 TRACE("Enabling or Disabling Components\n");
2140 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2142 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2144 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2145 comp->Enabled = FALSE;
2149 MSI_SetPropertyW(package,szCosting,szOne);
2150 /* set default run level if not set */
2151 level = msi_dup_property( package, szlevel );
2152 if (!level)
2153 MSI_SetPropertyW(package,szlevel, szOne);
2154 msi_free(level);
2156 ACTION_UpdateFeatureInstallStates(package);
2158 return MSI_SetFeatureStates(package);
2161 /* OK this value is "interpreted" and then formatted based on the
2162 first few characters */
2163 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2164 DWORD *size)
2166 LPSTR data = NULL;
2167 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2169 if (value[1]=='x')
2171 LPWSTR ptr;
2172 CHAR byte[5];
2173 LPWSTR deformated = NULL;
2174 int count;
2176 deformat_string(package, &value[2], &deformated);
2178 /* binary value type */
2179 ptr = deformated;
2180 *type = REG_BINARY;
2181 if (strlenW(ptr)%2)
2182 *size = (strlenW(ptr)/2)+1;
2183 else
2184 *size = strlenW(ptr)/2;
2186 data = msi_alloc(*size);
2188 byte[0] = '0';
2189 byte[1] = 'x';
2190 byte[4] = 0;
2191 count = 0;
2192 /* if uneven pad with a zero in front */
2193 if (strlenW(ptr)%2)
2195 byte[2]= '0';
2196 byte[3]= *ptr;
2197 ptr++;
2198 data[count] = (BYTE)strtol(byte,NULL,0);
2199 count ++;
2200 TRACE("Uneven byte count\n");
2202 while (*ptr)
2204 byte[2]= *ptr;
2205 ptr++;
2206 byte[3]= *ptr;
2207 ptr++;
2208 data[count] = (BYTE)strtol(byte,NULL,0);
2209 count ++;
2211 msi_free(deformated);
2213 TRACE("Data %i bytes(%i)\n",*size,count);
2215 else
2217 LPWSTR deformated;
2218 LPWSTR p;
2219 DWORD d = 0;
2220 deformat_string(package, &value[1], &deformated);
2222 *type=REG_DWORD;
2223 *size = sizeof(DWORD);
2224 data = msi_alloc(*size);
2225 p = deformated;
2226 if (*p == '-')
2227 p++;
2228 while (*p)
2230 if ( (*p < '0') || (*p > '9') )
2231 break;
2232 d *= 10;
2233 d += (*p - '0');
2234 p++;
2236 if (deformated[0] == '-')
2237 d = -d;
2238 *(LPDWORD)data = d;
2239 TRACE("DWORD %i\n",*(LPDWORD)data);
2241 msi_free(deformated);
2244 else
2246 static const WCHAR szMulti[] = {'[','~',']',0};
2247 LPCWSTR ptr;
2248 *type=REG_SZ;
2250 if (value[0]=='#')
2252 if (value[1]=='%')
2254 ptr = &value[2];
2255 *type=REG_EXPAND_SZ;
2257 else
2258 ptr = &value[1];
2260 else
2261 ptr=value;
2263 if (strstrW(value,szMulti))
2264 *type = REG_MULTI_SZ;
2266 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2268 return data;
2271 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2273 MSIPACKAGE *package = (MSIPACKAGE*)param;
2274 static const WCHAR szHCR[] =
2275 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2276 'R','O','O','T','\\',0};
2277 static const WCHAR szHCU[] =
2278 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2279 'U','S','E','R','\\',0};
2280 static const WCHAR szHLM[] =
2281 {'H','K','E','Y','_','L','O','C','A','L','_',
2282 'M','A','C','H','I','N','E','\\',0};
2283 static const WCHAR szHU[] =
2284 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2286 LPSTR value_data = NULL;
2287 HKEY root_key, hkey;
2288 DWORD type,size;
2289 LPWSTR deformated;
2290 LPCWSTR szRoot, component, name, key, value;
2291 MSICOMPONENT *comp;
2292 MSIRECORD * uirow;
2293 LPWSTR uikey;
2294 INT root;
2295 BOOL check_first = FALSE;
2296 UINT rc;
2298 ui_progress(package,2,0,0,0);
2300 value = NULL;
2301 key = NULL;
2302 uikey = NULL;
2303 name = NULL;
2305 component = MSI_RecordGetString(row, 6);
2306 comp = get_loaded_component(package,component);
2307 if (!comp)
2308 return ERROR_SUCCESS;
2310 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2312 TRACE("Skipping write due to disabled component %s\n",
2313 debugstr_w(component));
2315 comp->Action = comp->Installed;
2317 return ERROR_SUCCESS;
2320 comp->Action = INSTALLSTATE_LOCAL;
2322 name = MSI_RecordGetString(row, 4);
2323 if( MSI_RecordIsNull(row,5) && name )
2325 /* null values can have special meanings */
2326 if (name[0]=='-' && name[1] == 0)
2327 return ERROR_SUCCESS;
2328 else if ((name[0]=='+' && name[1] == 0) ||
2329 (name[0] == '*' && name[1] == 0))
2330 name = NULL;
2331 check_first = TRUE;
2334 root = MSI_RecordGetInteger(row,2);
2335 key = MSI_RecordGetString(row, 3);
2337 /* get the root key */
2338 switch (root)
2340 case -1:
2342 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2343 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2344 if (all_users && all_users[0] == '1')
2346 root_key = HKEY_LOCAL_MACHINE;
2347 szRoot = szHLM;
2349 else
2351 root_key = HKEY_CURRENT_USER;
2352 szRoot = szHCU;
2354 msi_free(all_users);
2356 break;
2357 case 0: root_key = HKEY_CLASSES_ROOT;
2358 szRoot = szHCR;
2359 break;
2360 case 1: root_key = HKEY_CURRENT_USER;
2361 szRoot = szHCU;
2362 break;
2363 case 2: root_key = HKEY_LOCAL_MACHINE;
2364 szRoot = szHLM;
2365 break;
2366 case 3: root_key = HKEY_USERS;
2367 szRoot = szHU;
2368 break;
2369 default:
2370 ERR("Unknown root %i\n",root);
2371 root_key=NULL;
2372 szRoot = NULL;
2373 break;
2375 if (!root_key)
2376 return ERROR_SUCCESS;
2378 deformat_string(package, key , &deformated);
2379 size = strlenW(deformated) + strlenW(szRoot) + 1;
2380 uikey = msi_alloc(size*sizeof(WCHAR));
2381 strcpyW(uikey,szRoot);
2382 strcatW(uikey,deformated);
2384 if (RegCreateKeyW( root_key, deformated, &hkey))
2386 ERR("Could not create key %s\n",debugstr_w(deformated));
2387 msi_free(deformated);
2388 msi_free(uikey);
2389 return ERROR_SUCCESS;
2391 msi_free(deformated);
2393 value = MSI_RecordGetString(row,5);
2394 if (value)
2395 value_data = parse_value(package, value, &type, &size);
2396 else
2398 static const WCHAR szEmpty[] = {0};
2399 value_data = (LPSTR)strdupW(szEmpty);
2400 size = 0;
2401 type = REG_SZ;
2404 deformat_string(package, name, &deformated);
2406 /* get the double nulls to terminate SZ_MULTI */
2407 if (type == REG_MULTI_SZ)
2408 size +=sizeof(WCHAR);
2410 if (!check_first)
2412 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2413 debugstr_w(uikey));
2414 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2416 else
2418 DWORD sz = 0;
2419 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2420 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2422 TRACE("value %s of %s checked already exists\n",
2423 debugstr_w(deformated), debugstr_w(uikey));
2425 else
2427 TRACE("Checked and setting value %s of %s\n",
2428 debugstr_w(deformated), debugstr_w(uikey));
2429 if (deformated || size)
2430 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2433 RegCloseKey(hkey);
2435 uirow = MSI_CreateRecord(3);
2436 MSI_RecordSetStringW(uirow,2,deformated);
2437 MSI_RecordSetStringW(uirow,1,uikey);
2439 if (type == REG_SZ)
2440 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2441 else
2442 MSI_RecordSetStringW(uirow,3,value);
2444 ui_actiondata(package,szWriteRegistryValues,uirow);
2445 msiobj_release( &uirow->hdr );
2447 msi_free(value_data);
2448 msi_free(deformated);
2449 msi_free(uikey);
2451 return ERROR_SUCCESS;
2454 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2456 UINT rc;
2457 MSIQUERY * view;
2458 static const WCHAR ExecSeqQuery[] =
2459 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2460 '`','R','e','g','i','s','t','r','y','`',0 };
2462 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2463 if (rc != ERROR_SUCCESS)
2464 return ERROR_SUCCESS;
2466 /* increment progress bar each time action data is sent */
2467 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2469 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2471 msiobj_release(&view->hdr);
2472 return rc;
2475 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2477 package->script->CurrentlyScripting = TRUE;
2479 return ERROR_SUCCESS;
2483 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2485 MSICOMPONENT *comp;
2486 DWORD progress = 0;
2487 DWORD total = 0;
2488 static const WCHAR q1[]=
2489 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2490 '`','R','e','g','i','s','t','r','y','`',0};
2491 UINT rc;
2492 MSIQUERY * view;
2493 MSIFEATURE *feature;
2494 MSIFILE *file;
2496 TRACE("InstallValidate\n");
2498 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2499 if (rc == ERROR_SUCCESS)
2501 MSI_IterateRecords( view, &progress, NULL, package );
2502 msiobj_release( &view->hdr );
2503 total += progress * REG_PROGRESS_VALUE;
2506 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2507 total += COMPONENT_PROGRESS_VALUE;
2509 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2510 total += file->FileSize;
2512 ui_progress(package,0,total,0,0);
2514 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2516 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2517 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2518 feature->ActionRequest);
2521 return ERROR_SUCCESS;
2524 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2526 MSIPACKAGE* package = (MSIPACKAGE*)param;
2527 LPCWSTR cond = NULL;
2528 LPCWSTR message = NULL;
2529 UINT r;
2531 static const WCHAR title[]=
2532 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2534 cond = MSI_RecordGetString(row,1);
2536 r = MSI_EvaluateConditionW(package,cond);
2537 if (r == MSICONDITION_FALSE)
2539 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2541 LPWSTR deformated;
2542 message = MSI_RecordGetString(row,2);
2543 deformat_string(package,message,&deformated);
2544 MessageBoxW(NULL,deformated,title,MB_OK);
2545 msi_free(deformated);
2548 return ERROR_INSTALL_FAILURE;
2551 return ERROR_SUCCESS;
2554 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2556 UINT rc;
2557 MSIQUERY * view = NULL;
2558 static const WCHAR ExecSeqQuery[] =
2559 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2560 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2562 TRACE("Checking launch conditions\n");
2564 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2565 if (rc != ERROR_SUCCESS)
2566 return ERROR_SUCCESS;
2568 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2569 msiobj_release(&view->hdr);
2571 return rc;
2574 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2577 if (!cmp->KeyPath)
2578 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2580 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2582 MSIRECORD * row = 0;
2583 UINT root,len;
2584 LPWSTR deformated,buffer,deformated_name;
2585 LPCWSTR key,name;
2586 static const WCHAR ExecSeqQuery[] =
2587 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2588 '`','R','e','g','i','s','t','r','y','`',' ',
2589 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2590 ' ','=',' ' ,'\'','%','s','\'',0 };
2591 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2592 static const WCHAR fmt2[]=
2593 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2595 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2596 if (!row)
2597 return NULL;
2599 root = MSI_RecordGetInteger(row,2);
2600 key = MSI_RecordGetString(row, 3);
2601 name = MSI_RecordGetString(row, 4);
2602 deformat_string(package, key , &deformated);
2603 deformat_string(package, name, &deformated_name);
2605 len = strlenW(deformated) + 6;
2606 if (deformated_name)
2607 len+=strlenW(deformated_name);
2609 buffer = msi_alloc( len *sizeof(WCHAR));
2611 if (deformated_name)
2612 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2613 else
2614 sprintfW(buffer,fmt,root,deformated);
2616 msi_free(deformated);
2617 msi_free(deformated_name);
2618 msiobj_release(&row->hdr);
2620 return buffer;
2622 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2624 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2625 return NULL;
2627 else
2629 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2631 if (file)
2632 return strdupW( file->TargetPath );
2634 return NULL;
2637 static HKEY openSharedDLLsKey(void)
2639 HKEY hkey=0;
2640 static const WCHAR path[] =
2641 {'S','o','f','t','w','a','r','e','\\',
2642 'M','i','c','r','o','s','o','f','t','\\',
2643 'W','i','n','d','o','w','s','\\',
2644 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2645 'S','h','a','r','e','d','D','L','L','s',0};
2647 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2648 return hkey;
2651 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2653 HKEY hkey;
2654 DWORD count=0;
2655 DWORD type;
2656 DWORD sz = sizeof(count);
2657 DWORD rc;
2659 hkey = openSharedDLLsKey();
2660 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2661 if (rc != ERROR_SUCCESS)
2662 count = 0;
2663 RegCloseKey(hkey);
2664 return count;
2667 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2669 HKEY hkey;
2671 hkey = openSharedDLLsKey();
2672 if (count > 0)
2673 msi_reg_set_val_dword( hkey, path, count );
2674 else
2675 RegDeleteValueW(hkey,path);
2676 RegCloseKey(hkey);
2677 return count;
2681 * Return TRUE if the count should be written out and FALSE if not
2683 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2685 MSIFEATURE *feature;
2686 INT count = 0;
2687 BOOL write = FALSE;
2689 /* only refcount DLLs */
2690 if (comp->KeyPath == NULL ||
2691 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2692 comp->Attributes & msidbComponentAttributesODBCDataSource)
2693 write = FALSE;
2694 else
2696 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2697 write = (count > 0);
2699 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2700 write = TRUE;
2703 /* increment counts */
2704 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2706 ComponentList *cl;
2708 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2709 continue;
2711 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2713 if ( cl->component == comp )
2714 count++;
2718 /* decrement counts */
2719 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2721 ComponentList *cl;
2723 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2724 continue;
2726 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2728 if ( cl->component == comp )
2729 count--;
2733 /* ref count all the files in the component */
2734 if (write)
2736 MSIFILE *file;
2738 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2740 if (file->Component == comp)
2741 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2745 /* add a count for permenent */
2746 if (comp->Attributes & msidbComponentAttributesPermanent)
2747 count ++;
2749 comp->RefCount = count;
2751 if (write)
2752 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2756 * Ok further analysis makes me think that this work is
2757 * actually done in the PublishComponents and PublishFeatures
2758 * step, and not here. It appears like the keypath and all that is
2759 * resolved in this step, however actually written in the Publish steps.
2760 * But we will leave it here for now because it is unclear
2762 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2764 WCHAR squished_pc[GUID_SIZE];
2765 WCHAR squished_cc[GUID_SIZE];
2766 UINT rc;
2767 MSICOMPONENT *comp;
2768 HKEY hkey=0,hkey2=0;
2770 /* writes the Component and Features values to the registry */
2772 rc = MSIREG_OpenComponents(&hkey);
2773 if (rc != ERROR_SUCCESS)
2774 return rc;
2776 squash_guid(package->ProductCode,squished_pc);
2777 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2779 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2781 MSIRECORD * uirow;
2783 ui_progress(package,2,0,0,0);
2784 if (!comp->ComponentId)
2785 continue;
2787 squash_guid(comp->ComponentId,squished_cc);
2789 msi_free(comp->FullKeypath);
2790 comp->FullKeypath = resolve_keypath( package, comp );
2792 /* do the refcounting */
2793 ACTION_RefCountComponent( package, comp );
2795 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2796 debugstr_w(comp->Component),
2797 debugstr_w(squished_cc),
2798 debugstr_w(comp->FullKeypath),
2799 comp->RefCount);
2801 * Write the keypath out if the component is to be registered
2802 * and delete the key if the component is to be deregistered
2804 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2806 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2807 if (rc != ERROR_SUCCESS)
2808 continue;
2810 if (!comp->FullKeypath)
2811 continue;
2813 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2815 if (comp->Attributes & msidbComponentAttributesPermanent)
2817 static const WCHAR szPermKey[] =
2818 { '0','0','0','0','0','0','0','0','0','0','0','0',
2819 '0','0','0','0','0','0','0','0','0','0','0','0',
2820 '0','0','0','0','0','0','0','0',0 };
2822 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2825 RegCloseKey(hkey2);
2827 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2829 DWORD res;
2831 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2832 if (rc != ERROR_SUCCESS)
2833 continue;
2835 RegDeleteValueW(hkey2,squished_pc);
2837 /* if the key is empty delete it */
2838 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2839 RegCloseKey(hkey2);
2840 if (res == ERROR_NO_MORE_ITEMS)
2841 RegDeleteKeyW(hkey,squished_cc);
2845 /* UI stuff */
2846 uirow = MSI_CreateRecord(3);
2847 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2848 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2849 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2850 ui_actiondata(package,szProcessComponents,uirow);
2851 msiobj_release( &uirow->hdr );
2853 RegCloseKey(hkey);
2854 return rc;
2857 typedef struct {
2858 CLSID clsid;
2859 LPWSTR source;
2861 LPWSTR path;
2862 ITypeLib *ptLib;
2863 } typelib_struct;
2865 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2866 LPWSTR lpszName, LONG_PTR lParam)
2868 TLIBATTR *attr;
2869 typelib_struct *tl_struct = (typelib_struct*) lParam;
2870 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2871 int sz;
2872 HRESULT res;
2874 if (!IS_INTRESOURCE(lpszName))
2876 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2877 return TRUE;
2880 sz = strlenW(tl_struct->source)+4;
2881 sz *= sizeof(WCHAR);
2883 if ((INT_PTR)lpszName == 1)
2884 tl_struct->path = strdupW(tl_struct->source);
2885 else
2887 tl_struct->path = msi_alloc(sz);
2888 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2891 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2892 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2893 if (!SUCCEEDED(res))
2895 msi_free(tl_struct->path);
2896 tl_struct->path = NULL;
2898 return TRUE;
2901 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2902 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2904 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2905 return FALSE;
2908 msi_free(tl_struct->path);
2909 tl_struct->path = NULL;
2911 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2912 ITypeLib_Release(tl_struct->ptLib);
2914 return TRUE;
2917 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2919 MSIPACKAGE* package = (MSIPACKAGE*)param;
2920 LPCWSTR component;
2921 MSICOMPONENT *comp;
2922 MSIFILE *file;
2923 typelib_struct tl_struct;
2924 HMODULE module;
2925 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2927 component = MSI_RecordGetString(row,3);
2928 comp = get_loaded_component(package,component);
2929 if (!comp)
2930 return ERROR_SUCCESS;
2932 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2934 TRACE("Skipping typelib reg due to disabled component\n");
2936 comp->Action = comp->Installed;
2938 return ERROR_SUCCESS;
2941 comp->Action = INSTALLSTATE_LOCAL;
2943 file = get_loaded_file( package, comp->KeyPath );
2944 if (!file)
2945 return ERROR_SUCCESS;
2947 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2948 if (module)
2950 LPCWSTR guid;
2951 guid = MSI_RecordGetString(row,1);
2952 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2953 tl_struct.source = strdupW( file->TargetPath );
2954 tl_struct.path = NULL;
2956 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2957 (LONG_PTR)&tl_struct);
2959 if (tl_struct.path)
2961 LPWSTR help = NULL;
2962 LPCWSTR helpid;
2963 HRESULT res;
2965 helpid = MSI_RecordGetString(row,6);
2967 if (helpid)
2968 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2969 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2970 msi_free(help);
2972 if (!SUCCEEDED(res))
2973 ERR("Failed to register type library %s\n",
2974 debugstr_w(tl_struct.path));
2975 else
2977 ui_actiondata(package,szRegisterTypeLibraries,row);
2979 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2982 ITypeLib_Release(tl_struct.ptLib);
2983 msi_free(tl_struct.path);
2985 else
2986 ERR("Failed to load type library %s\n",
2987 debugstr_w(tl_struct.source));
2989 FreeLibrary(module);
2990 msi_free(tl_struct.source);
2992 else
2993 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
2995 return ERROR_SUCCESS;
2998 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3001 * OK this is a bit confusing.. I am given a _Component key and I believe
3002 * that the file that is being registered as a type library is the "key file
3003 * of that component" which I interpret to mean "The file in the KeyPath of
3004 * that component".
3006 UINT rc;
3007 MSIQUERY * view;
3008 static const WCHAR Query[] =
3009 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3010 '`','T','y','p','e','L','i','b','`',0};
3012 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3013 if (rc != ERROR_SUCCESS)
3014 return ERROR_SUCCESS;
3016 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3017 msiobj_release(&view->hdr);
3018 return rc;
3021 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3023 MSIPACKAGE *package = (MSIPACKAGE*)param;
3024 LPWSTR target_file, target_folder, filename;
3025 LPCWSTR buffer, extension;
3026 MSICOMPONENT *comp;
3027 static const WCHAR szlnk[]={'.','l','n','k',0};
3028 IShellLinkW *sl = NULL;
3029 IPersistFile *pf = NULL;
3030 HRESULT res;
3032 buffer = MSI_RecordGetString(row,4);
3033 comp = get_loaded_component(package,buffer);
3034 if (!comp)
3035 return ERROR_SUCCESS;
3037 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3039 TRACE("Skipping shortcut creation due to disabled component\n");
3041 comp->Action = comp->Installed;
3043 return ERROR_SUCCESS;
3046 comp->Action = INSTALLSTATE_LOCAL;
3048 ui_actiondata(package,szCreateShortcuts,row);
3050 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3051 &IID_IShellLinkW, (LPVOID *) &sl );
3053 if (FAILED( res ))
3055 ERR("CLSID_ShellLink not available\n");
3056 goto err;
3059 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3060 if (FAILED( res ))
3062 ERR("QueryInterface(IID_IPersistFile) failed\n");
3063 goto err;
3066 buffer = MSI_RecordGetString(row,2);
3067 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3069 /* may be needed because of a bug somehwere else */
3070 create_full_pathW(target_folder);
3072 filename = msi_dup_record_field( row, 3 );
3073 reduce_to_longfilename(filename);
3075 extension = strchrW(filename,'.');
3076 if (!extension || strcmpiW(extension,szlnk))
3078 int len = strlenW(filename);
3079 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3080 memcpy(filename + len, szlnk, sizeof(szlnk));
3082 target_file = build_directory_name(2, target_folder, filename);
3083 msi_free(target_folder);
3084 msi_free(filename);
3086 buffer = MSI_RecordGetString(row,5);
3087 if (strchrW(buffer,'['))
3089 LPWSTR deformated;
3090 deformat_string(package,buffer,&deformated);
3091 IShellLinkW_SetPath(sl,deformated);
3092 msi_free(deformated);
3094 else
3096 FIXME("poorly handled shortcut format, advertised shortcut\n");
3097 IShellLinkW_SetPath(sl,comp->FullKeypath);
3100 if (!MSI_RecordIsNull(row,6))
3102 LPWSTR deformated;
3103 buffer = MSI_RecordGetString(row,6);
3104 deformat_string(package,buffer,&deformated);
3105 IShellLinkW_SetArguments(sl,deformated);
3106 msi_free(deformated);
3109 if (!MSI_RecordIsNull(row,7))
3111 buffer = MSI_RecordGetString(row,7);
3112 IShellLinkW_SetDescription(sl,buffer);
3115 if (!MSI_RecordIsNull(row,8))
3116 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3118 if (!MSI_RecordIsNull(row,9))
3120 LPWSTR Path;
3121 INT index;
3123 buffer = MSI_RecordGetString(row,9);
3125 Path = build_icon_path(package,buffer);
3126 index = MSI_RecordGetInteger(row,10);
3128 /* no value means 0 */
3129 if (index == MSI_NULL_INTEGER)
3130 index = 0;
3132 IShellLinkW_SetIconLocation(sl,Path,index);
3133 msi_free(Path);
3136 if (!MSI_RecordIsNull(row,11))
3137 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3139 if (!MSI_RecordIsNull(row,12))
3141 LPWSTR Path;
3142 buffer = MSI_RecordGetString(row,12);
3143 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3144 if (Path)
3145 IShellLinkW_SetWorkingDirectory(sl,Path);
3146 msi_free(Path);
3149 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3150 IPersistFile_Save(pf,target_file,FALSE);
3152 msi_free(target_file);
3154 err:
3155 if (pf)
3156 IPersistFile_Release( pf );
3157 if (sl)
3158 IShellLinkW_Release( sl );
3160 return ERROR_SUCCESS;
3163 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3165 UINT rc;
3166 HRESULT res;
3167 MSIQUERY * view;
3168 static const WCHAR Query[] =
3169 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3170 '`','S','h','o','r','t','c','u','t','`',0};
3172 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3173 if (rc != ERROR_SUCCESS)
3174 return ERROR_SUCCESS;
3176 res = CoInitialize( NULL );
3177 if (FAILED (res))
3179 ERR("CoInitialize failed\n");
3180 return ERROR_FUNCTION_FAILED;
3183 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3184 msiobj_release(&view->hdr);
3186 CoUninitialize();
3188 return rc;
3191 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3193 MSIPACKAGE* package = (MSIPACKAGE*)param;
3194 HANDLE the_file;
3195 LPWSTR FilePath;
3196 LPCWSTR FileName;
3197 CHAR buffer[1024];
3198 DWORD sz;
3199 UINT rc;
3200 MSIRECORD *uirow;
3202 FileName = MSI_RecordGetString(row,1);
3203 if (!FileName)
3205 ERR("Unable to get FileName\n");
3206 return ERROR_SUCCESS;
3209 FilePath = build_icon_path(package,FileName);
3211 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3213 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3214 FILE_ATTRIBUTE_NORMAL, NULL);
3216 if (the_file == INVALID_HANDLE_VALUE)
3218 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3219 msi_free(FilePath);
3220 return ERROR_SUCCESS;
3225 DWORD write;
3226 sz = 1024;
3227 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3228 if (rc != ERROR_SUCCESS)
3230 ERR("Failed to get stream\n");
3231 CloseHandle(the_file);
3232 DeleteFileW(FilePath);
3233 break;
3235 WriteFile(the_file,buffer,sz,&write,NULL);
3236 } while (sz == 1024);
3238 msi_free(FilePath);
3240 CloseHandle(the_file);
3242 uirow = MSI_CreateRecord(1);
3243 MSI_RecordSetStringW(uirow,1,FileName);
3244 ui_actiondata(package,szPublishProduct,uirow);
3245 msiobj_release( &uirow->hdr );
3247 return ERROR_SUCCESS;
3250 static BOOL msi_check_publish(MSIPACKAGE *package)
3252 MSIFEATURE *feature;
3254 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3256 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3257 return TRUE;
3260 return FALSE;
3264 * 99% of the work done here is only done for
3265 * advertised installs. However this is where the
3266 * Icon table is processed and written out
3267 * so that is what I am going to do here.
3269 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3271 UINT rc;
3272 MSIQUERY * view;
3273 MSISOURCELISTINFO *info;
3274 MSIMEDIADISK *disk;
3275 static const WCHAR Query[]=
3276 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3277 '`','I','c','o','n','`',0};
3278 /* for registry stuff */
3279 HKEY hkey=0;
3280 HKEY hukey=0;
3281 HKEY hudkey=0, props=0;
3282 static const WCHAR szProductLanguage[] =
3283 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3284 static const WCHAR szARPProductIcon[] =
3285 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3286 static const WCHAR szProductVersion[] =
3287 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3288 DWORD langid;
3289 LPWSTR buffer;
3290 DWORD size;
3291 MSIHANDLE hDb, hSumInfo;
3293 /* FIXME: also need to publish if the product is in advertise mode */
3294 if (!msi_check_publish(package))
3295 return ERROR_SUCCESS;
3297 /* write out icon files */
3299 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3300 if (rc == ERROR_SUCCESS)
3302 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3303 msiobj_release(&view->hdr);
3306 /* ok there is a lot more done here but i need to figure out what */
3308 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3309 if (rc != ERROR_SUCCESS)
3310 goto end;
3312 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3313 if (rc != ERROR_SUCCESS)
3314 goto end;
3316 rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3317 if (rc != ERROR_SUCCESS)
3318 goto end;
3320 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode,&props,TRUE);
3321 if (rc != ERROR_SUCCESS)
3322 goto end;
3324 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3325 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3326 msi_free(buffer);
3328 langid = msi_get_property_int( package, szProductLanguage, 0 );
3329 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3331 buffer = msi_dup_property( package, szARPProductIcon );
3332 if (buffer)
3334 LPWSTR path = build_icon_path(package,buffer);
3335 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3336 msi_free( path );
3338 msi_free(buffer);
3340 buffer = msi_dup_property( package, szProductVersion );
3341 if (buffer)
3343 DWORD verdword = msi_version_str_to_dword(buffer);
3344 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3346 msi_free(buffer);
3348 /* FIXME: Need to write more keys to the user registry */
3350 hDb= alloc_msihandle( &package->db->hdr );
3351 if (!hDb) {
3352 rc = ERROR_NOT_ENOUGH_MEMORY;
3353 goto end;
3355 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3356 MsiCloseHandle(hDb);
3357 if (rc == ERROR_SUCCESS)
3359 WCHAR guidbuffer[0x200];
3360 size = 0x200;
3361 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3362 guidbuffer, &size);
3363 if (rc == ERROR_SUCCESS)
3365 WCHAR squashed[GUID_SIZE];
3366 /* for now we only care about the first guid */
3367 LPWSTR ptr = strchrW(guidbuffer,';');
3368 if (ptr) *ptr = 0;
3369 squash_guid(guidbuffer,squashed);
3370 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
3372 else
3374 ERR("Unable to query Revision_Number...\n");
3375 rc = ERROR_SUCCESS;
3377 MsiCloseHandle(hSumInfo);
3379 else
3381 ERR("Unable to open Summary Information\n");
3382 rc = ERROR_SUCCESS;
3385 /* publish the SourceList info */
3386 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3388 MsiSourceListSetInfoW(package->ProductCode, NULL,
3389 info->context, info->options,
3390 info->property, info->value);
3393 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3395 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3396 disk->context, disk->options,
3397 disk->disk_id, disk->volume_label, disk->disk_prompt);
3400 end:
3401 RegCloseKey(hkey);
3402 RegCloseKey(hukey);
3403 RegCloseKey(hudkey);
3404 RegCloseKey(props);
3406 return rc;
3409 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3411 MSIPACKAGE *package = (MSIPACKAGE*)param;
3412 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3413 LPWSTR deformated_section, deformated_key, deformated_value;
3414 LPWSTR folder, fullname = NULL;
3415 MSIRECORD * uirow;
3416 INT action;
3417 MSICOMPONENT *comp;
3418 static const WCHAR szWindowsFolder[] =
3419 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3421 component = MSI_RecordGetString(row, 8);
3422 comp = get_loaded_component(package,component);
3424 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3426 TRACE("Skipping ini file due to disabled component %s\n",
3427 debugstr_w(component));
3429 comp->Action = comp->Installed;
3431 return ERROR_SUCCESS;
3434 comp->Action = INSTALLSTATE_LOCAL;
3436 identifier = MSI_RecordGetString(row,1);
3437 filename = MSI_RecordGetString(row,2);
3438 dirproperty = MSI_RecordGetString(row,3);
3439 section = MSI_RecordGetString(row,4);
3440 key = MSI_RecordGetString(row,5);
3441 value = MSI_RecordGetString(row,6);
3442 action = MSI_RecordGetInteger(row,7);
3444 deformat_string(package,section,&deformated_section);
3445 deformat_string(package,key,&deformated_key);
3446 deformat_string(package,value,&deformated_value);
3448 if (dirproperty)
3450 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3451 if (!folder)
3452 folder = msi_dup_property( package, dirproperty );
3454 else
3455 folder = msi_dup_property( package, szWindowsFolder );
3457 if (!folder)
3459 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3460 goto cleanup;
3463 fullname = build_directory_name(2, folder, filename);
3465 if (action == 0)
3467 TRACE("Adding value %s to section %s in %s\n",
3468 debugstr_w(deformated_key), debugstr_w(deformated_section),
3469 debugstr_w(fullname));
3470 WritePrivateProfileStringW(deformated_section, deformated_key,
3471 deformated_value, fullname);
3473 else if (action == 1)
3475 WCHAR returned[10];
3476 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3477 returned, 10, fullname);
3478 if (returned[0] == 0)
3480 TRACE("Adding value %s to section %s in %s\n",
3481 debugstr_w(deformated_key), debugstr_w(deformated_section),
3482 debugstr_w(fullname));
3484 WritePrivateProfileStringW(deformated_section, deformated_key,
3485 deformated_value, fullname);
3488 else if (action == 3)
3489 FIXME("Append to existing section not yet implemented\n");
3491 uirow = MSI_CreateRecord(4);
3492 MSI_RecordSetStringW(uirow,1,identifier);
3493 MSI_RecordSetStringW(uirow,2,deformated_section);
3494 MSI_RecordSetStringW(uirow,3,deformated_key);
3495 MSI_RecordSetStringW(uirow,4,deformated_value);
3496 ui_actiondata(package,szWriteIniValues,uirow);
3497 msiobj_release( &uirow->hdr );
3498 cleanup:
3499 msi_free(fullname);
3500 msi_free(folder);
3501 msi_free(deformated_key);
3502 msi_free(deformated_value);
3503 msi_free(deformated_section);
3504 return ERROR_SUCCESS;
3507 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3509 UINT rc;
3510 MSIQUERY * view;
3511 static const WCHAR ExecSeqQuery[] =
3512 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3513 '`','I','n','i','F','i','l','e','`',0};
3515 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3516 if (rc != ERROR_SUCCESS)
3518 TRACE("no IniFile table\n");
3519 return ERROR_SUCCESS;
3522 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3523 msiobj_release(&view->hdr);
3524 return rc;
3527 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3529 MSIPACKAGE *package = (MSIPACKAGE*)param;
3530 LPCWSTR filename;
3531 LPWSTR FullName;
3532 MSIFILE *file;
3533 DWORD len;
3534 static const WCHAR ExeStr[] =
3535 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3536 static const WCHAR close[] = {'\"',0};
3537 STARTUPINFOW si;
3538 PROCESS_INFORMATION info;
3539 BOOL brc;
3540 MSIRECORD *uirow;
3541 LPWSTR uipath, p;
3543 memset(&si,0,sizeof(STARTUPINFOW));
3545 filename = MSI_RecordGetString(row,1);
3546 file = get_loaded_file( package, filename );
3548 if (!file)
3550 ERR("Unable to find file id %s\n",debugstr_w(filename));
3551 return ERROR_SUCCESS;
3554 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3556 FullName = msi_alloc(len*sizeof(WCHAR));
3557 strcpyW(FullName,ExeStr);
3558 strcatW( FullName, file->TargetPath );
3559 strcatW(FullName,close);
3561 TRACE("Registering %s\n",debugstr_w(FullName));
3562 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3563 &si, &info);
3565 if (brc)
3566 msi_dialog_check_messages(info.hProcess);
3568 msi_free(FullName);
3570 /* the UI chunk */
3571 uirow = MSI_CreateRecord( 2 );
3572 uipath = strdupW( file->TargetPath );
3573 p = strrchrW(uipath,'\\');
3574 if (p)
3575 p[0]=0;
3576 MSI_RecordSetStringW( uirow, 1, &p[1] );
3577 MSI_RecordSetStringW( uirow, 2, uipath);
3578 ui_actiondata( package, szSelfRegModules, uirow);
3579 msiobj_release( &uirow->hdr );
3580 msi_free( uipath );
3581 /* FIXME: call ui_progress? */
3583 return ERROR_SUCCESS;
3586 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3588 UINT rc;
3589 MSIQUERY * view;
3590 static const WCHAR ExecSeqQuery[] =
3591 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3592 '`','S','e','l','f','R','e','g','`',0};
3594 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3595 if (rc != ERROR_SUCCESS)
3597 TRACE("no SelfReg table\n");
3598 return ERROR_SUCCESS;
3601 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3602 msiobj_release(&view->hdr);
3604 return ERROR_SUCCESS;
3607 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3609 MSIFEATURE *feature;
3610 UINT rc;
3611 HKEY hkey=0;
3612 HKEY hukey=0;
3614 if (!msi_check_publish(package))
3615 return ERROR_SUCCESS;
3617 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3618 if (rc != ERROR_SUCCESS)
3619 goto end;
3621 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3622 if (rc != ERROR_SUCCESS)
3623 goto end;
3625 /* here the guids are base 85 encoded */
3626 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3628 ComponentList *cl;
3629 LPWSTR data = NULL;
3630 GUID clsid;
3631 INT size;
3632 BOOL absent = FALSE;
3633 MSIRECORD *uirow;
3635 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3636 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3637 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3638 absent = TRUE;
3640 size = 1;
3641 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3643 size += 21;
3645 if (feature->Feature_Parent)
3646 size += strlenW( feature->Feature_Parent )+2;
3648 data = msi_alloc(size * sizeof(WCHAR));
3650 data[0] = 0;
3651 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3653 MSICOMPONENT* component = cl->component;
3654 WCHAR buf[21];
3656 buf[0] = 0;
3657 if (component->ComponentId)
3659 TRACE("From %s\n",debugstr_w(component->ComponentId));
3660 CLSIDFromString(component->ComponentId, &clsid);
3661 encode_base85_guid(&clsid,buf);
3662 TRACE("to %s\n",debugstr_w(buf));
3663 strcatW(data,buf);
3666 if (feature->Feature_Parent)
3668 static const WCHAR sep[] = {'\2',0};
3669 strcatW(data,sep);
3670 strcatW(data,feature->Feature_Parent);
3673 msi_reg_set_val_str( hkey, feature->Feature, data );
3674 msi_free(data);
3676 size = 0;
3677 if (feature->Feature_Parent)
3678 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3679 if (!absent)
3681 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3682 (LPBYTE)feature->Feature_Parent,size);
3684 else
3686 size += 2*sizeof(WCHAR);
3687 data = msi_alloc(size);
3688 data[0] = 0x6;
3689 data[1] = 0;
3690 if (feature->Feature_Parent)
3691 strcpyW( &data[1], feature->Feature_Parent );
3692 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3693 (LPBYTE)data,size);
3694 msi_free(data);
3697 /* the UI chunk */
3698 uirow = MSI_CreateRecord( 1 );
3699 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3700 ui_actiondata( package, szPublishFeatures, uirow);
3701 msiobj_release( &uirow->hdr );
3702 /* FIXME: call ui_progress? */
3705 end:
3706 RegCloseKey(hkey);
3707 RegCloseKey(hukey);
3708 return rc;
3711 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3713 UINT r;
3714 HKEY hkey;
3716 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3718 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3719 if (r == ERROR_SUCCESS)
3721 RegDeleteValueW(hkey, feature->Feature);
3722 RegCloseKey(hkey);
3725 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3726 if (r == ERROR_SUCCESS)
3728 RegDeleteValueW(hkey, feature->Feature);
3729 RegCloseKey(hkey);
3732 return ERROR_SUCCESS;
3735 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3737 MSIFEATURE *feature;
3739 if (msi_check_publish(package))
3740 return ERROR_SUCCESS;
3742 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3744 msi_unpublish_feature(package, feature);
3747 return ERROR_SUCCESS;
3750 static UINT msi_get_local_package_name( LPWSTR path )
3752 static const WCHAR szInstaller[] = {
3753 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3754 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3755 DWORD time, len, i;
3756 HANDLE handle;
3758 time = GetTickCount();
3759 GetWindowsDirectoryW( path, MAX_PATH );
3760 lstrcatW( path, szInstaller );
3761 CreateDirectoryW( path, NULL );
3763 len = lstrlenW(path);
3764 for (i=0; i<0x10000; i++)
3766 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3767 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3768 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3769 if (handle != INVALID_HANDLE_VALUE)
3771 CloseHandle(handle);
3772 break;
3774 if (GetLastError() != ERROR_FILE_EXISTS &&
3775 GetLastError() != ERROR_SHARING_VIOLATION)
3776 return ERROR_FUNCTION_FAILED;
3779 return ERROR_SUCCESS;
3782 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3784 static const WCHAR szOriginalDatabase[] =
3785 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
3786 WCHAR packagefile[MAX_PATH];
3787 LPWSTR msiFilePath;
3788 UINT r;
3790 r = msi_get_local_package_name( packagefile );
3791 if (r != ERROR_SUCCESS)
3792 return r;
3794 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3796 msiFilePath = msi_dup_property( package, szOriginalDatabase );
3797 r = CopyFileW( msiFilePath, packagefile, FALSE);
3799 if (!r)
3801 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3802 debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
3803 msi_free( msiFilePath );
3804 return ERROR_FUNCTION_FAILED;
3806 msi_free( msiFilePath );
3808 /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
3809 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3810 return ERROR_SUCCESS;
3813 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3815 LPWSTR prop, val, key;
3816 static const LPCSTR propval[] = {
3817 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3818 "ARPCONTACT", "Contact",
3819 "ARPCOMMENTS", "Comments",
3820 "ProductName", "DisplayName",
3821 "ProductVersion", "DisplayVersion",
3822 "ARPHELPLINK", "HelpLink",
3823 "ARPHELPTELEPHONE", "HelpTelephone",
3824 "ARPINSTALLLOCATION", "InstallLocation",
3825 "SourceDir", "InstallSource",
3826 "Manufacturer", "Publisher",
3827 "ARPREADME", "Readme",
3828 "ARPSIZE", "Size",
3829 "ARPURLINFOABOUT", "URLInfoAbout",
3830 "ARPURLUPDATEINFO", "URLUpdateInfo",
3831 NULL,
3833 const LPCSTR *p = propval;
3835 while( *p )
3837 prop = strdupAtoW( *p++ );
3838 key = strdupAtoW( *p++ );
3839 val = msi_dup_property( package, prop );
3840 msi_reg_set_val_str( hkey, key, val );
3841 msi_free(val);
3842 msi_free(key);
3843 msi_free(prop);
3845 return ERROR_SUCCESS;
3848 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3850 HKEY hkey=0;
3851 HKEY hudkey=0, props=0;
3852 LPWSTR buffer = NULL;
3853 UINT rc;
3854 DWORD size, langid;
3855 static const WCHAR szWindowsInstaller[] =
3856 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3857 static const WCHAR szUpgradeCode[] =
3858 {'U','p','g','r','a','d','e','C','o','d','e',0};
3859 static const WCHAR modpath_fmt[] =
3860 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3861 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3862 static const WCHAR szModifyPath[] =
3863 {'M','o','d','i','f','y','P','a','t','h',0};
3864 static const WCHAR szUninstallString[] =
3865 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3866 static const WCHAR szEstimatedSize[] =
3867 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3868 static const WCHAR szProductLanguage[] =
3869 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3870 static const WCHAR szProductVersion[] =
3871 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3873 SYSTEMTIME systime;
3874 static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
3875 LPWSTR upgrade_code;
3876 WCHAR szDate[9];
3878 /* FIXME: also need to publish if the product is in advertise mode */
3879 if (!msi_check_publish(package))
3880 return ERROR_SUCCESS;
3882 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
3883 if (rc != ERROR_SUCCESS)
3884 return rc;
3886 /* dump all the info i can grab */
3887 /* FIXME: Flesh out more information */
3889 msi_write_uninstall_property_vals( package, hkey );
3891 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
3893 msi_make_package_local( package, hkey );
3895 /* do ModifyPath and UninstallString */
3896 size = deformat_string(package,modpath_fmt,&buffer);
3897 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3898 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
3899 msi_free(buffer);
3901 /* FIXME: Write real Estimated Size when we have it */
3902 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
3904 GetLocalTime(&systime);
3905 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
3906 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
3908 langid = msi_get_property_int( package, szProductLanguage, 0 );
3909 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
3911 buffer = msi_dup_property( package, szProductVersion );
3912 if (buffer)
3914 DWORD verdword = msi_version_str_to_dword(buffer);
3916 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
3917 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
3918 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
3920 msi_free(buffer);
3922 /* Handle Upgrade Codes */
3923 upgrade_code = msi_dup_property( package, szUpgradeCode );
3924 if (upgrade_code)
3926 HKEY hkey2;
3927 WCHAR squashed[33];
3928 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3929 squash_guid(package->ProductCode,squashed);
3930 msi_reg_set_val_str( hkey2, squashed, NULL );
3931 RegCloseKey(hkey2);
3932 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
3933 squash_guid(package->ProductCode,squashed);
3934 msi_reg_set_val_str( hkey2, squashed, NULL );
3935 RegCloseKey(hkey2);
3937 msi_free(upgrade_code);
3940 RegCloseKey(hkey);
3942 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3943 if (rc != ERROR_SUCCESS)
3944 return rc;
3946 RegCloseKey(hudkey);
3948 rc = MSIREG_OpenInstallPropertiesKey(package->ProductCode, &props, TRUE);
3949 if (rc != ERROR_SUCCESS)
3950 return rc;
3952 msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
3953 RegCloseKey(props);
3955 return ERROR_SUCCESS;
3958 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
3960 return execute_script(package,INSTALL_SCRIPT);
3963 static UINT msi_unpublish_product(MSIPACKAGE *package)
3965 LPWSTR remove = NULL;
3966 LPWSTR *features = NULL;
3967 BOOL full_uninstall = TRUE;
3968 MSIFEATURE *feature;
3970 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
3971 static const WCHAR szAll[] = {'A','L','L',0};
3973 remove = msi_dup_property(package, szRemove);
3974 if (!remove)
3975 return ERROR_SUCCESS;
3977 features = msi_split_string(remove, ',');
3978 if (!features)
3980 msi_free(remove);
3981 ERR("REMOVE feature list is empty!\n");
3982 return ERROR_FUNCTION_FAILED;
3985 if (!lstrcmpW(features[0], szAll))
3986 full_uninstall = TRUE;
3987 else
3989 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3991 if (feature->Action != INSTALLSTATE_ABSENT)
3992 full_uninstall = FALSE;
3996 if (!full_uninstall)
3997 goto done;
3999 MSIREG_DeleteProductKey(package->ProductCode);
4000 MSIREG_DeleteUserProductKey(package->ProductCode);
4001 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4002 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4004 done:
4005 msi_free(remove);
4006 msi_free(features);
4007 return ERROR_SUCCESS;
4010 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4012 UINT rc;
4014 rc = msi_unpublish_product(package);
4015 if (rc != ERROR_SUCCESS)
4016 return rc;
4018 /* turn off scheduling */
4019 package->script->CurrentlyScripting= FALSE;
4021 /* first do the same as an InstallExecute */
4022 rc = ACTION_InstallExecute(package);
4023 if (rc != ERROR_SUCCESS)
4024 return rc;
4026 /* then handle Commit Actions */
4027 rc = execute_script(package,COMMIT_SCRIPT);
4029 return rc;
4032 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4034 static const WCHAR RunOnce[] = {
4035 'S','o','f','t','w','a','r','e','\\',
4036 'M','i','c','r','o','s','o','f','t','\\',
4037 'W','i','n','d','o','w','s','\\',
4038 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4039 'R','u','n','O','n','c','e',0};
4040 static const WCHAR InstallRunOnce[] = {
4041 'S','o','f','t','w','a','r','e','\\',
4042 'M','i','c','r','o','s','o','f','t','\\',
4043 'W','i','n','d','o','w','s','\\',
4044 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4045 'I','n','s','t','a','l','l','e','r','\\',
4046 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4048 static const WCHAR msiexec_fmt[] = {
4049 '%','s',
4050 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4051 '\"','%','s','\"',0};
4052 static const WCHAR install_fmt[] = {
4053 '/','I',' ','\"','%','s','\"',' ',
4054 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4055 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4056 WCHAR buffer[256], sysdir[MAX_PATH];
4057 HKEY hkey;
4058 WCHAR squished_pc[100];
4060 squash_guid(package->ProductCode,squished_pc);
4062 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4063 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4064 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4065 squished_pc);
4067 msi_reg_set_val_str( hkey, squished_pc, buffer );
4068 RegCloseKey(hkey);
4070 TRACE("Reboot command %s\n",debugstr_w(buffer));
4072 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4073 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4075 msi_reg_set_val_str( hkey, squished_pc, buffer );
4076 RegCloseKey(hkey);
4078 return ERROR_INSTALL_SUSPEND;
4081 static UINT msi_set_sourcedir_props(MSIPACKAGE *package)
4083 LPWSTR p, source;
4084 DWORD len;
4086 p = strrchrW( package->PackagePath, '\\' );
4087 if (!p)
4088 return ERROR_SUCCESS;
4090 len = p - package->PackagePath + 2;
4091 source = msi_alloc( len * sizeof(WCHAR) );
4092 lstrcpynW( source, package->PackagePath, len );
4094 MSI_SetPropertyW( package, cszSourceDir, source );
4095 MSI_SetPropertyW( package, cszSOURCEDIR, source );
4097 msi_free( source );
4099 return ERROR_SUCCESS;
4102 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4104 DWORD attrib;
4105 UINT rc;
4108 * We are currently doing what should be done here in the top level Install
4109 * however for Administrative and uninstalls this step will be needed
4111 if (!package->PackagePath)
4112 return ERROR_SUCCESS;
4114 msi_set_sourcedir_props(package);
4116 attrib = GetFileAttributesW(package->PackagePath);
4117 if (attrib == INVALID_FILE_ATTRIBUTES)
4119 LPWSTR prompt;
4120 LPWSTR msg;
4121 DWORD size = 0;
4123 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4124 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4125 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4126 if (rc == ERROR_MORE_DATA)
4128 prompt = msi_alloc(size * sizeof(WCHAR));
4129 MsiSourceListGetInfoW(package->ProductCode, NULL,
4130 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
4131 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4133 else
4134 prompt = strdupW(package->PackagePath);
4136 msg = generate_error_string(package,1302,1,prompt);
4137 while(attrib == INVALID_FILE_ATTRIBUTES)
4139 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4140 if (rc == IDCANCEL)
4142 rc = ERROR_INSTALL_USEREXIT;
4143 break;
4145 attrib = GetFileAttributesW(package->PackagePath);
4147 msi_free(prompt);
4148 rc = ERROR_SUCCESS;
4150 else
4151 return ERROR_SUCCESS;
4153 return rc;
4156 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4158 HKEY hkey=0;
4159 LPWSTR buffer;
4160 LPWSTR productid;
4161 UINT rc,i;
4163 static const WCHAR szPropKeys[][80] =
4165 {'P','r','o','d','u','c','t','I','D',0},
4166 {'U','S','E','R','N','A','M','E',0},
4167 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4168 {0},
4171 static const WCHAR szRegKeys[][80] =
4173 {'P','r','o','d','u','c','t','I','D',0},
4174 {'R','e','g','O','w','n','e','r',0},
4175 {'R','e','g','C','o','m','p','a','n','y',0},
4176 {0},
4179 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4180 if (!productid)
4181 return ERROR_SUCCESS;
4183 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4184 if (rc != ERROR_SUCCESS)
4185 goto end;
4187 for( i = 0; szPropKeys[i][0]; i++ )
4189 buffer = msi_dup_property( package, szPropKeys[i] );
4190 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4191 msi_free( buffer );
4194 end:
4195 msi_free(productid);
4196 RegCloseKey(hkey);
4198 /* FIXME: call ui_actiondata */
4200 return ERROR_SUCCESS;
4204 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4206 UINT rc;
4208 package->script->InWhatSequence |= SEQUENCE_EXEC;
4209 rc = ACTION_ProcessExecSequence(package,FALSE);
4210 return rc;
4214 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4216 MSIPACKAGE *package = (MSIPACKAGE*)param;
4217 LPCWSTR compgroupid=NULL;
4218 LPCWSTR feature=NULL;
4219 LPCWSTR text = NULL;
4220 LPCWSTR qualifier = NULL;
4221 LPCWSTR component = NULL;
4222 LPWSTR advertise = NULL;
4223 LPWSTR output = NULL;
4224 HKEY hkey;
4225 UINT rc = ERROR_SUCCESS;
4226 MSICOMPONENT *comp;
4227 DWORD sz = 0;
4228 MSIRECORD *uirow;
4230 component = MSI_RecordGetString(rec,3);
4231 comp = get_loaded_component(package,component);
4233 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4234 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4235 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4237 TRACE("Skipping: Component %s not scheduled for install\n",
4238 debugstr_w(component));
4240 return ERROR_SUCCESS;
4243 compgroupid = MSI_RecordGetString(rec,1);
4244 qualifier = MSI_RecordGetString(rec,2);
4246 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4247 if (rc != ERROR_SUCCESS)
4248 goto end;
4250 text = MSI_RecordGetString(rec,4);
4251 feature = MSI_RecordGetString(rec,5);
4253 advertise = create_component_advertise_string(package, comp, feature);
4255 sz = strlenW(advertise);
4257 if (text)
4258 sz += lstrlenW(text);
4260 sz+=3;
4261 sz *= sizeof(WCHAR);
4263 output = msi_alloc_zero(sz);
4264 strcpyW(output,advertise);
4265 msi_free(advertise);
4267 if (text)
4268 strcatW(output,text);
4270 msi_reg_set_val_multi_str( hkey, qualifier, output );
4272 end:
4273 RegCloseKey(hkey);
4274 msi_free(output);
4276 /* the UI chunk */
4277 uirow = MSI_CreateRecord( 2 );
4278 MSI_RecordSetStringW( uirow, 1, compgroupid );
4279 MSI_RecordSetStringW( uirow, 2, qualifier);
4280 ui_actiondata( package, szPublishComponents, uirow);
4281 msiobj_release( &uirow->hdr );
4282 /* FIXME: call ui_progress? */
4284 return rc;
4288 * At present I am ignorning the advertised components part of this and only
4289 * focusing on the qualified component sets
4291 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4293 UINT rc;
4294 MSIQUERY * view;
4295 static const WCHAR ExecSeqQuery[] =
4296 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4297 '`','P','u','b','l','i','s','h',
4298 'C','o','m','p','o','n','e','n','t','`',0};
4300 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4301 if (rc != ERROR_SUCCESS)
4302 return ERROR_SUCCESS;
4304 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4305 msiobj_release(&view->hdr);
4307 return rc;
4310 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4312 MSIPACKAGE *package = (MSIPACKAGE*)param;
4313 MSIRECORD *row;
4314 MSIFILE *file;
4315 SC_HANDLE hscm, service = NULL;
4316 LPCWSTR name, disp, comp, depends, pass;
4317 LPCWSTR load_order, serv_name, key;
4318 DWORD serv_type, start_type;
4319 DWORD err_control;
4321 static const WCHAR query[] =
4322 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4323 '`','C','o','m','p','o','n','e','n','t','`',' ',
4324 'W','H','E','R','E',' ',
4325 '`','C','o','m','p','o','n','e','n','t','`',' ',
4326 '=','\'','%','s','\'',0};
4328 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4329 if (!hscm)
4331 ERR("Failed to open the SC Manager!\n");
4332 goto done;
4335 start_type = MSI_RecordGetInteger(rec, 5);
4336 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4337 goto done;
4339 depends = MSI_RecordGetString(rec, 8);
4340 if (depends && *depends)
4341 FIXME("Dependency list unhandled!\n");
4343 name = MSI_RecordGetString(rec, 2);
4344 disp = MSI_RecordGetString(rec, 3);
4345 serv_type = MSI_RecordGetInteger(rec, 4);
4346 err_control = MSI_RecordGetInteger(rec, 6);
4347 load_order = MSI_RecordGetString(rec, 7);
4348 serv_name = MSI_RecordGetString(rec, 9);
4349 pass = MSI_RecordGetString(rec, 10);
4350 comp = MSI_RecordGetString(rec, 12);
4352 /* fetch the service path */
4353 row = MSI_QueryGetRecord(package->db, query, comp);
4354 if (!row)
4356 ERR("Control query failed!\n");
4357 goto done;
4360 key = MSI_RecordGetString(row, 6);
4361 msiobj_release(&row->hdr);
4363 file = get_loaded_file(package, key);
4364 if (!file)
4366 ERR("Failed to load the service file\n");
4367 goto done;
4370 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4371 start_type, err_control, file->TargetPath,
4372 load_order, NULL, NULL, serv_name, pass);
4373 if (!service)
4375 if (GetLastError() != ERROR_SERVICE_EXISTS)
4376 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4379 done:
4380 CloseServiceHandle(service);
4381 CloseServiceHandle(hscm);
4383 return ERROR_SUCCESS;
4386 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4388 UINT rc;
4389 MSIQUERY * view;
4390 static const WCHAR ExecSeqQuery[] =
4391 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4392 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4394 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4395 if (rc != ERROR_SUCCESS)
4396 return ERROR_SUCCESS;
4398 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4399 msiobj_release(&view->hdr);
4401 return rc;
4404 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4405 static LPCWSTR *msi_service_args_to_vector(LPCWSTR name, LPWSTR args, DWORD *numargs)
4407 LPCWSTR *vector;
4408 LPWSTR p, q;
4409 DWORD sep_len;
4411 static const WCHAR separator[] = {'[','~',']',0};
4413 *numargs = 0;
4414 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4416 if (!args)
4417 return NULL;
4419 vector = msi_alloc(sizeof(LPWSTR));
4420 if (!vector)
4421 return NULL;
4423 p = args;
4426 (*numargs)++;
4427 vector[*numargs - 1] = p;
4429 if ((q = strstrW(p, separator)))
4431 *q = '\0';
4433 vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4434 if (!vector)
4435 return NULL;
4437 p = q + sep_len;
4439 } while (q);
4441 return vector;
4444 static MSICOMPONENT *msi_find_component( MSIPACKAGE *package, LPCWSTR component )
4446 MSICOMPONENT *comp;
4448 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
4450 if (!lstrcmpW(comp->Component, component))
4451 return comp;
4454 return NULL;
4457 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4459 MSIPACKAGE *package = (MSIPACKAGE *)param;
4460 MSICOMPONENT *comp;
4461 SC_HANDLE scm, service = NULL;
4462 LPCWSTR name, *vector = NULL;
4463 LPWSTR args;
4464 DWORD event, numargs;
4465 UINT r = ERROR_FUNCTION_FAILED;
4467 comp = msi_find_component(package, MSI_RecordGetString(rec, 6));
4468 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4469 return ERROR_SUCCESS;
4471 name = MSI_RecordGetString(rec, 2);
4472 event = MSI_RecordGetInteger(rec, 3);
4473 args = strdupW(MSI_RecordGetString(rec, 4));
4475 if (!(event & msidbServiceControlEventStart))
4476 return ERROR_SUCCESS;
4478 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4479 if (!scm)
4481 ERR("Failed to open the service control manager\n");
4482 goto done;
4485 service = OpenServiceW(scm, name, SERVICE_START);
4486 if (!service)
4488 ERR("Failed to open service %s\n", debugstr_w(name));
4489 goto done;
4492 vector = msi_service_args_to_vector(name, args, &numargs);
4494 if (!StartServiceW(service, numargs, vector))
4496 ERR("Failed to start service %s\n", debugstr_w(name));
4497 goto done;
4500 r = ERROR_SUCCESS;
4502 done:
4503 CloseServiceHandle(service);
4504 CloseServiceHandle(scm);
4506 msi_free(args);
4507 msi_free(vector);
4508 return r;
4511 static UINT ACTION_StartServices( MSIPACKAGE *package )
4513 UINT rc;
4514 MSIQUERY *view;
4516 static const WCHAR query[] = {
4517 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4518 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4520 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4521 if (rc != ERROR_SUCCESS)
4522 return ERROR_SUCCESS;
4524 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4525 msiobj_release(&view->hdr);
4527 return rc;
4530 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4532 MSIFILE *file;
4534 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4536 if (!lstrcmpW(file->File, filename))
4537 return file;
4540 return NULL;
4543 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4545 MSIPACKAGE *package = (MSIPACKAGE*)param;
4546 LPWSTR driver, driver_path, ptr;
4547 WCHAR outpath[MAX_PATH];
4548 MSIFILE *driver_file, *setup_file;
4549 LPCWSTR desc;
4550 DWORD len, usage;
4551 UINT r = ERROR_SUCCESS;
4553 static const WCHAR driver_fmt[] = {
4554 'D','r','i','v','e','r','=','%','s',0};
4555 static const WCHAR setup_fmt[] = {
4556 'S','e','t','u','p','=','%','s',0};
4557 static const WCHAR usage_fmt[] = {
4558 'F','i','l','e','U','s','a','g','e','=','1',0};
4560 desc = MSI_RecordGetString(rec, 3);
4562 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4563 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4565 if (!driver_file || !setup_file)
4567 ERR("ODBC Driver entry not found!\n");
4568 return ERROR_FUNCTION_FAILED;
4571 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4572 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4573 lstrlenW(usage_fmt) + 1;
4574 driver = msi_alloc(len * sizeof(WCHAR));
4575 if (!driver)
4576 return ERROR_OUTOFMEMORY;
4578 ptr = driver;
4579 lstrcpyW(ptr, desc);
4580 ptr += lstrlenW(ptr) + 1;
4582 sprintfW(ptr, driver_fmt, driver_file->FileName);
4583 ptr += lstrlenW(ptr) + 1;
4585 sprintfW(ptr, setup_fmt, setup_file->FileName);
4586 ptr += lstrlenW(ptr) + 1;
4588 lstrcpyW(ptr, usage_fmt);
4589 ptr += lstrlenW(ptr) + 1;
4590 *ptr = '\0';
4592 driver_path = strdupW(driver_file->TargetPath);
4593 ptr = strrchrW(driver_path, '\\');
4594 if (ptr) *ptr = '\0';
4596 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4597 NULL, ODBC_INSTALL_COMPLETE, &usage))
4599 ERR("Failed to install SQL driver!\n");
4600 r = ERROR_FUNCTION_FAILED;
4603 msi_free(driver);
4604 msi_free(driver_path);
4606 return r;
4609 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4611 MSIPACKAGE *package = (MSIPACKAGE*)param;
4612 LPWSTR translator, translator_path, ptr;
4613 WCHAR outpath[MAX_PATH];
4614 MSIFILE *translator_file, *setup_file;
4615 LPCWSTR desc;
4616 DWORD len, usage;
4617 UINT r = ERROR_SUCCESS;
4619 static const WCHAR translator_fmt[] = {
4620 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4621 static const WCHAR setup_fmt[] = {
4622 'S','e','t','u','p','=','%','s',0};
4624 desc = MSI_RecordGetString(rec, 3);
4626 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4627 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4629 if (!translator_file || !setup_file)
4631 ERR("ODBC Translator entry not found!\n");
4632 return ERROR_FUNCTION_FAILED;
4635 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4636 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4637 translator = msi_alloc(len * sizeof(WCHAR));
4638 if (!translator)
4639 return ERROR_OUTOFMEMORY;
4641 ptr = translator;
4642 lstrcpyW(ptr, desc);
4643 ptr += lstrlenW(ptr) + 1;
4645 sprintfW(ptr, translator_fmt, translator_file->FileName);
4646 ptr += lstrlenW(ptr) + 1;
4648 sprintfW(ptr, setup_fmt, setup_file->FileName);
4649 ptr += lstrlenW(ptr) + 1;
4650 *ptr = '\0';
4652 translator_path = strdupW(translator_file->TargetPath);
4653 ptr = strrchrW(translator_path, '\\');
4654 if (ptr) *ptr = '\0';
4656 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4657 NULL, ODBC_INSTALL_COMPLETE, &usage))
4659 ERR("Failed to install SQL translator!\n");
4660 r = ERROR_FUNCTION_FAILED;
4663 msi_free(translator);
4664 msi_free(translator_path);
4666 return r;
4669 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4671 LPWSTR attrs;
4672 LPCWSTR desc, driver;
4673 WORD request = ODBC_ADD_SYS_DSN;
4674 INT registration;
4675 DWORD len;
4676 UINT r = ERROR_SUCCESS;
4678 static const WCHAR attrs_fmt[] = {
4679 'D','S','N','=','%','s',0 };
4681 desc = MSI_RecordGetString(rec, 3);
4682 driver = MSI_RecordGetString(rec, 4);
4683 registration = MSI_RecordGetInteger(rec, 5);
4685 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4686 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4688 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4689 attrs = msi_alloc(len * sizeof(WCHAR));
4690 if (!attrs)
4691 return ERROR_OUTOFMEMORY;
4693 sprintfW(attrs, attrs_fmt, desc);
4694 attrs[len - 1] = '\0';
4696 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4698 ERR("Failed to install SQL data source!\n");
4699 r = ERROR_FUNCTION_FAILED;
4702 msi_free(attrs);
4704 return r;
4707 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4709 UINT rc;
4710 MSIQUERY *view;
4712 static const WCHAR driver_query[] = {
4713 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4714 'O','D','B','C','D','r','i','v','e','r',0 };
4716 static const WCHAR translator_query[] = {
4717 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4718 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4720 static const WCHAR source_query[] = {
4721 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4722 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4724 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4725 if (rc != ERROR_SUCCESS)
4726 return ERROR_SUCCESS;
4728 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4729 msiobj_release(&view->hdr);
4731 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4732 if (rc != ERROR_SUCCESS)
4733 return ERROR_SUCCESS;
4735 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4736 msiobj_release(&view->hdr);
4738 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4739 if (rc != ERROR_SUCCESS)
4740 return ERROR_SUCCESS;
4742 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4743 msiobj_release(&view->hdr);
4745 return rc;
4748 #define ENV_ACT_SETALWAYS 0x1
4749 #define ENV_ACT_SETABSENT 0x2
4750 #define ENV_ACT_REMOVE 0x4
4751 #define ENV_ACT_REMOVEMATCH 0x8
4753 #define ENV_MOD_MACHINE 0x20000000
4754 #define ENV_MOD_APPEND 0x40000000
4755 #define ENV_MOD_PREFIX 0x80000000
4756 #define ENV_MOD_MASK 0xC0000000
4758 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
4760 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
4762 LPCWSTR cptr = *name;
4763 LPCWSTR ptr = *value;
4765 static const WCHAR prefix[] = {'[','~',']',0};
4766 static const int prefix_len = 3;
4768 *flags = 0;
4769 while (*cptr)
4771 if (*cptr == '=')
4772 *flags |= ENV_ACT_SETALWAYS;
4773 else if (*cptr == '+')
4774 *flags |= ENV_ACT_SETABSENT;
4775 else if (*cptr == '-')
4776 *flags |= ENV_ACT_REMOVE;
4777 else if (*cptr == '!')
4778 *flags |= ENV_ACT_REMOVEMATCH;
4779 else if (*cptr == '*')
4780 *flags |= ENV_MOD_MACHINE;
4781 else
4782 break;
4784 cptr++;
4785 (*name)++;
4788 if (!*cptr)
4790 ERR("Missing environment variable\n");
4791 return ERROR_FUNCTION_FAILED;
4794 if (!strncmpW(ptr, prefix, prefix_len))
4796 *flags |= ENV_MOD_APPEND;
4797 *value += lstrlenW(prefix);
4799 else if (lstrlenW(*value) >= prefix_len)
4801 ptr += lstrlenW(ptr) - prefix_len;
4802 if (!lstrcmpW(ptr, prefix))
4804 *flags |= ENV_MOD_PREFIX;
4805 /* the "[~]" will be removed by deformat_string */;
4809 if (!*flags ||
4810 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
4811 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
4812 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
4813 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
4815 ERR("Invalid flags: %08x\n", *flags);
4816 return ERROR_FUNCTION_FAILED;
4819 return ERROR_SUCCESS;
4822 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
4824 MSIPACKAGE *package = param;
4825 LPCWSTR name, value, comp;
4826 LPWSTR data = NULL, newval = NULL;
4827 LPWSTR deformatted = NULL, ptr;
4828 DWORD flags, type, size;
4829 LONG res;
4830 HKEY env = NULL, root = HKEY_CURRENT_USER;
4832 static const WCHAR environment[] =
4833 {'S','y','s','t','e','m','\\',
4834 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
4835 'C','o','n','t','r','o','l','\\',
4836 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
4837 'E','n','v','i','r','o','n','m','e','n','t',0};
4838 static const WCHAR semicolon[] = {';',0};
4840 name = MSI_RecordGetString(rec, 2);
4841 value = MSI_RecordGetString(rec, 3);
4842 comp = MSI_RecordGetString(rec, 4);
4844 res = env_set_flags(&name, &value, &flags);
4845 if (res != ERROR_SUCCESS)
4846 goto done;
4848 deformat_string(package, value, &deformatted);
4849 if (!deformatted)
4851 res = ERROR_OUTOFMEMORY;
4852 goto done;
4855 value = deformatted;
4857 if (flags & ENV_MOD_MACHINE)
4858 root = HKEY_LOCAL_MACHINE;
4860 res = RegOpenKeyExW(root, environment, 0, KEY_ALL_ACCESS, &env);
4861 if (res != ERROR_SUCCESS)
4862 goto done;
4864 if (flags & ENV_ACT_REMOVE)
4865 FIXME("Not removing environment variable on uninstall!\n");
4867 size = 0;
4868 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
4869 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
4870 (res == ERROR_SUCCESS && type != REG_SZ))
4871 goto done;
4873 if (res != ERROR_FILE_NOT_FOUND)
4875 if (flags & ENV_ACT_SETABSENT)
4877 res = ERROR_SUCCESS;
4878 goto done;
4881 data = msi_alloc(size);
4882 if (!data)
4884 RegCloseKey(env);
4885 return ERROR_OUTOFMEMORY;
4888 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
4889 if (res != ERROR_SUCCESS)
4890 goto done;
4892 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
4894 res = RegDeleteKeyW(env, name);
4895 goto done;
4898 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
4899 newval = msi_alloc(size);
4900 ptr = newval;
4901 if (!newval)
4903 res = ERROR_OUTOFMEMORY;
4904 goto done;
4907 if (!(flags & ENV_MOD_MASK))
4908 lstrcpyW(newval, value);
4909 else
4911 if (flags & ENV_MOD_PREFIX)
4913 lstrcpyW(newval, value);
4914 lstrcatW(newval, semicolon);
4915 ptr = newval + lstrlenW(value) + 1;
4918 lstrcpyW(ptr, data);
4920 if (flags & ENV_MOD_APPEND)
4922 lstrcatW(newval, semicolon);
4923 lstrcatW(newval, value);
4927 else
4929 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
4930 newval = msi_alloc(size);
4931 if (!newval)
4933 res = ERROR_OUTOFMEMORY;
4934 goto done;
4937 lstrcpyW(newval, value);
4940 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
4941 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
4943 done:
4944 if (env) RegCloseKey(env);
4945 msi_free(deformatted);
4946 msi_free(data);
4947 msi_free(newval);
4948 return res;
4951 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
4953 UINT rc;
4954 MSIQUERY * view;
4955 static const WCHAR ExecSeqQuery[] =
4956 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4957 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
4958 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4959 if (rc != ERROR_SUCCESS)
4960 return ERROR_SUCCESS;
4962 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
4963 msiobj_release(&view->hdr);
4965 return rc;
4968 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
4969 LPCSTR action, LPCWSTR table )
4971 static const WCHAR query[] = {
4972 'S','E','L','E','C','T',' ','*',' ',
4973 'F','R','O','M',' ','`','%','s','`',0 };
4974 MSIQUERY *view = NULL;
4975 DWORD count = 0;
4976 UINT r;
4978 r = MSI_OpenQuery( package->db, &view, query, table );
4979 if (r == ERROR_SUCCESS)
4981 r = MSI_IterateRecords(view, &count, NULL, package);
4982 msiobj_release(&view->hdr);
4985 if (count)
4986 FIXME("%s -> %u ignored %s table values\n",
4987 action, count, debugstr_w(table));
4989 return ERROR_SUCCESS;
4992 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
4994 TRACE("%p\n", package);
4995 return ERROR_SUCCESS;
4998 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5000 static const WCHAR table[] =
5001 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5002 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5005 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5007 static const WCHAR table[] = { 'M','o','v','e','F','i','l','e',0 };
5008 return msi_unimplemented_action_stub( package, "MoveFiles", table );
5011 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5013 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5014 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5017 static UINT ACTION_BindImage( MSIPACKAGE *package )
5019 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5020 return msi_unimplemented_action_stub( package, "BindImage", table );
5023 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5025 static const WCHAR table[] = {
5026 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5027 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5030 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5032 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5033 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5036 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5038 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5039 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5042 static UINT ACTION_StopServices( MSIPACKAGE *package )
5044 static const WCHAR table[] = {
5045 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5046 return msi_unimplemented_action_stub( package, "StopServices", table );
5049 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5051 static const WCHAR table[] = {
5052 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5053 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5055 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5057 static const WCHAR table[] = {
5058 'P','r','o','d','u','c','t','I','D',0 };
5059 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5062 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5064 static const WCHAR table[] = {
5065 'E','n','v','i','r','o','n','m','e','n','t',0 };
5066 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5069 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5071 static const WCHAR table[] = {
5072 'M','s','i','A','s','s','e','m','b','l','y',0 };
5073 return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
5076 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5078 static const WCHAR table[] = {
5079 'M','s','i','A','s','s','e','m','b','l','y',0 };
5080 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5083 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5085 static const WCHAR table[] = { 'F','o','n','t',0 };
5086 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5089 static UINT ACTION_CCPSearch( MSIPACKAGE *package )
5091 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5092 return msi_unimplemented_action_stub( package, "CCPSearch", table );
5095 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5097 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5098 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5101 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5103 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5104 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5107 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5109 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5110 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5113 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5115 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5116 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5119 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5121 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5122 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5125 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5127 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5128 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5131 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5133 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5134 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5137 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5139 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5140 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5143 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5145 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5146 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5149 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5151 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5152 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5155 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5157 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5158 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5161 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5163 static const WCHAR table[] = { 'A','p','p','I','d',0 };
5164 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5167 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5169 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5170 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5173 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5175 static const WCHAR table[] = { 'M','I','M','E',0 };
5176 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5179 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5181 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5182 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5185 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5187 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5188 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5191 static const struct _actions StandardActions[] = {
5192 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5193 { szAppSearch, ACTION_AppSearch },
5194 { szBindImage, ACTION_BindImage },
5195 { szCCPSearch, ACTION_CCPSearch },
5196 { szCostFinalize, ACTION_CostFinalize },
5197 { szCostInitialize, ACTION_CostInitialize },
5198 { szCreateFolders, ACTION_CreateFolders },
5199 { szCreateShortcuts, ACTION_CreateShortcuts },
5200 { szDeleteServices, ACTION_DeleteServices },
5201 { szDisableRollback, NULL },
5202 { szDuplicateFiles, ACTION_DuplicateFiles },
5203 { szExecuteAction, ACTION_ExecuteAction },
5204 { szFileCost, ACTION_FileCost },
5205 { szFindRelatedProducts, ACTION_FindRelatedProducts },
5206 { szForceReboot, ACTION_ForceReboot },
5207 { szInstallAdminPackage, NULL },
5208 { szInstallExecute, ACTION_InstallExecute },
5209 { szInstallExecuteAgain, ACTION_InstallExecute },
5210 { szInstallFiles, ACTION_InstallFiles},
5211 { szInstallFinalize, ACTION_InstallFinalize },
5212 { szInstallInitialize, ACTION_InstallInitialize },
5213 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5214 { szInstallValidate, ACTION_InstallValidate },
5215 { szIsolateComponents, ACTION_IsolateComponents },
5216 { szLaunchConditions, ACTION_LaunchConditions },
5217 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5218 { szMoveFiles, ACTION_MoveFiles },
5219 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5220 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5221 { szInstallODBC, ACTION_InstallODBC },
5222 { szInstallServices, ACTION_InstallServices },
5223 { szPatchFiles, ACTION_PatchFiles },
5224 { szProcessComponents, ACTION_ProcessComponents },
5225 { szPublishComponents, ACTION_PublishComponents },
5226 { szPublishFeatures, ACTION_PublishFeatures },
5227 { szPublishProduct, ACTION_PublishProduct },
5228 { szRegisterClassInfo, ACTION_RegisterClassInfo },
5229 { szRegisterComPlus, ACTION_RegisterComPlus},
5230 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5231 { szRegisterFonts, ACTION_RegisterFonts },
5232 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5233 { szRegisterProduct, ACTION_RegisterProduct },
5234 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5235 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5236 { szRegisterUser, ACTION_RegisterUser },
5237 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5238 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5239 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5240 { szRemoveFiles, ACTION_RemoveFiles },
5241 { szRemoveFolders, ACTION_RemoveFolders },
5242 { szRemoveIniValues, ACTION_RemoveIniValues },
5243 { szRemoveODBC, ACTION_RemoveODBC },
5244 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5245 { szRemoveShortcuts, ACTION_RemoveShortcuts },
5246 { szResolveSource, ACTION_ResolveSource },
5247 { szRMCCPSearch, ACTION_RMCCPSearch },
5248 { szScheduleReboot, NULL },
5249 { szSelfRegModules, ACTION_SelfRegModules },
5250 { szSelfUnregModules, ACTION_SelfUnregModules },
5251 { szSetODBCFolders, NULL },
5252 { szStartServices, ACTION_StartServices },
5253 { szStopServices, ACTION_StopServices },
5254 { szUnpublishComponents, ACTION_UnpublishComponents },
5255 { szUnpublishFeatures, ACTION_UnpublishFeatures },
5256 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5257 { szUnregisterComPlus, ACTION_UnregisterComPlus },
5258 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5259 { szUnregisterFonts, ACTION_UnregisterFonts },
5260 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5261 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5262 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5263 { szValidateProductID, ACTION_ValidateProductID },
5264 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5265 { szWriteIniValues, ACTION_WriteIniValues },
5266 { szWriteRegistryValues, ACTION_WriteRegistryValues },
5267 { NULL, NULL },