Release 2.9.
[wine.git] / dlls / msi / action.c
blob16652000ee57d87b73d6ee61ab86003c194cc9c0
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
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnpublishProduct[] =
149 {'U','n','p','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
150 static const WCHAR szUnregisterComPlus[] =
151 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
152 static const WCHAR szUnregisterTypeLibraries[] =
153 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
154 static const WCHAR szValidateProductID[] =
155 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
156 static const WCHAR szWriteEnvironmentStrings[] =
157 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
159 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
161 static const WCHAR Query_t[] =
162 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
163 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
164 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
165 ' ','\'','%','s','\'',0};
166 MSIRECORD * row;
168 row = MSI_QueryGetRecord( package->db, Query_t, action );
169 if (!row)
170 return;
171 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
172 msiobj_release(&row->hdr);
175 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
176 UINT rc)
178 MSIRECORD * row;
179 static const WCHAR template_s[]=
180 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
181 '%','s', '.',0};
182 static const WCHAR template_e[]=
183 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
184 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
185 '%','i','.',0};
186 static const WCHAR format[] =
187 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
188 WCHAR message[1024];
189 WCHAR timet[0x100];
191 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
192 if (start)
193 sprintfW(message,template_s,timet,action);
194 else
195 sprintfW(message,template_e,timet,action,rc);
197 row = MSI_CreateRecord(1);
198 MSI_RecordSetStringW(row,1,message);
200 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
201 msiobj_release(&row->hdr);
204 enum parse_state
206 state_whitespace,
207 state_token,
208 state_quote
211 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
213 enum parse_state state = state_quote;
214 const WCHAR *p;
215 WCHAR *out = value;
216 BOOL ignore, in_quotes = FALSE;
217 int count = 0, len = 0;
219 for (p = str; *p; p++)
221 ignore = FALSE;
222 switch (state)
224 case state_whitespace:
225 switch (*p)
227 case ' ':
228 in_quotes = TRUE;
229 ignore = TRUE;
230 len++;
231 break;
232 case '"':
233 state = state_quote;
234 if (in_quotes && p[1] != '\"') count--;
235 else count++;
236 break;
237 default:
238 state = state_token;
239 in_quotes = TRUE;
240 len++;
241 break;
243 break;
245 case state_token:
246 switch (*p)
248 case '"':
249 state = state_quote;
250 if (in_quotes) count--;
251 else count++;
252 break;
253 case ' ':
254 state = state_whitespace;
255 if (!count) goto done;
256 in_quotes = TRUE;
257 len++;
258 break;
259 default:
260 if (count) in_quotes = TRUE;
261 len++;
262 break;
264 break;
266 case state_quote:
267 switch (*p)
269 case '"':
270 if (in_quotes && p[1] != '\"') count--;
271 else count++;
272 break;
273 case ' ':
274 state = state_whitespace;
275 if (!count || (count > 1 && !len)) goto done;
276 in_quotes = TRUE;
277 len++;
278 break;
279 default:
280 state = state_token;
281 if (count) in_quotes = TRUE;
282 len++;
283 break;
285 break;
287 default: break;
289 if (!ignore) *out++ = *p;
290 if (!count) in_quotes = FALSE;
293 done:
294 if (!len) *value = 0;
295 else *out = 0;
297 *quotes = count;
298 return p - str;
301 static void remove_quotes( WCHAR *str )
303 WCHAR *p = str;
304 int len = strlenW( str );
306 while ((p = strchrW( p, '"' )))
308 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
309 p++;
313 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
314 BOOL preserve_case )
316 LPCWSTR ptr, ptr2;
317 int num_quotes;
318 DWORD len;
319 WCHAR *prop, *val;
320 UINT r;
322 if (!szCommandLine)
323 return ERROR_SUCCESS;
325 ptr = szCommandLine;
326 while (*ptr)
328 while (*ptr == ' ') ptr++;
329 if (!*ptr) break;
331 ptr2 = strchrW( ptr, '=' );
332 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
334 len = ptr2 - ptr;
335 if (!len) return ERROR_INVALID_COMMAND_LINE;
337 while (ptr[len - 1] == ' ') len--;
339 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
340 memcpy( prop, ptr, len * sizeof(WCHAR) );
341 prop[len] = 0;
342 if (!preserve_case) struprW( prop );
344 ptr2++;
345 while (*ptr2 == ' ') ptr2++;
347 num_quotes = 0;
348 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
349 len = parse_prop( ptr2, val, &num_quotes );
350 if (num_quotes % 2)
352 WARN("unbalanced quotes\n");
353 msi_free( val );
354 msi_free( prop );
355 return ERROR_INVALID_COMMAND_LINE;
357 remove_quotes( val );
358 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
360 r = msi_set_property( package->db, prop, val, -1 );
361 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
362 msi_reset_folders( package, TRUE );
364 msi_free( val );
365 msi_free( prop );
367 ptr = ptr2 + len;
370 return ERROR_SUCCESS;
373 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
375 LPCWSTR pc;
376 LPWSTR p, *ret = NULL;
377 UINT count = 0;
379 if (!str)
380 return ret;
382 /* count the number of substrings */
383 for ( pc = str, count = 0; pc; count++ )
385 pc = strchrW( pc, sep );
386 if (pc)
387 pc++;
390 /* allocate space for an array of substring pointers and the substrings */
391 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
392 (lstrlenW(str)+1) * sizeof(WCHAR) );
393 if (!ret)
394 return ret;
396 /* copy the string and set the pointers */
397 p = (LPWSTR) &ret[count+1];
398 lstrcpyW( p, str );
399 for( count = 0; (ret[count] = p); count++ )
401 p = strchrW( p, sep );
402 if (p)
403 *p++ = 0;
406 return ret;
409 static BOOL ui_sequence_exists( MSIPACKAGE *package )
411 static const WCHAR query [] = {
412 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
413 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
414 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
415 MSIQUERY *view;
416 DWORD count = 0;
418 if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
420 MSI_IterateRecords( view, &count, NULL, package );
421 msiobj_release( &view->hdr );
423 return count != 0;
426 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
428 WCHAR *source, *check, *p, *db;
429 DWORD len;
431 if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
432 return ERROR_OUTOFMEMORY;
434 if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' )))
436 msi_free(db);
437 return ERROR_SUCCESS;
439 len = p - db + 2;
440 source = msi_alloc( len * sizeof(WCHAR) );
441 lstrcpynW( source, db, len );
442 msi_free( db );
444 check = msi_dup_property( package->db, szSourceDir );
445 if (!check || replace)
447 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
448 if (r == ERROR_SUCCESS)
449 msi_reset_folders( package, TRUE );
451 msi_free( check );
453 check = msi_dup_property( package->db, szSOURCEDIR );
454 if (!check || replace)
455 msi_set_property( package->db, szSOURCEDIR, source, -1 );
457 msi_free( check );
458 msi_free( source );
460 return ERROR_SUCCESS;
463 static BOOL needs_ui_sequence(MSIPACKAGE *package)
465 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
468 UINT msi_set_context(MSIPACKAGE *package)
470 UINT r = msi_locate_product( package->ProductCode, &package->Context );
471 if (r != ERROR_SUCCESS)
473 int num = msi_get_property_int( package->db, szAllUsers, 0 );
474 if (num == 1 || num == 2)
475 package->Context = MSIINSTALLCONTEXT_MACHINE;
476 else
477 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
479 return ERROR_SUCCESS;
482 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
484 UINT rc;
485 LPCWSTR cond, action;
486 MSIPACKAGE *package = param;
488 action = MSI_RecordGetString(row,1);
489 if (!action)
491 ERR("Error is retrieving action name\n");
492 return ERROR_FUNCTION_FAILED;
495 /* check conditions */
496 cond = MSI_RecordGetString(row,2);
498 /* this is a hack to skip errors in the condition code */
499 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
501 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
502 return ERROR_SUCCESS;
505 if (needs_ui_sequence(package))
506 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
507 else
508 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
510 msi_dialog_check_messages( NULL );
512 if (package->CurrentInstallState != ERROR_SUCCESS)
513 rc = package->CurrentInstallState;
515 if (rc == ERROR_FUNCTION_NOT_CALLED)
516 rc = ERROR_SUCCESS;
518 if (rc != ERROR_SUCCESS)
519 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
521 if (package->need_reboot_now)
523 TRACE("action %s asked for immediate reboot, suspending installation\n",
524 debugstr_w(action));
525 rc = ACTION_ForceReboot( package );
527 return rc;
530 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
532 static const WCHAR query[] = {
533 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
534 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
535 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
536 '`','S','e','q','u','e','n','c','e','`',0};
537 MSIQUERY *view;
538 UINT r;
540 TRACE("%p %s\n", package, debugstr_w(table));
542 r = MSI_OpenQuery( package->db, &view, query, table );
543 if (r == ERROR_SUCCESS)
545 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
546 msiobj_release(&view->hdr);
548 return r;
551 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
553 static const WCHAR query[] = {
554 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
555 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
556 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
557 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
558 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
559 static const WCHAR query_validate[] = {
560 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
561 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
562 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
563 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
564 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
565 MSIQUERY *view;
566 INT seq = 0;
567 UINT rc;
569 if (package->script->ExecuteSequenceRun)
571 TRACE("Execute Sequence already Run\n");
572 return ERROR_SUCCESS;
575 package->script->ExecuteSequenceRun = TRUE;
577 /* get the sequence number */
578 if (UIran)
580 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
581 if (!row) return ERROR_FUNCTION_FAILED;
582 seq = MSI_RecordGetInteger(row,1);
583 msiobj_release(&row->hdr);
585 rc = MSI_OpenQuery(package->db, &view, query, seq);
586 if (rc == ERROR_SUCCESS)
588 TRACE("Running the actions\n");
590 msi_set_property( package->db, szSourceDir, NULL, -1 );
591 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
592 msiobj_release(&view->hdr);
594 return rc;
597 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
599 static const WCHAR query[] = {
600 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
601 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
602 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
603 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
604 MSIQUERY *view;
605 UINT rc;
607 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
608 if (rc == ERROR_SUCCESS)
610 TRACE("Running the actions\n");
611 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
612 msiobj_release(&view->hdr);
614 return rc;
617 /********************************************************
618 * ACTION helper functions and functions that perform the actions
619 *******************************************************/
620 static BOOL ACTION_HandleCustomAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc, UINT script )
622 BOOL ret=FALSE;
623 UINT arc;
625 arc = ACTION_CustomAction( package, action, script );
626 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
628 *rc = arc;
629 ret = TRUE;
631 return ret;
634 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
636 MSICOMPONENT *comp;
638 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
640 if (!strcmpW( Component, comp->Component )) return comp;
642 return NULL;
645 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
647 MSIFEATURE *feature;
649 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
651 if (!strcmpW( Feature, feature->Feature )) return feature;
653 return NULL;
656 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
658 MSIFILE *file;
660 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
662 if (!strcmpW( key, file->File )) return file;
664 return NULL;
667 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
669 MSIFOLDER *folder;
671 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
673 if (!strcmpW( dir, folder->Directory )) return folder;
675 return NULL;
679 * Recursively create all directories in the path.
680 * shamelessly stolen from setupapi/queue.c
682 BOOL msi_create_full_path( const WCHAR *path )
684 BOOL ret = TRUE;
685 WCHAR *new_path;
686 int len;
688 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
689 strcpyW( new_path, path );
691 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
692 new_path[len - 1] = 0;
694 while (!CreateDirectoryW( new_path, NULL ))
696 WCHAR *slash;
697 DWORD last_error = GetLastError();
698 if (last_error == ERROR_ALREADY_EXISTS) break;
699 if (last_error != ERROR_PATH_NOT_FOUND)
701 ret = FALSE;
702 break;
704 if (!(slash = strrchrW( new_path, '\\' )))
706 ret = FALSE;
707 break;
709 len = slash - new_path;
710 new_path[len] = 0;
711 if (!msi_create_full_path( new_path ))
713 ret = FALSE;
714 break;
716 new_path[len] = '\\';
718 msi_free( new_path );
719 return ret;
722 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
724 MSIRECORD *row;
726 row = MSI_CreateRecord( 4 );
727 MSI_RecordSetInteger( row, 1, a );
728 MSI_RecordSetInteger( row, 2, b );
729 MSI_RecordSetInteger( row, 3, c );
730 MSI_RecordSetInteger( row, 4, d );
731 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
732 msiobj_release( &row->hdr );
734 msi_dialog_check_messages( NULL );
737 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
739 static const WCHAR query[] =
740 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
741 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
742 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
743 WCHAR message[1024];
744 MSIRECORD *row = 0;
745 DWORD size;
747 if (!package->LastAction || strcmpW( package->LastAction, action ))
749 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
751 if (MSI_RecordIsNull( row, 3 ))
753 msiobj_release( &row->hdr );
754 return;
756 /* update the cached action format */
757 msi_free( package->ActionFormat );
758 package->ActionFormat = msi_dup_record_field( row, 3 );
759 msi_free( package->LastAction );
760 package->LastAction = strdupW( action );
761 msiobj_release( &row->hdr );
763 size = 1024;
764 MSI_RecordSetStringW( record, 0, package->ActionFormat );
765 MSI_FormatRecordW( package, record, message, &size );
766 row = MSI_CreateRecord( 1 );
767 MSI_RecordSetStringW( row, 1, message );
768 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
769 msiobj_release( &row->hdr );
772 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
774 if (!comp->Enabled)
776 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
777 return INSTALLSTATE_UNKNOWN;
779 if (package->need_rollback) return comp->Installed;
780 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
782 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
783 return INSTALLSTATE_UNKNOWN;
785 return comp->ActionRequest;
788 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
790 if (package->need_rollback) return feature->Installed;
791 return feature->ActionRequest;
794 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
796 MSIPACKAGE *package = param;
797 LPCWSTR dir, component, full_path;
798 MSIRECORD *uirow;
799 MSIFOLDER *folder;
800 MSICOMPONENT *comp;
802 component = MSI_RecordGetString(row, 2);
803 if (!component)
804 return ERROR_SUCCESS;
806 comp = msi_get_loaded_component(package, component);
807 if (!comp)
808 return ERROR_SUCCESS;
810 comp->Action = msi_get_component_action( package, comp );
811 if (comp->Action != INSTALLSTATE_LOCAL)
813 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
814 return ERROR_SUCCESS;
817 dir = MSI_RecordGetString(row,1);
818 if (!dir)
820 ERR("Unable to get folder id\n");
821 return ERROR_SUCCESS;
824 uirow = MSI_CreateRecord(1);
825 MSI_RecordSetStringW(uirow, 1, dir);
826 msi_ui_actiondata(package, szCreateFolders, uirow);
827 msiobj_release(&uirow->hdr);
829 full_path = msi_get_target_folder( package, dir );
830 if (!full_path)
832 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
833 return ERROR_SUCCESS;
835 TRACE("folder is %s\n", debugstr_w(full_path));
837 folder = msi_get_loaded_folder( package, dir );
838 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
839 folder->State = FOLDER_STATE_CREATED;
840 return ERROR_SUCCESS;
843 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
845 static const WCHAR query[] = {
846 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
847 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
848 MSIQUERY *view;
849 UINT rc;
851 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
852 if (rc != ERROR_SUCCESS)
853 return ERROR_SUCCESS;
855 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
856 msiobj_release(&view->hdr);
857 return rc;
860 static void remove_persistent_folder( MSIFOLDER *folder )
862 FolderList *fl;
864 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
866 remove_persistent_folder( fl->folder );
868 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
870 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
874 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
876 MSIPACKAGE *package = param;
877 LPCWSTR dir, component, full_path;
878 MSIRECORD *uirow;
879 MSIFOLDER *folder;
880 MSICOMPONENT *comp;
882 component = MSI_RecordGetString(row, 2);
883 if (!component)
884 return ERROR_SUCCESS;
886 comp = msi_get_loaded_component(package, component);
887 if (!comp)
888 return ERROR_SUCCESS;
890 comp->Action = msi_get_component_action( package, comp );
891 if (comp->Action != INSTALLSTATE_ABSENT)
893 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
894 return ERROR_SUCCESS;
897 dir = MSI_RecordGetString( row, 1 );
898 if (!dir)
900 ERR("Unable to get folder id\n");
901 return ERROR_SUCCESS;
904 full_path = msi_get_target_folder( package, dir );
905 if (!full_path)
907 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
908 return ERROR_SUCCESS;
910 TRACE("folder is %s\n", debugstr_w(full_path));
912 uirow = MSI_CreateRecord( 1 );
913 MSI_RecordSetStringW( uirow, 1, dir );
914 msi_ui_actiondata( package, szRemoveFolders, uirow );
915 msiobj_release( &uirow->hdr );
917 folder = msi_get_loaded_folder( package, dir );
918 remove_persistent_folder( folder );
919 return ERROR_SUCCESS;
922 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
924 static const WCHAR query[] = {
925 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
926 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
927 MSIQUERY *view;
928 UINT rc;
930 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
931 if (rc != ERROR_SUCCESS)
932 return ERROR_SUCCESS;
934 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
935 msiobj_release( &view->hdr );
936 return rc;
939 static UINT load_component( MSIRECORD *row, LPVOID param )
941 MSIPACKAGE *package = param;
942 MSICOMPONENT *comp;
944 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
945 if (!comp)
946 return ERROR_FUNCTION_FAILED;
948 list_add_tail( &package->components, &comp->entry );
950 /* fill in the data */
951 comp->Component = msi_dup_record_field( row, 1 );
953 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
955 comp->ComponentId = msi_dup_record_field( row, 2 );
956 comp->Directory = msi_dup_record_field( row, 3 );
957 comp->Attributes = MSI_RecordGetInteger(row,4);
958 comp->Condition = msi_dup_record_field( row, 5 );
959 comp->KeyPath = msi_dup_record_field( row, 6 );
961 comp->Installed = INSTALLSTATE_UNKNOWN;
962 comp->Action = INSTALLSTATE_UNKNOWN;
963 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
965 comp->assembly = msi_load_assembly( package, comp );
966 return ERROR_SUCCESS;
969 UINT msi_load_all_components( MSIPACKAGE *package )
971 static const WCHAR query[] = {
972 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
973 '`','C','o','m','p','o','n','e','n','t','`',0};
974 MSIQUERY *view;
975 UINT r;
977 if (!list_empty(&package->components))
978 return ERROR_SUCCESS;
980 r = MSI_DatabaseOpenViewW( package->db, query, &view );
981 if (r != ERROR_SUCCESS)
982 return r;
984 if (!msi_init_assembly_caches( package ))
986 ERR("can't initialize assembly caches\n");
987 msiobj_release( &view->hdr );
988 return ERROR_FUNCTION_FAILED;
991 r = MSI_IterateRecords(view, NULL, load_component, package);
992 msiobj_release(&view->hdr);
993 return r;
996 typedef struct {
997 MSIPACKAGE *package;
998 MSIFEATURE *feature;
999 } _ilfs;
1001 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1003 ComponentList *cl;
1005 cl = msi_alloc( sizeof (*cl) );
1006 if ( !cl )
1007 return ERROR_NOT_ENOUGH_MEMORY;
1008 cl->component = comp;
1009 list_add_tail( &feature->Components, &cl->entry );
1011 return ERROR_SUCCESS;
1014 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1016 FeatureList *fl;
1018 fl = msi_alloc( sizeof(*fl) );
1019 if ( !fl )
1020 return ERROR_NOT_ENOUGH_MEMORY;
1021 fl->feature = child;
1022 list_add_tail( &parent->Children, &fl->entry );
1024 return ERROR_SUCCESS;
1027 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1029 _ilfs* ilfs = param;
1030 LPCWSTR component;
1031 MSICOMPONENT *comp;
1033 component = MSI_RecordGetString(row,1);
1035 /* check to see if the component is already loaded */
1036 comp = msi_get_loaded_component( ilfs->package, component );
1037 if (!comp)
1039 WARN("ignoring unknown component %s\n", debugstr_w(component));
1040 return ERROR_SUCCESS;
1042 add_feature_component( ilfs->feature, comp );
1043 comp->Enabled = TRUE;
1045 return ERROR_SUCCESS;
1048 static UINT load_feature(MSIRECORD * row, LPVOID param)
1050 static const WCHAR query[] = {
1051 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1052 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1053 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1054 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1055 MSIPACKAGE *package = param;
1056 MSIFEATURE *feature;
1057 MSIQUERY *view;
1058 _ilfs ilfs;
1059 UINT rc;
1061 /* fill in the data */
1063 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1064 if (!feature)
1065 return ERROR_NOT_ENOUGH_MEMORY;
1067 list_init( &feature->Children );
1068 list_init( &feature->Components );
1070 feature->Feature = msi_dup_record_field( row, 1 );
1072 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1074 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1075 feature->Title = msi_dup_record_field( row, 3 );
1076 feature->Description = msi_dup_record_field( row, 4 );
1078 if (!MSI_RecordIsNull(row,5))
1079 feature->Display = MSI_RecordGetInteger(row,5);
1081 feature->Level= MSI_RecordGetInteger(row,6);
1082 feature->Directory = msi_dup_record_field( row, 7 );
1083 feature->Attributes = MSI_RecordGetInteger(row,8);
1085 feature->Installed = INSTALLSTATE_UNKNOWN;
1086 feature->Action = INSTALLSTATE_UNKNOWN;
1087 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1089 list_add_tail( &package->features, &feature->entry );
1091 /* load feature components */
1093 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1094 if (rc != ERROR_SUCCESS)
1095 return ERROR_SUCCESS;
1097 ilfs.package = package;
1098 ilfs.feature = feature;
1100 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1101 msiobj_release(&view->hdr);
1102 return rc;
1105 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1107 MSIPACKAGE *package = param;
1108 MSIFEATURE *parent, *child;
1110 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1111 if (!child)
1112 return ERROR_FUNCTION_FAILED;
1114 if (!child->Feature_Parent)
1115 return ERROR_SUCCESS;
1117 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1118 if (!parent)
1119 return ERROR_FUNCTION_FAILED;
1121 add_feature_child( parent, child );
1122 return ERROR_SUCCESS;
1125 UINT msi_load_all_features( MSIPACKAGE *package )
1127 static const WCHAR query[] = {
1128 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1129 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1130 '`','D','i','s','p','l','a','y','`',0};
1131 MSIQUERY *view;
1132 UINT r;
1134 if (!list_empty(&package->features))
1135 return ERROR_SUCCESS;
1137 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1138 if (r != ERROR_SUCCESS)
1139 return r;
1141 r = MSI_IterateRecords( view, NULL, load_feature, package );
1142 if (r != ERROR_SUCCESS)
1144 msiobj_release( &view->hdr );
1145 return r;
1147 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1148 msiobj_release( &view->hdr );
1149 return r;
1152 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1154 if (!p)
1155 return p;
1156 p = strchrW(p, ch);
1157 if (!p)
1158 return p;
1159 *p = 0;
1160 return p+1;
1163 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1165 static const WCHAR query[] = {
1166 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1167 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1168 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1169 MSIQUERY *view = NULL;
1170 MSIRECORD *row = NULL;
1171 UINT r;
1173 TRACE("%s\n", debugstr_w(file->File));
1175 r = MSI_OpenQuery(package->db, &view, query, file->File);
1176 if (r != ERROR_SUCCESS)
1177 goto done;
1179 r = MSI_ViewExecute(view, NULL);
1180 if (r != ERROR_SUCCESS)
1181 goto done;
1183 r = MSI_ViewFetch(view, &row);
1184 if (r != ERROR_SUCCESS)
1185 goto done;
1187 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1188 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1189 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1190 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1191 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1193 done:
1194 if (view) msiobj_release(&view->hdr);
1195 if (row) msiobj_release(&row->hdr);
1196 return r;
1199 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1201 MSIRECORD *row;
1202 static const WCHAR query[] = {
1203 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1204 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1205 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1207 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1208 if (!row)
1210 WARN("query failed\n");
1211 return ERROR_FUNCTION_FAILED;
1214 file->disk_id = MSI_RecordGetInteger( row, 1 );
1215 msiobj_release( &row->hdr );
1216 return ERROR_SUCCESS;
1219 static UINT load_file(MSIRECORD *row, LPVOID param)
1221 MSIPACKAGE* package = param;
1222 LPCWSTR component;
1223 MSIFILE *file;
1225 /* fill in the data */
1227 file = msi_alloc_zero( sizeof (MSIFILE) );
1228 if (!file)
1229 return ERROR_NOT_ENOUGH_MEMORY;
1231 file->File = msi_dup_record_field( row, 1 );
1233 component = MSI_RecordGetString( row, 2 );
1234 file->Component = msi_get_loaded_component( package, component );
1236 if (!file->Component)
1238 WARN("Component not found: %s\n", debugstr_w(component));
1239 msi_free(file->File);
1240 msi_free(file);
1241 return ERROR_SUCCESS;
1244 file->FileName = msi_dup_record_field( row, 3 );
1245 msi_reduce_to_long_filename( file->FileName );
1247 file->ShortName = msi_dup_record_field( row, 3 );
1248 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1250 file->FileSize = MSI_RecordGetInteger( row, 4 );
1251 file->Version = msi_dup_record_field( row, 5 );
1252 file->Language = msi_dup_record_field( row, 6 );
1253 file->Attributes = MSI_RecordGetInteger( row, 7 );
1254 file->Sequence = MSI_RecordGetInteger( row, 8 );
1256 file->state = msifs_invalid;
1258 /* if the compressed bits are not set in the file attributes,
1259 * then read the information from the package word count property
1261 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1263 file->IsCompressed = FALSE;
1265 else if (file->Attributes &
1266 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1268 file->IsCompressed = TRUE;
1270 else if (file->Attributes & msidbFileAttributesNoncompressed)
1272 file->IsCompressed = FALSE;
1274 else
1276 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1279 load_file_hash(package, file);
1280 load_file_disk_id(package, file);
1282 TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1284 list_add_tail( &package->files, &file->entry );
1286 return ERROR_SUCCESS;
1289 static UINT load_all_files(MSIPACKAGE *package)
1291 static const WCHAR query[] = {
1292 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1293 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1294 '`','S','e','q','u','e','n','c','e','`', 0};
1295 MSIQUERY *view;
1296 UINT rc;
1298 if (!list_empty(&package->files))
1299 return ERROR_SUCCESS;
1301 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1302 if (rc != ERROR_SUCCESS)
1303 return ERROR_SUCCESS;
1305 rc = MSI_IterateRecords(view, NULL, load_file, package);
1306 msiobj_release(&view->hdr);
1307 return rc;
1310 static UINT load_media( MSIRECORD *row, LPVOID param )
1312 MSIPACKAGE *package = param;
1313 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1314 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1316 /* FIXME: load external cabinets and directory sources too */
1317 if (!cabinet || cabinet[0] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
1318 return ERROR_SUCCESS;
1320 return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1323 static UINT load_all_media( MSIPACKAGE *package )
1325 static const WCHAR query[] = {
1326 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1327 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1328 '`','D','i','s','k','I','d','`',0};
1329 MSIQUERY *view;
1330 UINT r;
1332 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1333 if (r != ERROR_SUCCESS)
1334 return ERROR_SUCCESS;
1336 r = MSI_IterateRecords( view, NULL, load_media, package );
1337 msiobj_release( &view->hdr );
1338 return r;
1341 static UINT load_patch_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
1343 static const WCHAR query[] =
1344 {'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1345 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1346 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','u',0};
1347 MSIRECORD *rec;
1349 if (!(rec = MSI_QueryGetRecord( package->db, query, patch->Sequence )))
1351 WARN("query failed\n");
1352 return ERROR_FUNCTION_FAILED;
1355 patch->disk_id = MSI_RecordGetInteger( rec, 1 );
1356 msiobj_release( &rec->hdr );
1357 return ERROR_SUCCESS;
1360 static UINT load_patch(MSIRECORD *row, LPVOID param)
1362 MSIPACKAGE *package = param;
1363 MSIFILEPATCH *patch;
1364 const WCHAR *file_key;
1366 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1367 if (!patch)
1368 return ERROR_NOT_ENOUGH_MEMORY;
1370 file_key = MSI_RecordGetString( row, 1 );
1371 patch->File = msi_get_loaded_file( package, file_key );
1372 if (!patch->File)
1374 ERR("Failed to find target for patch in File table\n");
1375 msi_free(patch);
1376 return ERROR_FUNCTION_FAILED;
1379 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1380 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1381 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1383 /* FIXME:
1384 * Header field - for patch validation.
1385 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1388 load_patch_disk_id( package, patch );
1390 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1392 list_add_tail( &package->filepatches, &patch->entry );
1394 return ERROR_SUCCESS;
1397 static UINT load_all_patches(MSIPACKAGE *package)
1399 static const WCHAR query[] = {
1400 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1401 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1402 '`','S','e','q','u','e','n','c','e','`',0};
1403 MSIQUERY *view;
1404 UINT rc;
1406 if (!list_empty(&package->filepatches))
1407 return ERROR_SUCCESS;
1409 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1410 if (rc != ERROR_SUCCESS)
1411 return ERROR_SUCCESS;
1413 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1414 msiobj_release(&view->hdr);
1415 return rc;
1418 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1420 static const WCHAR query[] = {
1421 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1422 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1423 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1424 MSIQUERY *view;
1426 folder->persistent = FALSE;
1427 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1429 if (!MSI_ViewExecute( view, NULL ))
1431 MSIRECORD *rec;
1432 if (!MSI_ViewFetch( view, &rec ))
1434 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1435 folder->persistent = TRUE;
1436 msiobj_release( &rec->hdr );
1439 msiobj_release( &view->hdr );
1441 return ERROR_SUCCESS;
1444 static UINT load_folder( MSIRECORD *row, LPVOID param )
1446 MSIPACKAGE *package = param;
1447 static WCHAR szEmpty[] = { 0 };
1448 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1449 MSIFOLDER *folder;
1451 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1452 list_init( &folder->children );
1453 folder->Directory = msi_dup_record_field( row, 1 );
1454 folder->Parent = msi_dup_record_field( row, 2 );
1455 p = msi_dup_record_field(row, 3);
1457 TRACE("%s\n", debugstr_w(folder->Directory));
1459 /* split src and target dir */
1460 tgt_short = p;
1461 src_short = folder_split_path( p, ':' );
1463 /* split the long and short paths */
1464 tgt_long = folder_split_path( tgt_short, '|' );
1465 src_long = folder_split_path( src_short, '|' );
1467 /* check for no-op dirs */
1468 if (tgt_short && !strcmpW( szDot, tgt_short ))
1469 tgt_short = szEmpty;
1470 if (src_short && !strcmpW( szDot, src_short ))
1471 src_short = szEmpty;
1473 if (!tgt_long)
1474 tgt_long = tgt_short;
1476 if (!src_short) {
1477 src_short = tgt_short;
1478 src_long = tgt_long;
1481 if (!src_long)
1482 src_long = src_short;
1484 /* FIXME: use the target short path too */
1485 folder->TargetDefault = strdupW(tgt_long);
1486 folder->SourceShortPath = strdupW(src_short);
1487 folder->SourceLongPath = strdupW(src_long);
1488 msi_free(p);
1490 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1491 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1492 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1494 load_folder_persistence( package, folder );
1496 list_add_tail( &package->folders, &folder->entry );
1497 return ERROR_SUCCESS;
1500 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1502 FolderList *fl;
1504 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1505 fl->folder = child;
1506 list_add_tail( &parent->children, &fl->entry );
1507 return ERROR_SUCCESS;
1510 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1512 MSIPACKAGE *package = param;
1513 MSIFOLDER *parent, *child;
1515 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1516 return ERROR_FUNCTION_FAILED;
1518 if (!child->Parent) return ERROR_SUCCESS;
1520 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1521 return ERROR_FUNCTION_FAILED;
1523 return add_folder_child( parent, child );
1526 static UINT load_all_folders( MSIPACKAGE *package )
1528 static const WCHAR query[] = {
1529 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1530 '`','D','i','r','e','c','t','o','r','y','`',0};
1531 MSIQUERY *view;
1532 UINT r;
1534 if (!list_empty(&package->folders))
1535 return ERROR_SUCCESS;
1537 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1538 if (r != ERROR_SUCCESS)
1539 return r;
1541 r = MSI_IterateRecords( view, NULL, load_folder, package );
1542 if (r != ERROR_SUCCESS)
1544 msiobj_release( &view->hdr );
1545 return r;
1547 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1548 msiobj_release( &view->hdr );
1549 return r;
1552 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1554 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1555 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1557 load_all_folders( package );
1558 msi_load_all_components( package );
1559 msi_load_all_features( package );
1560 load_all_files( package );
1561 load_all_patches( package );
1562 load_all_media( package );
1564 return ERROR_SUCCESS;
1567 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1569 const WCHAR *action = package->script->Actions[script][index];
1570 ui_actionstart( package, action );
1571 TRACE("executing %s\n", debugstr_w(action));
1572 return ACTION_PerformAction( package, action, script );
1575 static UINT execute_script( MSIPACKAGE *package, UINT script )
1577 UINT i, rc = ERROR_SUCCESS;
1579 TRACE("executing script %u\n", script);
1581 if (!package->script)
1583 ERR("no script!\n");
1584 return ERROR_FUNCTION_FAILED;
1586 if (script == SCRIPT_ROLLBACK)
1588 for (i = package->script->ActionCount[script]; i > 0; i--)
1590 rc = execute_script_action( package, script, i - 1 );
1591 if (rc != ERROR_SUCCESS) break;
1594 else
1596 for (i = 0; i < package->script->ActionCount[script]; i++)
1598 rc = execute_script_action( package, script, i );
1599 if (rc != ERROR_SUCCESS) break;
1602 msi_free_action_script(package, script);
1603 return rc;
1606 static UINT ACTION_FileCost(MSIPACKAGE *package)
1608 return ERROR_SUCCESS;
1611 static void get_client_counts( MSIPACKAGE *package )
1613 MSICOMPONENT *comp;
1614 HKEY hkey;
1616 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1618 if (!comp->ComponentId) continue;
1620 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1621 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1623 comp->num_clients = 0;
1624 continue;
1626 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1627 NULL, NULL, NULL, NULL );
1628 RegCloseKey( hkey );
1632 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1634 MSICOMPONENT *comp;
1635 UINT r;
1637 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1639 if (!comp->ComponentId) continue;
1641 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1642 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1643 &comp->Installed );
1644 if (r == ERROR_SUCCESS) continue;
1646 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1647 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1648 &comp->Installed );
1649 if (r == ERROR_SUCCESS) continue;
1651 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1652 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1653 &comp->Installed );
1654 if (r == ERROR_SUCCESS) continue;
1656 comp->Installed = INSTALLSTATE_ABSENT;
1660 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1662 MSIFEATURE *feature;
1664 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1666 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1668 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1669 feature->Installed = INSTALLSTATE_ABSENT;
1670 else
1671 feature->Installed = state;
1675 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1677 return (feature->Level > 0 && feature->Level <= level);
1680 static BOOL process_state_property(MSIPACKAGE* package, int level,
1681 LPCWSTR property, INSTALLSTATE state)
1683 LPWSTR override;
1684 MSIFEATURE *feature;
1685 BOOL remove = !strcmpW(property, szRemove);
1686 BOOL reinstall = !strcmpW(property, szReinstall);
1688 override = msi_dup_property( package->db, property );
1689 if (!override)
1690 return FALSE;
1692 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1694 if (feature->Level <= 0)
1695 continue;
1697 if (reinstall)
1698 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1699 else if (remove)
1700 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1702 if (!strcmpiW( override, szAll ))
1704 feature->Action = state;
1705 feature->ActionRequest = state;
1707 else
1709 LPWSTR ptr = override;
1710 LPWSTR ptr2 = strchrW(override,',');
1712 while (ptr)
1714 int len = ptr2 - ptr;
1716 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1717 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1719 feature->Action = state;
1720 feature->ActionRequest = state;
1721 break;
1723 if (ptr2)
1725 ptr=ptr2+1;
1726 ptr2 = strchrW(ptr,',');
1728 else
1729 break;
1733 msi_free(override);
1734 return TRUE;
1737 static BOOL process_overrides( MSIPACKAGE *package, int level )
1739 static const WCHAR szAddLocal[] =
1740 {'A','D','D','L','O','C','A','L',0};
1741 static const WCHAR szAddSource[] =
1742 {'A','D','D','S','O','U','R','C','E',0};
1743 static const WCHAR szAdvertise[] =
1744 {'A','D','V','E','R','T','I','S','E',0};
1745 BOOL ret = FALSE;
1747 /* all these activation/deactivation things happen in order and things
1748 * later on the list override things earlier on the list.
1750 * 0 INSTALLLEVEL processing
1751 * 1 ADDLOCAL
1752 * 2 REMOVE
1753 * 3 ADDSOURCE
1754 * 4 ADDDEFAULT
1755 * 5 REINSTALL
1756 * 6 ADVERTISE
1757 * 7 COMPADDLOCAL
1758 * 8 COMPADDSOURCE
1759 * 9 FILEADDLOCAL
1760 * 10 FILEADDSOURCE
1761 * 11 FILEADDDEFAULT
1763 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1764 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1765 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1766 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1767 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1769 if (ret && !package->full_reinstall)
1770 msi_set_property( package->db, szPreselected, szOne, -1 );
1772 return ret;
1775 static void disable_children( MSIFEATURE *feature, int level )
1777 FeatureList *fl;
1779 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1781 if (!is_feature_selected( feature, level ))
1783 TRACE("child %s (level %d request %d) follows disabled parent %s (level %d request %d)\n",
1784 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1785 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1787 fl->feature->Level = feature->Level;
1788 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1789 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1791 disable_children( fl->feature, level );
1795 static void follow_parent( MSIFEATURE *feature )
1797 FeatureList *fl;
1799 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1801 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1803 TRACE("child %s (level %d request %d) follows parent %s (level %d request %d)\n",
1804 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1805 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1807 fl->feature->Action = feature->Action;
1808 fl->feature->ActionRequest = feature->ActionRequest;
1810 follow_parent( fl->feature );
1814 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1816 int level;
1817 MSICOMPONENT* component;
1818 MSIFEATURE *feature;
1820 TRACE("Checking Install Level\n");
1822 level = msi_get_property_int(package->db, szInstallLevel, 1);
1824 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1826 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1828 if (!is_feature_selected( feature, level )) continue;
1830 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1832 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1834 feature->Action = INSTALLSTATE_SOURCE;
1835 feature->ActionRequest = INSTALLSTATE_SOURCE;
1837 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1839 feature->Action = INSTALLSTATE_ADVERTISED;
1840 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1842 else
1844 feature->Action = INSTALLSTATE_LOCAL;
1845 feature->ActionRequest = INSTALLSTATE_LOCAL;
1849 /* disable child features of unselected parent or follow parent */
1850 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1852 if (feature->Feature_Parent) continue;
1853 disable_children( feature, level );
1854 follow_parent( feature );
1857 else /* preselected */
1859 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1861 if (!is_feature_selected( feature, level )) continue;
1863 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1865 if (feature->Installed == INSTALLSTATE_ABSENT)
1867 feature->Action = INSTALLSTATE_UNKNOWN;
1868 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1870 else
1872 feature->Action = feature->Installed;
1873 feature->ActionRequest = feature->Installed;
1877 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1879 if (feature->Feature_Parent) continue;
1880 disable_children( feature, level );
1881 follow_parent( feature );
1885 /* now we want to set component state based based on feature state */
1886 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1888 ComponentList *cl;
1890 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1891 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1892 feature->ActionRequest, feature->Action);
1894 /* features with components that have compressed files are made local */
1895 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1897 if (cl->component->ForceLocalState &&
1898 feature->ActionRequest == INSTALLSTATE_SOURCE)
1900 feature->Action = INSTALLSTATE_LOCAL;
1901 feature->ActionRequest = INSTALLSTATE_LOCAL;
1902 break;
1906 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1908 component = cl->component;
1910 switch (feature->ActionRequest)
1912 case INSTALLSTATE_ABSENT:
1913 component->anyAbsent = 1;
1914 break;
1915 case INSTALLSTATE_ADVERTISED:
1916 component->hasAdvertisedFeature = 1;
1917 break;
1918 case INSTALLSTATE_SOURCE:
1919 component->hasSourceFeature = 1;
1920 break;
1921 case INSTALLSTATE_LOCAL:
1922 component->hasLocalFeature = 1;
1923 break;
1924 case INSTALLSTATE_DEFAULT:
1925 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1926 component->hasAdvertisedFeature = 1;
1927 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1928 component->hasSourceFeature = 1;
1929 else
1930 component->hasLocalFeature = 1;
1931 break;
1932 default:
1933 break;
1938 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1940 /* check if it's local or source */
1941 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1942 (component->hasLocalFeature || component->hasSourceFeature))
1944 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1945 !component->ForceLocalState)
1947 component->Action = INSTALLSTATE_SOURCE;
1948 component->ActionRequest = INSTALLSTATE_SOURCE;
1950 else
1952 component->Action = INSTALLSTATE_LOCAL;
1953 component->ActionRequest = INSTALLSTATE_LOCAL;
1955 continue;
1958 /* if any feature is local, the component must be local too */
1959 if (component->hasLocalFeature)
1961 component->Action = INSTALLSTATE_LOCAL;
1962 component->ActionRequest = INSTALLSTATE_LOCAL;
1963 continue;
1965 if (component->hasSourceFeature)
1967 component->Action = INSTALLSTATE_SOURCE;
1968 component->ActionRequest = INSTALLSTATE_SOURCE;
1969 continue;
1971 if (component->hasAdvertisedFeature)
1973 component->Action = INSTALLSTATE_ADVERTISED;
1974 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1975 continue;
1977 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1978 if (component->anyAbsent && component->ComponentId)
1980 component->Action = INSTALLSTATE_ABSENT;
1981 component->ActionRequest = INSTALLSTATE_ABSENT;
1985 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1987 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1989 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1990 component->Action = INSTALLSTATE_LOCAL;
1991 component->ActionRequest = INSTALLSTATE_LOCAL;
1994 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1995 component->Installed == INSTALLSTATE_SOURCE &&
1996 component->hasSourceFeature)
1998 component->Action = INSTALLSTATE_UNKNOWN;
1999 component->ActionRequest = INSTALLSTATE_UNKNOWN;
2002 TRACE("component %s (installed %d request %d action %d)\n",
2003 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
2005 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
2006 component->num_clients++;
2007 else if (component->Action == INSTALLSTATE_ABSENT)
2008 component->num_clients--;
2011 return ERROR_SUCCESS;
2014 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2016 MSIPACKAGE *package = param;
2017 LPCWSTR name;
2018 MSIFEATURE *feature;
2020 name = MSI_RecordGetString( row, 1 );
2022 feature = msi_get_loaded_feature( package, name );
2023 if (!feature)
2024 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2025 else
2027 LPCWSTR Condition;
2028 Condition = MSI_RecordGetString(row,3);
2030 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2032 int level = MSI_RecordGetInteger(row,2);
2033 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2034 feature->Level = level;
2037 return ERROR_SUCCESS;
2040 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2042 static const WCHAR name[] = {'\\',0};
2043 VS_FIXEDFILEINFO *ptr, *ret;
2044 LPVOID version;
2045 DWORD versize, handle;
2046 UINT sz;
2048 versize = GetFileVersionInfoSizeW( filename, &handle );
2049 if (!versize)
2050 return NULL;
2052 version = msi_alloc( versize );
2053 if (!version)
2054 return NULL;
2056 GetFileVersionInfoW( filename, 0, versize, version );
2058 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2060 msi_free( version );
2061 return NULL;
2064 ret = msi_alloc( sz );
2065 memcpy( ret, ptr, sz );
2067 msi_free( version );
2068 return ret;
2071 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2073 DWORD ms, ls;
2075 msi_parse_version_string( version, &ms, &ls );
2077 if (fi->dwFileVersionMS > ms) return 1;
2078 else if (fi->dwFileVersionMS < ms) return -1;
2079 else if (fi->dwFileVersionLS > ls) return 1;
2080 else if (fi->dwFileVersionLS < ls) return -1;
2081 return 0;
2084 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2086 DWORD ms1, ms2;
2088 msi_parse_version_string( ver1, &ms1, NULL );
2089 msi_parse_version_string( ver2, &ms2, NULL );
2091 if (ms1 > ms2) return 1;
2092 else if (ms1 < ms2) return -1;
2093 return 0;
2096 DWORD msi_get_disk_file_size( LPCWSTR filename )
2098 HANDLE file;
2099 DWORD size;
2101 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2102 if (file == INVALID_HANDLE_VALUE)
2103 return INVALID_FILE_SIZE;
2105 size = GetFileSize( file, NULL );
2106 CloseHandle( file );
2107 return size;
2110 BOOL msi_file_hash_matches( MSIFILE *file )
2112 UINT r;
2113 MSIFILEHASHINFO hash;
2115 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2116 r = msi_get_filehash( file->TargetPath, &hash );
2117 if (r != ERROR_SUCCESS)
2118 return FALSE;
2120 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2123 static WCHAR *create_temp_dir( MSIDATABASE *db )
2125 static UINT id;
2126 WCHAR *ret;
2128 if (!db->tempfolder)
2130 WCHAR tmp[MAX_PATH];
2131 UINT len = sizeof(tmp)/sizeof(tmp[0]);
2133 if (msi_get_property( db, szTempFolder, tmp, &len ) ||
2134 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
2136 GetTempPathW( MAX_PATH, tmp );
2138 if (!(db->tempfolder = strdupW( tmp ))) return NULL;
2141 if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
2143 for (;;)
2145 if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret ))
2147 msi_free( ret );
2148 return NULL;
2150 if (CreateDirectoryW( ret, NULL )) break;
2154 return ret;
2158 * msi_build_directory_name()
2160 * This function is to save messing round with directory names
2161 * It handles adding backslashes between path segments,
2162 * and can add \ at the end of the directory name if told to.
2164 * It takes a variable number of arguments.
2165 * It always allocates a new string for the result, so make sure
2166 * to free the return value when finished with it.
2168 * The first arg is the number of path segments that follow.
2169 * The arguments following count are a list of path segments.
2170 * A path segment may be NULL.
2172 * Path segments will be added with a \ separating them.
2173 * A \ will not be added after the last segment, however if the
2174 * last segment is NULL, then the last character will be a \
2176 WCHAR *msi_build_directory_name( DWORD count, ... )
2178 DWORD sz = 1, i;
2179 WCHAR *dir;
2180 va_list va;
2182 va_start( va, count );
2183 for (i = 0; i < count; i++)
2185 const WCHAR *str = va_arg( va, const WCHAR * );
2186 if (str) sz += strlenW( str ) + 1;
2188 va_end( va );
2190 dir = msi_alloc( sz * sizeof(WCHAR) );
2191 dir[0] = 0;
2193 va_start( va, count );
2194 for (i = 0; i < count; i++)
2196 const WCHAR *str = va_arg( va, const WCHAR * );
2197 if (!str) continue;
2198 strcatW( dir, str );
2199 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2201 va_end( va );
2202 return dir;
2205 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2207 return comp->assembly && !comp->assembly->application;
2210 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2212 msi_free( file->TargetPath );
2213 if (msi_is_global_assembly( file->Component ))
2215 MSIASSEMBLY *assembly = file->Component->assembly;
2217 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2218 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2220 else
2222 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2223 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2226 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2229 static UINT calculate_file_cost( MSIPACKAGE *package )
2231 VS_FIXEDFILEINFO *file_version;
2232 WCHAR *font_version;
2233 MSIFILE *file;
2235 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2237 MSICOMPONENT *comp = file->Component;
2238 DWORD file_size;
2240 if (!comp->Enabled) continue;
2242 if (file->IsCompressed)
2243 comp->ForceLocalState = TRUE;
2245 set_target_path( package, file );
2247 if ((comp->assembly && !comp->assembly->installed) ||
2248 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2250 comp->Cost += file->FileSize;
2251 continue;
2253 file_size = msi_get_disk_file_size( file->TargetPath );
2254 TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2256 if (file->Version)
2258 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2260 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2262 comp->Cost += file->FileSize - file_size;
2264 msi_free( file_version );
2265 continue;
2267 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2269 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2271 comp->Cost += file->FileSize - file_size;
2273 msi_free( font_version );
2274 continue;
2277 if (file_size != file->FileSize)
2279 comp->Cost += file->FileSize - file_size;
2282 return ERROR_SUCCESS;
2285 WCHAR *msi_normalize_path( const WCHAR *in )
2287 const WCHAR *p = in;
2288 WCHAR *q, *ret;
2289 int n, len = strlenW( in ) + 2;
2291 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2293 len = 0;
2294 while (1)
2296 /* copy until the end of the string or a space */
2297 while (*p != ' ' && (*q = *p))
2299 p++, len++;
2300 /* reduce many backslashes to one */
2301 if (*p != '\\' || *q != '\\')
2302 q++;
2305 /* quit at the end of the string */
2306 if (!*p)
2307 break;
2309 /* count the number of spaces */
2310 n = 0;
2311 while (p[n] == ' ')
2312 n++;
2314 /* if it's leading or trailing space, skip it */
2315 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2316 p += n;
2317 else /* copy n spaces */
2318 while (n && (*q++ = *p++)) n--;
2320 while (q - ret > 0 && q[-1] == ' ') q--;
2321 if (q - ret > 0 && q[-1] != '\\')
2323 q[0] = '\\';
2324 q[1] = 0;
2326 return ret;
2329 static WCHAR *get_install_location( MSIPACKAGE *package )
2331 HKEY hkey;
2332 WCHAR *path;
2334 if (!package->ProductCode) return NULL;
2335 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2336 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2338 msi_free( path );
2339 path = NULL;
2341 RegCloseKey( hkey );
2342 return path;
2345 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2347 FolderList *fl;
2348 MSIFOLDER *folder, *parent, *child;
2349 WCHAR *path, *normalized_path;
2351 TRACE("resolving %s\n", debugstr_w(name));
2353 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2355 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2357 if (!(path = get_install_location( package )) &&
2358 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2360 path = msi_dup_property( package->db, szRootDrive );
2363 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2365 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2367 parent = msi_get_loaded_folder( package, folder->Parent );
2368 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2370 else
2371 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2373 normalized_path = msi_normalize_path( path );
2374 msi_free( path );
2375 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2377 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2378 msi_free( normalized_path );
2379 return;
2381 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2382 msi_free( folder->ResolvedTarget );
2383 folder->ResolvedTarget = normalized_path;
2385 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2387 child = fl->folder;
2388 msi_resolve_target_folder( package, child->Directory, load_prop );
2390 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2393 static ULONGLONG get_volume_space_required( MSIPACKAGE *package )
2395 MSICOMPONENT *comp;
2396 ULONGLONG ret = 0;
2398 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2400 if (comp->Action == INSTALLSTATE_LOCAL) ret += comp->Cost;
2402 return ret;
2405 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2407 static const WCHAR query[] =
2408 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2409 '`','C','o','n','d','i','t','i','o','n','`',0};
2410 static const WCHAR szOutOfDiskSpace[] =
2411 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2412 static const WCHAR szPrimaryFolder[] =
2413 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2414 static const WCHAR szPrimaryVolumePath[] =
2415 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2416 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2417 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2418 'A','v','a','i','l','a','b','l','e',0};
2419 static const WCHAR szPrimaryVolumeSpaceRequired[] =
2420 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2421 'R','e','q','u','i','r','e','d',0};
2422 static const WCHAR szPrimaryVolumeSpaceRemaining[] =
2423 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2424 'R','e','m','a','i','n','i','n','g',0};
2425 static const WCHAR szOutOfNoRbDiskSpace[] =
2426 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2427 MSICOMPONENT *comp;
2428 MSIQUERY *view;
2429 WCHAR *level, *primary_key, *primary_folder;
2430 UINT rc;
2432 TRACE("Building directory properties\n");
2433 msi_resolve_target_folder( package, szTargetDir, TRUE );
2435 TRACE("Evaluating component conditions\n");
2436 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2438 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2440 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2441 comp->Enabled = FALSE;
2443 else
2444 comp->Enabled = TRUE;
2446 get_client_counts( package );
2448 /* read components states from the registry */
2449 ACTION_GetComponentInstallStates(package);
2450 ACTION_GetFeatureInstallStates(package);
2452 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2454 TRACE("Evaluating feature conditions\n");
2456 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2457 if (rc == ERROR_SUCCESS)
2459 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2460 msiobj_release( &view->hdr );
2461 if (rc != ERROR_SUCCESS)
2462 return rc;
2466 TRACE("Calculating file cost\n");
2467 calculate_file_cost( package );
2469 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2470 /* set default run level if not set */
2471 level = msi_dup_property( package->db, szInstallLevel );
2472 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2473 msi_free(level);
2475 if ((rc = MSI_SetFeatureStates( package ))) return rc;
2477 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2479 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2481 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2482 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2484 static const WCHAR fmtW[] = {'%','l','u',0};
2485 ULARGE_INTEGER free;
2486 ULONGLONG required;
2487 WCHAR buf[21];
2489 primary_folder[2] = 0;
2490 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2492 sprintfW( buf, fmtW, free.QuadPart / 512 );
2493 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2495 required = get_volume_space_required( package );
2496 sprintfW( buf, fmtW, required / 512 );
2497 msi_set_property( package->db, szPrimaryVolumeSpaceRequired, buf, -1 );
2499 sprintfW( buf, fmtW, (free.QuadPart - required) / 512 );
2500 msi_set_property( package->db, szPrimaryVolumeSpaceRemaining, buf, -1 );
2501 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2503 msi_free( primary_folder );
2505 msi_free( primary_key );
2508 /* FIXME: check volume disk space */
2509 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2510 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2512 return ERROR_SUCCESS;
2515 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2517 BYTE *data;
2519 if (!value)
2521 *size = sizeof(WCHAR);
2522 *type = REG_SZ;
2523 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2524 return data;
2526 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2528 if (value[1]=='x')
2530 LPWSTR ptr;
2531 CHAR byte[5];
2532 LPWSTR deformated = NULL;
2533 int count;
2535 deformat_string(package, &value[2], &deformated);
2537 /* binary value type */
2538 ptr = deformated;
2539 *type = REG_BINARY;
2540 if (strlenW(ptr)%2)
2541 *size = (strlenW(ptr)/2)+1;
2542 else
2543 *size = strlenW(ptr)/2;
2545 data = msi_alloc(*size);
2547 byte[0] = '0';
2548 byte[1] = 'x';
2549 byte[4] = 0;
2550 count = 0;
2551 /* if uneven pad with a zero in front */
2552 if (strlenW(ptr)%2)
2554 byte[2]= '0';
2555 byte[3]= *ptr;
2556 ptr++;
2557 data[count] = (BYTE)strtol(byte,NULL,0);
2558 count ++;
2559 TRACE("Uneven byte count\n");
2561 while (*ptr)
2563 byte[2]= *ptr;
2564 ptr++;
2565 byte[3]= *ptr;
2566 ptr++;
2567 data[count] = (BYTE)strtol(byte,NULL,0);
2568 count ++;
2570 msi_free(deformated);
2572 TRACE("Data %i bytes(%i)\n",*size,count);
2574 else
2576 LPWSTR deformated;
2577 LPWSTR p;
2578 DWORD d = 0;
2579 deformat_string(package, &value[1], &deformated);
2581 *type=REG_DWORD;
2582 *size = sizeof(DWORD);
2583 data = msi_alloc(*size);
2584 p = deformated;
2585 if (*p == '-')
2586 p++;
2587 while (*p)
2589 if ( (*p < '0') || (*p > '9') )
2590 break;
2591 d *= 10;
2592 d += (*p - '0');
2593 p++;
2595 if (deformated[0] == '-')
2596 d = -d;
2597 *(LPDWORD)data = d;
2598 TRACE("DWORD %i\n",*(LPDWORD)data);
2600 msi_free(deformated);
2603 else
2605 const WCHAR *ptr = value;
2607 *type = REG_SZ;
2608 if (value[0] == '#')
2610 ptr++; len--;
2611 if (value[1] == '%')
2613 ptr++; len--;
2614 *type = REG_EXPAND_SZ;
2617 data = (BYTE *)msi_strdupW( ptr, len );
2618 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2619 *size = (len + 1) * sizeof(WCHAR);
2621 return data;
2624 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2626 const WCHAR *ret;
2628 switch (root)
2630 case -1:
2631 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2633 *root_key = HKEY_LOCAL_MACHINE;
2634 ret = szHLM;
2636 else
2638 *root_key = HKEY_CURRENT_USER;
2639 ret = szHCU;
2641 break;
2642 case 0:
2643 *root_key = HKEY_CLASSES_ROOT;
2644 ret = szHCR;
2645 break;
2646 case 1:
2647 *root_key = HKEY_CURRENT_USER;
2648 ret = szHCU;
2649 break;
2650 case 2:
2651 *root_key = HKEY_LOCAL_MACHINE;
2652 ret = szHLM;
2653 break;
2654 case 3:
2655 *root_key = HKEY_USERS;
2656 ret = szHU;
2657 break;
2658 default:
2659 ERR("Unknown root %i\n", root);
2660 return NULL;
2663 return ret;
2666 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2668 REGSAM view = 0;
2669 if (is_wow64 || is_64bit)
2670 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2671 return view;
2674 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access )
2676 WCHAR *subkey, *p, *q;
2677 HKEY hkey, ret = NULL;
2678 LONG res;
2680 access |= get_registry_view( comp );
2682 if (!(subkey = strdupW( path ))) return NULL;
2683 p = subkey;
2684 if ((q = strchrW( p, '\\' ))) *q = 0;
2685 if (create)
2686 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2687 else
2688 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2689 if (res)
2691 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2692 msi_free( subkey );
2693 return NULL;
2695 if (q && q[1])
2697 ret = open_key( comp, hkey, q + 1, create, access );
2698 RegCloseKey( hkey );
2700 else ret = hkey;
2701 msi_free( subkey );
2702 return ret;
2705 static BOOL is_special_entry( const WCHAR *name )
2707 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2710 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2712 const WCHAR *p = str;
2713 WCHAR **ret;
2714 int i = 0;
2716 *count = 0;
2717 if (!str) return NULL;
2718 while ((p - str) < len)
2720 p += strlenW( p ) + 1;
2721 (*count)++;
2723 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2724 p = str;
2725 while ((p - str) < len)
2727 if (!(ret[i] = strdupW( p )))
2729 for (; i >= 0; i--) msi_free( ret[i] );
2730 msi_free( ret );
2731 return NULL;
2733 p += strlenW( p ) + 1;
2734 i++;
2736 return ret;
2739 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2740 WCHAR **right, DWORD right_count, DWORD *size )
2742 WCHAR *ret, *p;
2743 unsigned int i;
2745 *size = sizeof(WCHAR);
2746 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2747 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2749 if (!(ret = p = msi_alloc( *size ))) return NULL;
2751 for (i = 0; i < left_count; i++)
2753 strcpyW( p, left[i] );
2754 p += strlenW( p ) + 1;
2756 for (i = 0; i < right_count; i++)
2758 strcpyW( p, right[i] );
2759 p += strlenW( p ) + 1;
2761 *p = 0;
2762 return ret;
2765 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2766 WCHAR **new, DWORD new_count )
2768 DWORD ret = old_count;
2769 unsigned int i, j, k;
2771 for (i = 0; i < new_count; i++)
2773 for (j = 0; j < old_count; j++)
2775 if (old[j] && !strcmpW( new[i], old[j] ))
2777 msi_free( old[j] );
2778 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2779 old[k] = NULL;
2780 ret--;
2784 return ret;
2787 enum join_op
2789 JOIN_OP_APPEND,
2790 JOIN_OP_PREPEND,
2791 JOIN_OP_REPLACE
2794 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2795 WCHAR **new, DWORD new_count, DWORD *size )
2797 switch (op)
2799 case JOIN_OP_APPEND:
2800 old_count = remove_duplicate_values( old, old_count, new, new_count );
2801 return flatten_multi_string_values( old, old_count, new, new_count, size );
2803 case JOIN_OP_PREPEND:
2804 old_count = remove_duplicate_values( old, old_count, new, new_count );
2805 return flatten_multi_string_values( new, new_count, old, old_count, size );
2807 case JOIN_OP_REPLACE:
2808 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2810 default:
2811 ERR("unhandled join op %u\n", op);
2812 return NULL;
2816 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2817 BYTE *new_value, DWORD new_size, DWORD *size )
2819 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2820 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2821 enum join_op op = JOIN_OP_REPLACE;
2822 WCHAR **old = NULL, **new = NULL;
2823 BYTE *ret;
2825 if (new_size / sizeof(WCHAR) - 1 > 1)
2827 new_ptr = (const WCHAR *)new_value;
2828 new_len = new_size / sizeof(WCHAR) - 1;
2830 if (!new_ptr[0] && new_ptr[new_len - 1])
2832 op = JOIN_OP_APPEND;
2833 new_len--;
2834 new_ptr++;
2836 else if (new_ptr[0] && !new_ptr[new_len - 1])
2838 op = JOIN_OP_PREPEND;
2839 new_len--;
2841 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2843 op = JOIN_OP_REPLACE;
2844 new_len -= 2;
2845 new_ptr++;
2847 new = split_multi_string_values( new_ptr, new_len, &new_count );
2849 if (old_size / sizeof(WCHAR) - 1 > 1)
2851 old_ptr = (const WCHAR *)old_value;
2852 old_len = old_size / sizeof(WCHAR) - 1;
2853 old = split_multi_string_values( old_ptr, old_len, &old_count );
2855 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2856 for (i = 0; i < old_count; i++) msi_free( old[i] );
2857 for (i = 0; i < new_count; i++) msi_free( new[i] );
2858 msi_free( old );
2859 msi_free( new );
2860 return ret;
2863 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2865 BYTE *ret;
2866 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2867 if (!(ret = msi_alloc( *size ))) return NULL;
2868 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2869 return ret;
2872 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2874 MSIPACKAGE *package = param;
2875 BYTE *new_value, *old_value = NULL;
2876 HKEY root_key, hkey;
2877 DWORD type, old_type, new_size, old_size = 0;
2878 LPWSTR deformated, uikey;
2879 const WCHAR *szRoot, *component, *name, *key, *str;
2880 MSICOMPONENT *comp;
2881 MSIRECORD * uirow;
2882 INT root;
2883 BOOL check_first = FALSE;
2884 int len;
2886 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2888 component = MSI_RecordGetString(row, 6);
2889 comp = msi_get_loaded_component(package,component);
2890 if (!comp)
2891 return ERROR_SUCCESS;
2893 comp->Action = msi_get_component_action( package, comp );
2894 if (comp->Action != INSTALLSTATE_LOCAL)
2896 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2897 return ERROR_SUCCESS;
2900 name = MSI_RecordGetString(row, 4);
2901 if( MSI_RecordIsNull(row,5) && name )
2903 /* null values can have special meanings */
2904 if (name[0]=='-' && name[1] == 0)
2905 return ERROR_SUCCESS;
2906 if ((name[0] == '+' || name[0] == '*') && !name[1])
2907 check_first = TRUE;
2910 root = MSI_RecordGetInteger(row,2);
2911 key = MSI_RecordGetString(row, 3);
2913 szRoot = get_root_key( package, root, &root_key );
2914 if (!szRoot)
2915 return ERROR_SUCCESS;
2917 deformat_string(package, key , &deformated);
2918 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2919 strcpyW(uikey,szRoot);
2920 strcatW(uikey,deformated);
2922 if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE )))
2924 ERR("Could not create key %s\n", debugstr_w(deformated));
2925 msi_free(uikey);
2926 msi_free(deformated);
2927 return ERROR_FUNCTION_FAILED;
2929 msi_free( deformated );
2930 str = msi_record_get_string( row, 5, NULL );
2931 len = deformat_string( package, str, &deformated );
2932 new_value = parse_value( package, deformated, len, &type, &new_size );
2934 msi_free( deformated );
2935 deformat_string(package, name, &deformated);
2937 if (!is_special_entry( name ))
2939 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2940 if (type == REG_MULTI_SZ)
2942 BYTE *new;
2943 if (old_value && old_type != REG_MULTI_SZ)
2945 msi_free( old_value );
2946 old_value = NULL;
2947 old_size = 0;
2949 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2950 msi_free( new_value );
2951 new_value = new;
2953 if (!check_first)
2955 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2956 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2958 else if (!old_value)
2960 if (deformated || new_size)
2962 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2963 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2966 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2968 RegCloseKey(hkey);
2970 uirow = MSI_CreateRecord(3);
2971 MSI_RecordSetStringW(uirow,2,deformated);
2972 MSI_RecordSetStringW(uirow,1,uikey);
2973 if (type == REG_SZ || type == REG_EXPAND_SZ)
2974 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2975 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2976 msiobj_release( &uirow->hdr );
2978 msi_free(new_value);
2979 msi_free(old_value);
2980 msi_free(deformated);
2981 msi_free(uikey);
2983 return ERROR_SUCCESS;
2986 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2988 static const WCHAR query[] = {
2989 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2990 '`','R','e','g','i','s','t','r','y','`',0};
2991 MSIQUERY *view;
2992 UINT rc;
2994 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2995 if (rc != ERROR_SUCCESS)
2996 return ERROR_SUCCESS;
2998 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2999 msiobj_release(&view->hdr);
3000 return rc;
3003 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3005 REGSAM access = 0;
3006 WCHAR *subkey, *p;
3007 HKEY hkey;
3008 LONG res;
3010 access |= get_registry_view( comp );
3012 if (!(subkey = strdupW( path ))) return;
3015 if ((p = strrchrW( subkey, '\\' )))
3017 *p = 0;
3018 if (!p[1]) continue; /* trailing backslash */
3019 hkey = open_key( comp, root, subkey, FALSE, access | READ_CONTROL );
3020 if (!hkey) break;
3021 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
3022 RegCloseKey( hkey );
3024 else
3025 res = RegDeleteKeyExW( root, subkey, access, 0 );
3026 if (res)
3028 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
3029 break;
3031 } while (p);
3032 msi_free( subkey );
3035 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
3037 LONG res;
3038 HKEY hkey;
3039 DWORD num_subkeys, num_values;
3041 if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE )))
3043 if ((res = RegDeleteValueW( hkey, value )))
3044 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3046 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3047 NULL, NULL, NULL, NULL );
3048 RegCloseKey( hkey );
3049 if (!res && !num_subkeys && !num_values)
3051 TRACE("removing empty key %s\n", debugstr_w(path));
3052 delete_key( comp, root, path );
3057 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3059 LONG res;
3060 HKEY hkey;
3062 if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return;
3063 res = RegDeleteTreeW( hkey, NULL );
3064 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3065 delete_key( comp, root, path );
3066 RegCloseKey( hkey );
3069 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3071 MSIPACKAGE *package = param;
3072 LPCWSTR component, name, key_str, root_key_str;
3073 LPWSTR deformated_key, deformated_name, ui_key_str;
3074 MSICOMPONENT *comp;
3075 MSIRECORD *uirow;
3076 BOOL delete_key = FALSE;
3077 HKEY hkey_root;
3078 UINT size;
3079 INT root;
3081 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3083 component = MSI_RecordGetString( row, 6 );
3084 comp = msi_get_loaded_component( package, component );
3085 if (!comp)
3086 return ERROR_SUCCESS;
3088 comp->Action = msi_get_component_action( package, comp );
3089 if (comp->Action != INSTALLSTATE_ABSENT)
3091 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3092 return ERROR_SUCCESS;
3095 name = MSI_RecordGetString( row, 4 );
3096 if (MSI_RecordIsNull( row, 5 ) && name )
3098 if (name[0] == '+' && !name[1])
3099 return ERROR_SUCCESS;
3100 if ((name[0] == '-' || name[0] == '*') && !name[1])
3102 delete_key = TRUE;
3103 name = NULL;
3107 root = MSI_RecordGetInteger( row, 2 );
3108 key_str = MSI_RecordGetString( row, 3 );
3110 root_key_str = get_root_key( package, root, &hkey_root );
3111 if (!root_key_str)
3112 return ERROR_SUCCESS;
3114 deformat_string( package, key_str, &deformated_key );
3115 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3116 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3117 strcpyW( ui_key_str, root_key_str );
3118 strcatW( ui_key_str, deformated_key );
3120 deformat_string( package, name, &deformated_name );
3122 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3123 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3124 msi_free( deformated_key );
3126 uirow = MSI_CreateRecord( 2 );
3127 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3128 MSI_RecordSetStringW( uirow, 2, deformated_name );
3129 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3130 msiobj_release( &uirow->hdr );
3132 msi_free( ui_key_str );
3133 msi_free( deformated_name );
3134 return ERROR_SUCCESS;
3137 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3139 MSIPACKAGE *package = param;
3140 LPCWSTR component, name, key_str, root_key_str;
3141 LPWSTR deformated_key, deformated_name, ui_key_str;
3142 MSICOMPONENT *comp;
3143 MSIRECORD *uirow;
3144 BOOL delete_key = FALSE;
3145 HKEY hkey_root;
3146 UINT size;
3147 INT root;
3149 component = MSI_RecordGetString( row, 5 );
3150 comp = msi_get_loaded_component( package, component );
3151 if (!comp)
3152 return ERROR_SUCCESS;
3154 comp->Action = msi_get_component_action( package, comp );
3155 if (comp->Action != INSTALLSTATE_LOCAL)
3157 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3158 return ERROR_SUCCESS;
3161 if ((name = MSI_RecordGetString( row, 4 )))
3163 if (name[0] == '-' && !name[1])
3165 delete_key = TRUE;
3166 name = NULL;
3170 root = MSI_RecordGetInteger( row, 2 );
3171 key_str = MSI_RecordGetString( row, 3 );
3173 root_key_str = get_root_key( package, root, &hkey_root );
3174 if (!root_key_str)
3175 return ERROR_SUCCESS;
3177 deformat_string( package, key_str, &deformated_key );
3178 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3179 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3180 strcpyW( ui_key_str, root_key_str );
3181 strcatW( ui_key_str, deformated_key );
3183 deformat_string( package, name, &deformated_name );
3185 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3186 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3187 msi_free( deformated_key );
3189 uirow = MSI_CreateRecord( 2 );
3190 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3191 MSI_RecordSetStringW( uirow, 2, deformated_name );
3192 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3193 msiobj_release( &uirow->hdr );
3195 msi_free( ui_key_str );
3196 msi_free( deformated_name );
3197 return ERROR_SUCCESS;
3200 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3202 static const WCHAR registry_query[] = {
3203 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3204 '`','R','e','g','i','s','t','r','y','`',0};
3205 static const WCHAR remove_registry_query[] = {
3206 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3207 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3208 MSIQUERY *view;
3209 UINT rc;
3211 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3212 if (rc == ERROR_SUCCESS)
3214 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3215 msiobj_release( &view->hdr );
3216 if (rc != ERROR_SUCCESS)
3217 return rc;
3219 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3220 if (rc == ERROR_SUCCESS)
3222 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3223 msiobj_release( &view->hdr );
3224 if (rc != ERROR_SUCCESS)
3225 return rc;
3227 return ERROR_SUCCESS;
3230 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3232 return ERROR_SUCCESS;
3236 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3238 static const WCHAR query[]= {
3239 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3240 '`','R','e','g','i','s','t','r','y','`',0};
3241 MSICOMPONENT *comp;
3242 DWORD total = 0, count = 0;
3243 MSIQUERY *view;
3244 MSIFEATURE *feature;
3245 MSIFILE *file;
3246 UINT rc;
3248 TRACE("InstallValidate\n");
3250 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3251 if (rc == ERROR_SUCCESS)
3253 rc = MSI_IterateRecords( view, &count, NULL, package );
3254 msiobj_release( &view->hdr );
3255 if (rc != ERROR_SUCCESS)
3256 return rc;
3257 total += count * REG_PROGRESS_VALUE;
3259 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3260 total += COMPONENT_PROGRESS_VALUE;
3262 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3263 total += file->FileSize;
3265 msi_ui_progress( package, 0, total, 0, 0 );
3267 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3269 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3270 debugstr_w(feature->Feature), feature->Installed,
3271 feature->ActionRequest, feature->Action);
3273 return ERROR_SUCCESS;
3276 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3278 MSIPACKAGE* package = param;
3279 LPCWSTR cond = NULL;
3280 LPCWSTR message = NULL;
3281 UINT r;
3283 static const WCHAR title[]=
3284 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3286 cond = MSI_RecordGetString(row,1);
3288 r = MSI_EvaluateConditionW(package,cond);
3289 if (r == MSICONDITION_FALSE)
3291 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3293 LPWSTR deformated;
3294 message = MSI_RecordGetString(row,2);
3295 deformat_string(package,message,&deformated);
3296 MessageBoxW(NULL,deformated,title,MB_OK);
3297 msi_free(deformated);
3300 return ERROR_INSTALL_FAILURE;
3303 return ERROR_SUCCESS;
3306 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3308 static const WCHAR query[] = {
3309 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3310 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3311 MSIQUERY *view;
3312 UINT rc;
3314 TRACE("Checking launch conditions\n");
3316 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3317 if (rc != ERROR_SUCCESS)
3318 return ERROR_SUCCESS;
3320 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3321 msiobj_release(&view->hdr);
3322 return rc;
3325 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3328 if (!cmp->KeyPath)
3329 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3331 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3333 static const WCHAR query[] = {
3334 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3335 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3336 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3337 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3338 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3339 MSIRECORD *row;
3340 UINT root, len;
3341 LPWSTR deformated, buffer, deformated_name;
3342 LPCWSTR key, name;
3344 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3345 if (!row)
3346 return NULL;
3348 root = MSI_RecordGetInteger(row,2);
3349 key = MSI_RecordGetString(row, 3);
3350 name = MSI_RecordGetString(row, 4);
3351 deformat_string(package, key , &deformated);
3352 deformat_string(package, name, &deformated_name);
3354 len = strlenW(deformated) + 6;
3355 if (deformated_name)
3356 len+=strlenW(deformated_name);
3358 buffer = msi_alloc( len *sizeof(WCHAR));
3360 if (deformated_name)
3361 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3362 else
3363 sprintfW(buffer,fmt,root,deformated);
3365 msi_free(deformated);
3366 msi_free(deformated_name);
3367 msiobj_release(&row->hdr);
3369 return buffer;
3371 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3373 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3374 return NULL;
3376 else
3378 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3380 if (file)
3381 return strdupW( file->TargetPath );
3383 return NULL;
3386 static HKEY openSharedDLLsKey(void)
3388 HKEY hkey=0;
3389 static const WCHAR path[] =
3390 {'S','o','f','t','w','a','r','e','\\',
3391 'M','i','c','r','o','s','o','f','t','\\',
3392 'W','i','n','d','o','w','s','\\',
3393 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3394 'S','h','a','r','e','d','D','L','L','s',0};
3396 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3397 return hkey;
3400 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3402 HKEY hkey;
3403 DWORD count=0;
3404 DWORD type;
3405 DWORD sz = sizeof(count);
3406 DWORD rc;
3408 hkey = openSharedDLLsKey();
3409 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3410 if (rc != ERROR_SUCCESS)
3411 count = 0;
3412 RegCloseKey(hkey);
3413 return count;
3416 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3418 HKEY hkey;
3420 hkey = openSharedDLLsKey();
3421 if (count > 0)
3422 msi_reg_set_val_dword( hkey, path, count );
3423 else
3424 RegDeleteValueW(hkey,path);
3425 RegCloseKey(hkey);
3426 return count;
3429 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3431 MSIFEATURE *feature;
3432 INT count = 0;
3433 BOOL write = FALSE;
3435 /* only refcount DLLs */
3436 if (comp->KeyPath == NULL ||
3437 comp->assembly ||
3438 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3439 comp->Attributes & msidbComponentAttributesODBCDataSource)
3440 write = FALSE;
3441 else
3443 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3444 write = (count > 0);
3446 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3447 write = TRUE;
3450 /* increment counts */
3451 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3453 ComponentList *cl;
3455 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3456 continue;
3458 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3460 if ( cl->component == comp )
3461 count++;
3465 /* decrement counts */
3466 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3468 ComponentList *cl;
3470 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3471 continue;
3473 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3475 if ( cl->component == comp )
3476 count--;
3480 /* ref count all the files in the component */
3481 if (write)
3483 MSIFILE *file;
3485 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3487 if (file->Component == comp)
3488 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3492 /* add a count for permanent */
3493 if (comp->Attributes & msidbComponentAttributesPermanent)
3494 count ++;
3496 comp->RefCount = count;
3498 if (write)
3499 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3502 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3504 if (comp->assembly)
3506 const WCHAR prefixW[] = {'<','\\',0};
3507 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3508 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3510 if (keypath)
3512 strcpyW( keypath, prefixW );
3513 strcatW( keypath, comp->assembly->display_name );
3515 return keypath;
3517 return resolve_keypath( package, comp );
3520 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3522 WCHAR squashed_pc[SQUASHED_GUID_SIZE], squashed_cc[SQUASHED_GUID_SIZE];
3523 UINT rc;
3524 MSICOMPONENT *comp;
3525 HKEY hkey;
3527 TRACE("\n");
3529 squash_guid( package->ProductCode, squashed_pc );
3530 msi_set_sourcedir_props(package, FALSE);
3532 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3534 MSIRECORD *uirow;
3535 INSTALLSTATE action;
3537 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3538 if (!comp->ComponentId)
3539 continue;
3541 squash_guid( comp->ComponentId, squashed_cc );
3542 msi_free( comp->FullKeypath );
3543 comp->FullKeypath = build_full_keypath( package, comp );
3545 ACTION_RefCountComponent( package, comp );
3547 if (package->need_rollback) action = comp->Installed;
3548 else action = comp->ActionRequest;
3550 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3551 debugstr_w(comp->Component), debugstr_w(squashed_cc),
3552 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3554 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3556 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3557 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3558 else
3559 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3561 if (rc != ERROR_SUCCESS)
3562 continue;
3564 if (comp->Attributes & msidbComponentAttributesPermanent)
3566 static const WCHAR szPermKey[] =
3567 { '0','0','0','0','0','0','0','0','0','0','0','0',
3568 '0','0','0','0','0','0','0','0','0','0','0','0',
3569 '0','0','0','0','0','0','0','0',0 };
3571 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3573 if (action == INSTALLSTATE_LOCAL)
3574 msi_reg_set_val_str( hkey, squashed_pc, comp->FullKeypath );
3575 else
3577 MSIFILE *file;
3578 MSIRECORD *row;
3579 LPWSTR ptr, ptr2;
3580 WCHAR source[MAX_PATH];
3581 WCHAR base[MAX_PATH];
3582 LPWSTR sourcepath;
3584 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3585 static const WCHAR query[] = {
3586 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3587 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3588 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3589 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3590 '`','D','i','s','k','I','d','`',0};
3592 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3593 continue;
3595 if (!(row = MSI_QueryGetRecord(package->db, query, file->Sequence)))
3596 return ERROR_FUNCTION_FAILED;
3598 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3599 ptr2 = strrchrW(source, '\\') + 1;
3600 msiobj_release(&row->hdr);
3602 lstrcpyW(base, package->PackagePath);
3603 ptr = strrchrW(base, '\\');
3604 *(ptr + 1) = '\0';
3606 sourcepath = msi_resolve_file_source(package, file);
3607 ptr = sourcepath + lstrlenW(base);
3608 lstrcpyW(ptr2, ptr);
3609 msi_free(sourcepath);
3611 msi_reg_set_val_str( hkey, squashed_pc, source );
3613 RegCloseKey(hkey);
3615 else if (action == INSTALLSTATE_ABSENT)
3617 if (comp->num_clients <= 0)
3619 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3620 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3621 else
3622 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3624 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3626 else
3628 LONG res;
3630 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3631 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3632 else
3633 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3635 if (rc != ERROR_SUCCESS)
3637 WARN( "failed to open component key %u\n", rc );
3638 continue;
3640 res = RegDeleteValueW( hkey, squashed_pc );
3641 RegCloseKey(hkey);
3642 if (res) WARN( "failed to delete component value %d\n", res );
3646 /* UI stuff */
3647 uirow = MSI_CreateRecord(3);
3648 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3649 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3650 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3651 msi_ui_actiondata( package, szProcessComponents, uirow );
3652 msiobj_release( &uirow->hdr );
3654 return ERROR_SUCCESS;
3657 typedef struct {
3658 CLSID clsid;
3659 LPWSTR source;
3661 LPWSTR path;
3662 ITypeLib *ptLib;
3663 } typelib_struct;
3665 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3666 LPWSTR lpszName, LONG_PTR lParam)
3668 TLIBATTR *attr;
3669 typelib_struct *tl_struct = (typelib_struct*) lParam;
3670 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3671 int sz;
3672 HRESULT res;
3674 if (!IS_INTRESOURCE(lpszName))
3676 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3677 return TRUE;
3680 sz = strlenW(tl_struct->source)+4;
3681 sz *= sizeof(WCHAR);
3683 if ((INT_PTR)lpszName == 1)
3684 tl_struct->path = strdupW(tl_struct->source);
3685 else
3687 tl_struct->path = msi_alloc(sz);
3688 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3691 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3692 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3693 if (FAILED(res))
3695 msi_free(tl_struct->path);
3696 tl_struct->path = NULL;
3698 return TRUE;
3701 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3702 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3704 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3705 return FALSE;
3708 msi_free(tl_struct->path);
3709 tl_struct->path = NULL;
3711 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3712 ITypeLib_Release(tl_struct->ptLib);
3714 return TRUE;
3717 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3719 MSIPACKAGE* package = param;
3720 LPCWSTR component;
3721 MSICOMPONENT *comp;
3722 MSIFILE *file;
3723 typelib_struct tl_struct;
3724 ITypeLib *tlib;
3725 HMODULE module;
3726 HRESULT hr;
3728 component = MSI_RecordGetString(row,3);
3729 comp = msi_get_loaded_component(package,component);
3730 if (!comp)
3731 return ERROR_SUCCESS;
3733 comp->Action = msi_get_component_action( package, comp );
3734 if (comp->Action != INSTALLSTATE_LOCAL)
3736 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3737 return ERROR_SUCCESS;
3740 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3742 TRACE("component has no key path\n");
3743 return ERROR_SUCCESS;
3745 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3747 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3748 if (module)
3750 LPCWSTR guid;
3751 guid = MSI_RecordGetString(row,1);
3752 CLSIDFromString( guid, &tl_struct.clsid);
3753 tl_struct.source = strdupW( file->TargetPath );
3754 tl_struct.path = NULL;
3756 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3757 (LONG_PTR)&tl_struct);
3759 if (tl_struct.path)
3761 LPCWSTR helpid, help_path = NULL;
3762 HRESULT res;
3764 helpid = MSI_RecordGetString(row,6);
3766 if (helpid) help_path = msi_get_target_folder( package, helpid );
3767 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3769 if (FAILED(res))
3770 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3771 else
3772 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3774 ITypeLib_Release(tl_struct.ptLib);
3775 msi_free(tl_struct.path);
3777 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3779 FreeLibrary(module);
3780 msi_free(tl_struct.source);
3782 else
3784 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3785 if (FAILED(hr))
3787 ERR("Failed to load type library: %08x\n", hr);
3788 return ERROR_INSTALL_FAILURE;
3791 ITypeLib_Release(tlib);
3794 return ERROR_SUCCESS;
3797 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3799 static const WCHAR query[] = {
3800 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3801 '`','T','y','p','e','L','i','b','`',0};
3802 MSIQUERY *view;
3803 UINT rc;
3805 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3806 if (rc != ERROR_SUCCESS)
3807 return ERROR_SUCCESS;
3809 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3810 msiobj_release(&view->hdr);
3811 return rc;
3814 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3816 MSIPACKAGE *package = param;
3817 LPCWSTR component, guid;
3818 MSICOMPONENT *comp;
3819 GUID libid;
3820 UINT version;
3821 LCID language;
3822 SYSKIND syskind;
3823 HRESULT hr;
3825 component = MSI_RecordGetString( row, 3 );
3826 comp = msi_get_loaded_component( package, component );
3827 if (!comp)
3828 return ERROR_SUCCESS;
3830 comp->Action = msi_get_component_action( package, comp );
3831 if (comp->Action != INSTALLSTATE_ABSENT)
3833 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3834 return ERROR_SUCCESS;
3836 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3838 guid = MSI_RecordGetString( row, 1 );
3839 CLSIDFromString( guid, &libid );
3840 version = MSI_RecordGetInteger( row, 4 );
3841 language = MSI_RecordGetInteger( row, 2 );
3843 #ifdef _WIN64
3844 syskind = SYS_WIN64;
3845 #else
3846 syskind = SYS_WIN32;
3847 #endif
3849 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3850 if (FAILED(hr))
3852 WARN("Failed to unregister typelib: %08x\n", hr);
3855 return ERROR_SUCCESS;
3858 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3860 static const WCHAR query[] = {
3861 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3862 '`','T','y','p','e','L','i','b','`',0};
3863 MSIQUERY *view;
3864 UINT rc;
3866 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3867 if (rc != ERROR_SUCCESS)
3868 return ERROR_SUCCESS;
3870 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3871 msiobj_release( &view->hdr );
3872 return rc;
3875 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3877 static const WCHAR szlnk[] = {'.','l','n','k',0};
3878 LPCWSTR directory, extension, link_folder;
3879 LPWSTR link_file, filename;
3881 directory = MSI_RecordGetString( row, 2 );
3882 link_folder = msi_get_target_folder( package, directory );
3883 if (!link_folder)
3885 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3886 return NULL;
3888 /* may be needed because of a bug somewhere else */
3889 msi_create_full_path( link_folder );
3891 filename = msi_dup_record_field( row, 3 );
3892 msi_reduce_to_long_filename( filename );
3894 extension = strrchrW( filename, '.' );
3895 if (!extension || strcmpiW( extension, szlnk ))
3897 int len = strlenW( filename );
3898 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3899 memcpy( filename + len, szlnk, sizeof(szlnk) );
3901 link_file = msi_build_directory_name( 2, link_folder, filename );
3902 msi_free( filename );
3904 return link_file;
3907 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3909 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3910 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3911 WCHAR *folder, *dest, *path;
3913 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3914 folder = msi_dup_property( package->db, szWindowsFolder );
3915 else
3917 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3918 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3919 msi_free( appdata );
3921 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3922 msi_create_full_path( dest );
3923 path = msi_build_directory_name( 2, dest, icon_name );
3924 msi_free( folder );
3925 msi_free( dest );
3926 return path;
3929 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3931 MSIPACKAGE *package = param;
3932 LPWSTR link_file, deformated, path;
3933 LPCWSTR component, target;
3934 MSICOMPONENT *comp;
3935 IShellLinkW *sl = NULL;
3936 IPersistFile *pf = NULL;
3937 HRESULT res;
3939 component = MSI_RecordGetString(row, 4);
3940 comp = msi_get_loaded_component(package, component);
3941 if (!comp)
3942 return ERROR_SUCCESS;
3944 comp->Action = msi_get_component_action( package, comp );
3945 if (comp->Action != INSTALLSTATE_LOCAL)
3947 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3948 return ERROR_SUCCESS;
3950 msi_ui_actiondata( package, szCreateShortcuts, row );
3952 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3953 &IID_IShellLinkW, (LPVOID *) &sl );
3955 if (FAILED( res ))
3957 ERR("CLSID_ShellLink not available\n");
3958 goto err;
3961 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3962 if (FAILED( res ))
3964 ERR("QueryInterface(IID_IPersistFile) failed\n");
3965 goto err;
3968 target = MSI_RecordGetString(row, 5);
3969 if (strchrW(target, '['))
3971 deformat_string( package, target, &path );
3972 TRACE("target path is %s\n", debugstr_w(path));
3973 IShellLinkW_SetPath( sl, path );
3974 msi_free( path );
3976 else
3978 FIXME("poorly handled shortcut format, advertised shortcut\n");
3979 path = resolve_keypath( package, comp );
3980 IShellLinkW_SetPath( sl, path );
3981 msi_free( path );
3984 if (!MSI_RecordIsNull(row,6))
3986 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3987 deformat_string(package, arguments, &deformated);
3988 IShellLinkW_SetArguments(sl,deformated);
3989 msi_free(deformated);
3992 if (!MSI_RecordIsNull(row,7))
3994 LPCWSTR description = MSI_RecordGetString(row, 7);
3995 IShellLinkW_SetDescription(sl, description);
3998 if (!MSI_RecordIsNull(row,8))
3999 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
4001 if (!MSI_RecordIsNull(row,9))
4003 INT index;
4004 LPCWSTR icon = MSI_RecordGetString(row, 9);
4006 path = msi_build_icon_path(package, icon);
4007 index = MSI_RecordGetInteger(row,10);
4009 /* no value means 0 */
4010 if (index == MSI_NULL_INTEGER)
4011 index = 0;
4013 IShellLinkW_SetIconLocation(sl, path, index);
4014 msi_free(path);
4017 if (!MSI_RecordIsNull(row,11))
4018 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
4020 if (!MSI_RecordIsNull(row,12))
4022 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
4023 full_path = msi_get_target_folder( package, wkdir );
4024 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
4026 link_file = get_link_file(package, row);
4028 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
4029 IPersistFile_Save(pf, link_file, FALSE);
4030 msi_free(link_file);
4032 err:
4033 if (pf)
4034 IPersistFile_Release( pf );
4035 if (sl)
4036 IShellLinkW_Release( sl );
4038 return ERROR_SUCCESS;
4041 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4043 static const WCHAR query[] = {
4044 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4045 '`','S','h','o','r','t','c','u','t','`',0};
4046 MSIQUERY *view;
4047 HRESULT res;
4048 UINT rc;
4050 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4051 if (rc != ERROR_SUCCESS)
4052 return ERROR_SUCCESS;
4054 res = CoInitialize( NULL );
4056 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4057 msiobj_release(&view->hdr);
4059 if (SUCCEEDED(res)) CoUninitialize();
4060 return rc;
4063 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4065 MSIPACKAGE *package = param;
4066 LPWSTR link_file;
4067 LPCWSTR component;
4068 MSICOMPONENT *comp;
4070 component = MSI_RecordGetString( row, 4 );
4071 comp = msi_get_loaded_component( package, component );
4072 if (!comp)
4073 return ERROR_SUCCESS;
4075 comp->Action = msi_get_component_action( package, comp );
4076 if (comp->Action != INSTALLSTATE_ABSENT)
4078 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4079 return ERROR_SUCCESS;
4081 msi_ui_actiondata( package, szRemoveShortcuts, row );
4083 link_file = get_link_file( package, row );
4085 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4086 if (!DeleteFileW( link_file ))
4088 WARN("Failed to remove shortcut file %u\n", GetLastError());
4090 msi_free( link_file );
4092 return ERROR_SUCCESS;
4095 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4097 static const WCHAR query[] = {
4098 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4099 '`','S','h','o','r','t','c','u','t','`',0};
4100 MSIQUERY *view;
4101 UINT rc;
4103 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4104 if (rc != ERROR_SUCCESS)
4105 return ERROR_SUCCESS;
4107 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4108 msiobj_release( &view->hdr );
4109 return rc;
4112 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4114 MSIPACKAGE* package = param;
4115 HANDLE the_file;
4116 LPWSTR FilePath;
4117 LPCWSTR FileName;
4118 CHAR buffer[1024];
4119 DWORD sz;
4120 UINT rc;
4122 FileName = MSI_RecordGetString(row,1);
4123 if (!FileName)
4125 ERR("Unable to get FileName\n");
4126 return ERROR_SUCCESS;
4129 FilePath = msi_build_icon_path(package, FileName);
4131 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4133 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4134 FILE_ATTRIBUTE_NORMAL, NULL);
4136 if (the_file == INVALID_HANDLE_VALUE)
4138 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4139 msi_free(FilePath);
4140 return ERROR_SUCCESS;
4145 DWORD write;
4146 sz = 1024;
4147 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4148 if (rc != ERROR_SUCCESS)
4150 ERR("Failed to get stream\n");
4151 DeleteFileW(FilePath);
4152 break;
4154 WriteFile(the_file,buffer,sz,&write,NULL);
4155 } while (sz == 1024);
4157 msi_free(FilePath);
4158 CloseHandle(the_file);
4160 return ERROR_SUCCESS;
4163 static UINT msi_publish_icons(MSIPACKAGE *package)
4165 static const WCHAR query[]= {
4166 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4167 '`','I','c','o','n','`',0};
4168 MSIQUERY *view;
4169 UINT r;
4171 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4172 if (r == ERROR_SUCCESS)
4174 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4175 msiobj_release(&view->hdr);
4176 if (r != ERROR_SUCCESS)
4177 return r;
4179 return ERROR_SUCCESS;
4182 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4184 UINT r;
4185 HKEY source;
4186 LPWSTR buffer;
4187 MSIMEDIADISK *disk;
4188 MSISOURCELISTINFO *info;
4190 r = RegCreateKeyW(hkey, szSourceList, &source);
4191 if (r != ERROR_SUCCESS)
4192 return r;
4194 RegCloseKey(source);
4196 buffer = strrchrW(package->PackagePath, '\\') + 1;
4197 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4198 package->Context, MSICODE_PRODUCT,
4199 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4200 if (r != ERROR_SUCCESS)
4201 return r;
4203 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4204 package->Context, MSICODE_PRODUCT,
4205 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4206 if (r != ERROR_SUCCESS)
4207 return r;
4209 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4210 package->Context, MSICODE_PRODUCT,
4211 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4212 if (r != ERROR_SUCCESS)
4213 return r;
4215 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4217 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4218 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4219 info->options, info->value);
4220 else
4221 MsiSourceListSetInfoW(package->ProductCode, NULL,
4222 info->context, info->options,
4223 info->property, info->value);
4226 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4228 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4229 disk->context, disk->options,
4230 disk->disk_id, disk->volume_label, disk->disk_prompt);
4233 return ERROR_SUCCESS;
4236 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4238 static const WCHAR szARPProductIcon[] =
4239 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4240 static const WCHAR szAssignment[] =
4241 {'A','s','s','i','g','n','m','e','n','t',0};
4242 static const WCHAR szAdvertiseFlags[] =
4243 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4244 static const WCHAR szClients[] =
4245 {'C','l','i','e','n','t','s',0};
4246 static const WCHAR szColon[] = {':',0};
4247 MSIHANDLE hdb, suminfo;
4248 WCHAR *buffer, *ptr, guids[MAX_PATH], packcode[SQUASHED_GUID_SIZE];
4249 DWORD langid, size;
4250 UINT r;
4252 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4253 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4254 msi_free(buffer);
4256 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4257 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4259 /* FIXME */
4260 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4262 buffer = msi_dup_property(package->db, szARPProductIcon);
4263 if (buffer)
4265 LPWSTR path = msi_build_icon_path(package, buffer);
4266 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4267 msi_free(path);
4268 msi_free(buffer);
4271 buffer = msi_dup_property(package->db, szProductVersion);
4272 if (buffer)
4274 DWORD verdword = msi_version_str_to_dword(buffer);
4275 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4276 msi_free(buffer);
4279 msi_reg_set_val_dword(hkey, szAssignment, 0);
4280 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4281 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4282 msi_reg_set_val_str(hkey, szClients, szColon);
4284 hdb = alloc_msihandle(&package->db->hdr);
4285 if (!hdb)
4286 return ERROR_NOT_ENOUGH_MEMORY;
4288 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4289 MsiCloseHandle(hdb);
4290 if (r != ERROR_SUCCESS)
4291 goto done;
4293 size = MAX_PATH;
4294 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4295 NULL, guids, &size);
4296 if (r != ERROR_SUCCESS)
4297 goto done;
4299 ptr = strchrW(guids, ';');
4300 if (ptr) *ptr = 0;
4301 squash_guid(guids, packcode);
4302 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4304 done:
4305 MsiCloseHandle(suminfo);
4306 return ERROR_SUCCESS;
4309 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4311 UINT r;
4312 HKEY hkey;
4313 WCHAR *upgrade, squashed_pc[SQUASHED_GUID_SIZE];
4315 upgrade = msi_dup_property(package->db, szUpgradeCode);
4316 if (!upgrade)
4317 return ERROR_SUCCESS;
4319 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4320 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4321 else
4322 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4324 if (r != ERROR_SUCCESS)
4326 WARN("failed to open upgrade code key\n");
4327 msi_free(upgrade);
4328 return ERROR_SUCCESS;
4330 squash_guid(package->ProductCode, squashed_pc);
4331 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4332 RegCloseKey(hkey);
4333 msi_free(upgrade);
4334 return ERROR_SUCCESS;
4337 static BOOL msi_check_publish(MSIPACKAGE *package)
4339 MSIFEATURE *feature;
4341 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4343 feature->Action = msi_get_feature_action( package, feature );
4344 if (feature->Action == INSTALLSTATE_LOCAL)
4345 return TRUE;
4348 return FALSE;
4351 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4353 MSIFEATURE *feature;
4355 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4357 feature->Action = msi_get_feature_action( package, feature );
4358 if (feature->Action != INSTALLSTATE_ABSENT)
4359 return FALSE;
4362 return TRUE;
4365 static UINT msi_publish_patches( MSIPACKAGE *package )
4367 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4368 WCHAR patch_squashed[GUID_SIZE];
4369 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4370 LONG res;
4371 MSIPATCHINFO *patch;
4372 UINT r;
4373 WCHAR *p, *all_patches = NULL;
4374 DWORD len = 0;
4376 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4377 if (r != ERROR_SUCCESS)
4378 return ERROR_FUNCTION_FAILED;
4380 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4381 if (res != ERROR_SUCCESS)
4383 r = ERROR_FUNCTION_FAILED;
4384 goto done;
4387 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4388 if (r != ERROR_SUCCESS)
4389 goto done;
4391 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4393 squash_guid( patch->patchcode, patch_squashed );
4394 len += strlenW( patch_squashed ) + 1;
4397 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4398 if (!all_patches)
4399 goto done;
4401 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4403 HKEY patch_key;
4405 squash_guid( patch->patchcode, p );
4406 p += strlenW( p ) + 1;
4408 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4409 (const BYTE *)patch->transforms,
4410 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4411 if (res != ERROR_SUCCESS)
4412 goto done;
4414 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4415 if (r != ERROR_SUCCESS)
4416 goto done;
4418 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4419 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4420 RegCloseKey( patch_key );
4421 if (res != ERROR_SUCCESS)
4422 goto done;
4424 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4426 res = GetLastError();
4427 ERR("Unable to copy patch package %d\n", res);
4428 goto done;
4430 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4431 if (res != ERROR_SUCCESS)
4432 goto done;
4434 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state,
4435 sizeof(patch->state) );
4436 if (res != ERROR_SUCCESS)
4438 RegCloseKey( patch_key );
4439 goto done;
4442 res = RegSetValueExW( patch_key, szUninstallable, 0, REG_DWORD, (const BYTE *)&patch->uninstallable,
4443 sizeof(patch->uninstallable) );
4444 RegCloseKey( patch_key );
4445 if (res != ERROR_SUCCESS)
4446 goto done;
4449 all_patches[len] = 0;
4450 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4451 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4452 if (res != ERROR_SUCCESS)
4453 goto done;
4455 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4456 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4457 if (res != ERROR_SUCCESS)
4458 r = ERROR_FUNCTION_FAILED;
4460 done:
4461 RegCloseKey( product_patches_key );
4462 RegCloseKey( patches_key );
4463 RegCloseKey( product_key );
4464 msi_free( all_patches );
4465 return r;
4468 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4470 UINT rc;
4471 HKEY hukey = NULL, hudkey = NULL;
4472 MSIRECORD *uirow;
4474 if (!list_empty(&package->patches))
4476 rc = msi_publish_patches(package);
4477 if (rc != ERROR_SUCCESS)
4478 goto end;
4481 /* FIXME: also need to publish if the product is in advertise mode */
4482 if (!msi_check_publish(package))
4483 return ERROR_SUCCESS;
4485 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4486 &hukey, TRUE);
4487 if (rc != ERROR_SUCCESS)
4488 goto end;
4490 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4491 NULL, &hudkey, TRUE);
4492 if (rc != ERROR_SUCCESS)
4493 goto end;
4495 rc = msi_publish_upgrade_code(package);
4496 if (rc != ERROR_SUCCESS)
4497 goto end;
4499 rc = msi_publish_product_properties(package, hukey);
4500 if (rc != ERROR_SUCCESS)
4501 goto end;
4503 rc = msi_publish_sourcelist(package, hukey);
4504 if (rc != ERROR_SUCCESS)
4505 goto end;
4507 rc = msi_publish_icons(package);
4509 end:
4510 uirow = MSI_CreateRecord( 1 );
4511 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4512 msi_ui_actiondata( package, szPublishProduct, uirow );
4513 msiobj_release( &uirow->hdr );
4515 RegCloseKey(hukey);
4516 RegCloseKey(hudkey);
4517 return rc;
4520 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4522 WCHAR *filename, *ptr, *folder, *ret;
4523 const WCHAR *dirprop;
4525 filename = msi_dup_record_field( row, 2 );
4526 if (filename && (ptr = strchrW( filename, '|' )))
4527 ptr++;
4528 else
4529 ptr = filename;
4531 dirprop = MSI_RecordGetString( row, 3 );
4532 if (dirprop)
4534 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4535 if (!folder) folder = msi_dup_property( package->db, dirprop );
4537 else
4538 folder = msi_dup_property( package->db, szWindowsFolder );
4540 if (!folder)
4542 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4543 msi_free( filename );
4544 return NULL;
4547 ret = msi_build_directory_name( 2, folder, ptr );
4549 msi_free( filename );
4550 msi_free( folder );
4551 return ret;
4554 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4556 MSIPACKAGE *package = param;
4557 LPCWSTR component, section, key, value, identifier;
4558 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4559 MSIRECORD * uirow;
4560 INT action;
4561 MSICOMPONENT *comp;
4563 component = MSI_RecordGetString(row, 8);
4564 comp = msi_get_loaded_component(package,component);
4565 if (!comp)
4566 return ERROR_SUCCESS;
4568 comp->Action = msi_get_component_action( package, comp );
4569 if (comp->Action != INSTALLSTATE_LOCAL)
4571 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4572 return ERROR_SUCCESS;
4575 identifier = MSI_RecordGetString(row,1);
4576 section = MSI_RecordGetString(row,4);
4577 key = MSI_RecordGetString(row,5);
4578 value = MSI_RecordGetString(row,6);
4579 action = MSI_RecordGetInteger(row,7);
4581 deformat_string(package,section,&deformated_section);
4582 deformat_string(package,key,&deformated_key);
4583 deformat_string(package,value,&deformated_value);
4585 fullname = get_ini_file_name(package, row);
4587 if (action == 0)
4589 TRACE("Adding value %s to section %s in %s\n",
4590 debugstr_w(deformated_key), debugstr_w(deformated_section),
4591 debugstr_w(fullname));
4592 WritePrivateProfileStringW(deformated_section, deformated_key,
4593 deformated_value, fullname);
4595 else if (action == 1)
4597 WCHAR returned[10];
4598 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4599 returned, 10, fullname);
4600 if (returned[0] == 0)
4602 TRACE("Adding value %s to section %s in %s\n",
4603 debugstr_w(deformated_key), debugstr_w(deformated_section),
4604 debugstr_w(fullname));
4606 WritePrivateProfileStringW(deformated_section, deformated_key,
4607 deformated_value, fullname);
4610 else if (action == 3)
4611 FIXME("Append to existing section not yet implemented\n");
4613 uirow = MSI_CreateRecord(4);
4614 MSI_RecordSetStringW(uirow,1,identifier);
4615 MSI_RecordSetStringW(uirow,2,deformated_section);
4616 MSI_RecordSetStringW(uirow,3,deformated_key);
4617 MSI_RecordSetStringW(uirow,4,deformated_value);
4618 msi_ui_actiondata( package, szWriteIniValues, uirow );
4619 msiobj_release( &uirow->hdr );
4621 msi_free(fullname);
4622 msi_free(deformated_key);
4623 msi_free(deformated_value);
4624 msi_free(deformated_section);
4625 return ERROR_SUCCESS;
4628 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4630 static const WCHAR query[] = {
4631 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4632 '`','I','n','i','F','i','l','e','`',0};
4633 MSIQUERY *view;
4634 UINT rc;
4636 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4637 if (rc != ERROR_SUCCESS)
4638 return ERROR_SUCCESS;
4640 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4641 msiobj_release(&view->hdr);
4642 return rc;
4645 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4647 MSIPACKAGE *package = param;
4648 LPCWSTR component, section, key, value, identifier;
4649 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4650 MSICOMPONENT *comp;
4651 MSIRECORD *uirow;
4652 INT action;
4654 component = MSI_RecordGetString( row, 8 );
4655 comp = msi_get_loaded_component( package, component );
4656 if (!comp)
4657 return ERROR_SUCCESS;
4659 comp->Action = msi_get_component_action( package, comp );
4660 if (comp->Action != INSTALLSTATE_ABSENT)
4662 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4663 return ERROR_SUCCESS;
4666 identifier = MSI_RecordGetString( row, 1 );
4667 section = MSI_RecordGetString( row, 4 );
4668 key = MSI_RecordGetString( row, 5 );
4669 value = MSI_RecordGetString( row, 6 );
4670 action = MSI_RecordGetInteger( row, 7 );
4672 deformat_string( package, section, &deformated_section );
4673 deformat_string( package, key, &deformated_key );
4674 deformat_string( package, value, &deformated_value );
4676 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4678 filename = get_ini_file_name( package, row );
4680 TRACE("Removing key %s from section %s in %s\n",
4681 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4683 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4685 WARN("Unable to remove key %u\n", GetLastError());
4687 msi_free( filename );
4689 else
4690 FIXME("Unsupported action %d\n", action);
4693 uirow = MSI_CreateRecord( 4 );
4694 MSI_RecordSetStringW( uirow, 1, identifier );
4695 MSI_RecordSetStringW( uirow, 2, deformated_section );
4696 MSI_RecordSetStringW( uirow, 3, deformated_key );
4697 MSI_RecordSetStringW( uirow, 4, deformated_value );
4698 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4699 msiobj_release( &uirow->hdr );
4701 msi_free( deformated_key );
4702 msi_free( deformated_value );
4703 msi_free( deformated_section );
4704 return ERROR_SUCCESS;
4707 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4709 MSIPACKAGE *package = param;
4710 LPCWSTR component, section, key, value, identifier;
4711 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4712 MSICOMPONENT *comp;
4713 MSIRECORD *uirow;
4714 INT action;
4716 component = MSI_RecordGetString( row, 8 );
4717 comp = msi_get_loaded_component( package, component );
4718 if (!comp)
4719 return ERROR_SUCCESS;
4721 comp->Action = msi_get_component_action( package, comp );
4722 if (comp->Action != INSTALLSTATE_LOCAL)
4724 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4725 return ERROR_SUCCESS;
4728 identifier = MSI_RecordGetString( row, 1 );
4729 section = MSI_RecordGetString( row, 4 );
4730 key = MSI_RecordGetString( row, 5 );
4731 value = MSI_RecordGetString( row, 6 );
4732 action = MSI_RecordGetInteger( row, 7 );
4734 deformat_string( package, section, &deformated_section );
4735 deformat_string( package, key, &deformated_key );
4736 deformat_string( package, value, &deformated_value );
4738 if (action == msidbIniFileActionRemoveLine)
4740 filename = get_ini_file_name( package, row );
4742 TRACE("Removing key %s from section %s in %s\n",
4743 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4745 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4747 WARN("Unable to remove key %u\n", GetLastError());
4749 msi_free( filename );
4751 else
4752 FIXME("Unsupported action %d\n", action);
4754 uirow = MSI_CreateRecord( 4 );
4755 MSI_RecordSetStringW( uirow, 1, identifier );
4756 MSI_RecordSetStringW( uirow, 2, deformated_section );
4757 MSI_RecordSetStringW( uirow, 3, deformated_key );
4758 MSI_RecordSetStringW( uirow, 4, deformated_value );
4759 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4760 msiobj_release( &uirow->hdr );
4762 msi_free( deformated_key );
4763 msi_free( deformated_value );
4764 msi_free( deformated_section );
4765 return ERROR_SUCCESS;
4768 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4770 static const WCHAR query[] = {
4771 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4772 '`','I','n','i','F','i','l','e','`',0};
4773 static const WCHAR remove_query[] = {
4774 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4775 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4776 MSIQUERY *view;
4777 UINT rc;
4779 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4780 if (rc == ERROR_SUCCESS)
4782 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4783 msiobj_release( &view->hdr );
4784 if (rc != ERROR_SUCCESS)
4785 return rc;
4787 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4788 if (rc == ERROR_SUCCESS)
4790 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4791 msiobj_release( &view->hdr );
4792 if (rc != ERROR_SUCCESS)
4793 return rc;
4795 return ERROR_SUCCESS;
4798 static void register_dll( const WCHAR *dll, BOOL unregister )
4800 static const WCHAR regW[] =
4801 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4802 static const WCHAR unregW[] =
4803 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4804 PROCESS_INFORMATION pi;
4805 STARTUPINFOW si;
4806 WCHAR *cmd;
4808 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4810 if (unregister) sprintfW( cmd, unregW, dll );
4811 else sprintfW( cmd, regW, dll );
4813 memset( &si, 0, sizeof(STARTUPINFOW) );
4814 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4816 CloseHandle( pi.hThread );
4817 msi_dialog_check_messages( pi.hProcess );
4818 CloseHandle( pi.hProcess );
4820 msi_free( cmd );
4823 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4825 MSIPACKAGE *package = param;
4826 LPCWSTR filename;
4827 MSIFILE *file;
4828 MSIRECORD *uirow;
4830 filename = MSI_RecordGetString( row, 1 );
4831 file = msi_get_loaded_file( package, filename );
4832 if (!file)
4834 WARN("unable to find file %s\n", debugstr_w(filename));
4835 return ERROR_SUCCESS;
4837 file->Component->Action = msi_get_component_action( package, file->Component );
4838 if (file->Component->Action != INSTALLSTATE_LOCAL)
4840 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4841 return ERROR_SUCCESS;
4844 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4845 register_dll( file->TargetPath, FALSE );
4847 uirow = MSI_CreateRecord( 2 );
4848 MSI_RecordSetStringW( uirow, 1, file->File );
4849 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4850 msi_ui_actiondata( package, szSelfRegModules, uirow );
4851 msiobj_release( &uirow->hdr );
4853 return ERROR_SUCCESS;
4856 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4858 static const WCHAR query[] = {
4859 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4860 '`','S','e','l','f','R','e','g','`',0};
4861 MSIQUERY *view;
4862 UINT rc;
4864 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4865 if (rc != ERROR_SUCCESS)
4866 return ERROR_SUCCESS;
4868 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4869 msiobj_release(&view->hdr);
4870 return rc;
4873 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4875 MSIPACKAGE *package = param;
4876 LPCWSTR filename;
4877 MSIFILE *file;
4878 MSIRECORD *uirow;
4880 filename = MSI_RecordGetString( row, 1 );
4881 file = msi_get_loaded_file( package, filename );
4882 if (!file)
4884 WARN("unable to find file %s\n", debugstr_w(filename));
4885 return ERROR_SUCCESS;
4887 file->Component->Action = msi_get_component_action( package, file->Component );
4888 if (file->Component->Action != INSTALLSTATE_ABSENT)
4890 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4891 return ERROR_SUCCESS;
4894 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4895 register_dll( file->TargetPath, TRUE );
4897 uirow = MSI_CreateRecord( 2 );
4898 MSI_RecordSetStringW( uirow, 1, file->File );
4899 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4900 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4901 msiobj_release( &uirow->hdr );
4903 return ERROR_SUCCESS;
4906 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4908 static const WCHAR query[] = {
4909 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4910 '`','S','e','l','f','R','e','g','`',0};
4911 MSIQUERY *view;
4912 UINT rc;
4914 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4915 if (rc != ERROR_SUCCESS)
4916 return ERROR_SUCCESS;
4918 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4919 msiobj_release( &view->hdr );
4920 return rc;
4923 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4925 MSIFEATURE *feature;
4926 UINT rc;
4927 HKEY hkey = NULL, userdata = NULL;
4929 if (!msi_check_publish(package))
4930 return ERROR_SUCCESS;
4932 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4933 &hkey, TRUE);
4934 if (rc != ERROR_SUCCESS)
4935 goto end;
4937 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4938 &userdata, TRUE);
4939 if (rc != ERROR_SUCCESS)
4940 goto end;
4942 /* here the guids are base 85 encoded */
4943 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4945 ComponentList *cl;
4946 LPWSTR data = NULL;
4947 GUID clsid;
4948 INT size;
4949 BOOL absent = FALSE;
4950 MSIRECORD *uirow;
4952 if (feature->Level <= 0) continue;
4954 if (feature->Action != INSTALLSTATE_LOCAL &&
4955 feature->Action != INSTALLSTATE_SOURCE &&
4956 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4958 size = 1;
4959 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4961 size += 21;
4963 if (feature->Feature_Parent)
4964 size += strlenW( feature->Feature_Parent )+2;
4966 data = msi_alloc(size * sizeof(WCHAR));
4968 data[0] = 0;
4969 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4971 MSICOMPONENT* component = cl->component;
4972 WCHAR buf[21];
4974 buf[0] = 0;
4975 if (component->ComponentId)
4977 TRACE("From %s\n",debugstr_w(component->ComponentId));
4978 CLSIDFromString(component->ComponentId, &clsid);
4979 encode_base85_guid(&clsid,buf);
4980 TRACE("to %s\n",debugstr_w(buf));
4981 strcatW(data,buf);
4985 if (feature->Feature_Parent)
4987 static const WCHAR sep[] = {'\2',0};
4988 strcatW(data,sep);
4989 strcatW(data,feature->Feature_Parent);
4992 msi_reg_set_val_str( userdata, feature->Feature, data );
4993 msi_free(data);
4995 size = 0;
4996 if (feature->Feature_Parent)
4997 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4998 if (!absent)
5000 size += sizeof(WCHAR);
5001 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5002 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
5004 else
5006 size += 2*sizeof(WCHAR);
5007 data = msi_alloc(size);
5008 data[0] = 0x6;
5009 data[1] = 0;
5010 if (feature->Feature_Parent)
5011 strcpyW( &data[1], feature->Feature_Parent );
5012 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5013 (LPBYTE)data,size);
5014 msi_free(data);
5017 /* the UI chunk */
5018 uirow = MSI_CreateRecord( 1 );
5019 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5020 msi_ui_actiondata( package, szPublishFeatures, uirow );
5021 msiobj_release( &uirow->hdr );
5022 /* FIXME: call msi_ui_progress? */
5025 end:
5026 RegCloseKey(hkey);
5027 RegCloseKey(userdata);
5028 return rc;
5031 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
5033 UINT r;
5034 HKEY hkey;
5035 MSIRECORD *uirow;
5037 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
5039 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
5040 &hkey, FALSE);
5041 if (r == ERROR_SUCCESS)
5043 RegDeleteValueW(hkey, feature->Feature);
5044 RegCloseKey(hkey);
5047 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5048 &hkey, FALSE);
5049 if (r == ERROR_SUCCESS)
5051 RegDeleteValueW(hkey, feature->Feature);
5052 RegCloseKey(hkey);
5055 uirow = MSI_CreateRecord( 1 );
5056 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5057 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
5058 msiobj_release( &uirow->hdr );
5060 return ERROR_SUCCESS;
5063 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5065 MSIFEATURE *feature;
5067 if (!msi_check_unpublish(package))
5068 return ERROR_SUCCESS;
5070 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5072 msi_unpublish_feature(package, feature);
5075 return ERROR_SUCCESS;
5078 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5080 SYSTEMTIME systime;
5081 DWORD size, langid;
5082 WCHAR date[9], *val, *buffer;
5083 const WCHAR *prop, *key;
5085 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5086 static const WCHAR modpath_fmt[] =
5087 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5088 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5089 static const WCHAR szModifyPath[] =
5090 {'M','o','d','i','f','y','P','a','t','h',0};
5091 static const WCHAR szUninstallString[] =
5092 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5093 static const WCHAR szEstimatedSize[] =
5094 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5095 static const WCHAR szDisplayVersion[] =
5096 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5097 static const WCHAR szInstallSource[] =
5098 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5099 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5100 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5101 static const WCHAR szAuthorizedCDFPrefix[] =
5102 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5103 static const WCHAR szARPCONTACT[] =
5104 {'A','R','P','C','O','N','T','A','C','T',0};
5105 static const WCHAR szContact[] =
5106 {'C','o','n','t','a','c','t',0};
5107 static const WCHAR szARPCOMMENTS[] =
5108 {'A','R','P','C','O','M','M','E','N','T','S',0};
5109 static const WCHAR szComments[] =
5110 {'C','o','m','m','e','n','t','s',0};
5111 static const WCHAR szProductName[] =
5112 {'P','r','o','d','u','c','t','N','a','m','e',0};
5113 static const WCHAR szDisplayName[] =
5114 {'D','i','s','p','l','a','y','N','a','m','e',0};
5115 static const WCHAR szARPHELPLINK[] =
5116 {'A','R','P','H','E','L','P','L','I','N','K',0};
5117 static const WCHAR szHelpLink[] =
5118 {'H','e','l','p','L','i','n','k',0};
5119 static const WCHAR szARPHELPTELEPHONE[] =
5120 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5121 static const WCHAR szHelpTelephone[] =
5122 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5123 static const WCHAR szARPINSTALLLOCATION[] =
5124 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5125 static const WCHAR szManufacturer[] =
5126 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5127 static const WCHAR szPublisher[] =
5128 {'P','u','b','l','i','s','h','e','r',0};
5129 static const WCHAR szARPREADME[] =
5130 {'A','R','P','R','E','A','D','M','E',0};
5131 static const WCHAR szReadme[] =
5132 {'R','e','a','d','M','e',0};
5133 static const WCHAR szARPSIZE[] =
5134 {'A','R','P','S','I','Z','E',0};
5135 static const WCHAR szSize[] =
5136 {'S','i','z','e',0};
5137 static const WCHAR szARPURLINFOABOUT[] =
5138 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5139 static const WCHAR szURLInfoAbout[] =
5140 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5141 static const WCHAR szARPURLUPDATEINFO[] =
5142 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5143 static const WCHAR szURLUpdateInfo[] =
5144 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5145 static const WCHAR szARPSYSTEMCOMPONENT[] =
5146 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5147 static const WCHAR szSystemComponent[] =
5148 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5150 static const WCHAR *propval[] = {
5151 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5152 szARPCONTACT, szContact,
5153 szARPCOMMENTS, szComments,
5154 szProductName, szDisplayName,
5155 szARPHELPLINK, szHelpLink,
5156 szARPHELPTELEPHONE, szHelpTelephone,
5157 szARPINSTALLLOCATION, szInstallLocation,
5158 szSourceDir, szInstallSource,
5159 szManufacturer, szPublisher,
5160 szARPREADME, szReadme,
5161 szARPSIZE, szSize,
5162 szARPURLINFOABOUT, szURLInfoAbout,
5163 szARPURLUPDATEINFO, szURLUpdateInfo,
5164 NULL
5166 const WCHAR **p = propval;
5168 while (*p)
5170 prop = *p++;
5171 key = *p++;
5172 val = msi_dup_property(package->db, prop);
5173 msi_reg_set_val_str(hkey, key, val);
5174 msi_free(val);
5177 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5178 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5180 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5182 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5183 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5184 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5185 msi_free(buffer);
5187 /* FIXME: Write real Estimated Size when we have it */
5188 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5190 GetLocalTime(&systime);
5191 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5192 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5194 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5195 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5197 buffer = msi_dup_property(package->db, szProductVersion);
5198 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5199 if (buffer)
5201 DWORD verdword = msi_version_str_to_dword(buffer);
5203 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5204 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5205 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5206 msi_free(buffer);
5209 return ERROR_SUCCESS;
5212 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5214 WCHAR *upgrade_code, squashed_pc[SQUASHED_GUID_SIZE];
5215 MSIRECORD *uirow;
5216 HKEY hkey, props, upgrade_key;
5217 UINT rc;
5219 /* FIXME: also need to publish if the product is in advertise mode */
5220 if (!msi_check_publish(package))
5221 return ERROR_SUCCESS;
5223 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5224 if (rc != ERROR_SUCCESS)
5225 return rc;
5227 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5228 if (rc != ERROR_SUCCESS)
5229 goto done;
5231 rc = msi_publish_install_properties(package, hkey);
5232 if (rc != ERROR_SUCCESS)
5233 goto done;
5235 rc = msi_publish_install_properties(package, props);
5236 if (rc != ERROR_SUCCESS)
5237 goto done;
5239 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5240 if (upgrade_code)
5242 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5243 if (rc == ERROR_SUCCESS)
5245 squash_guid( package->ProductCode, squashed_pc );
5246 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5247 RegCloseKey( upgrade_key );
5249 msi_free( upgrade_code );
5251 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5252 package->delete_on_close = FALSE;
5254 done:
5255 uirow = MSI_CreateRecord( 1 );
5256 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5257 msi_ui_actiondata( package, szRegisterProduct, uirow );
5258 msiobj_release( &uirow->hdr );
5260 RegCloseKey(hkey);
5261 return ERROR_SUCCESS;
5264 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5266 return execute_script(package, SCRIPT_INSTALL);
5269 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5271 MSIPACKAGE *package = param;
5272 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5273 WCHAR *p, *icon_path;
5275 if (!icon) return ERROR_SUCCESS;
5276 if ((icon_path = msi_build_icon_path( package, icon )))
5278 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5279 DeleteFileW( icon_path );
5280 if ((p = strrchrW( icon_path, '\\' )))
5282 *p = 0;
5283 RemoveDirectoryW( icon_path );
5285 msi_free( icon_path );
5287 return ERROR_SUCCESS;
5290 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5292 static const WCHAR query[]= {
5293 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5294 MSIQUERY *view;
5295 UINT r;
5297 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5298 if (r == ERROR_SUCCESS)
5300 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5301 msiobj_release( &view->hdr );
5302 if (r != ERROR_SUCCESS)
5303 return r;
5305 return ERROR_SUCCESS;
5308 static void remove_product_upgrade_code( MSIPACKAGE *package )
5310 WCHAR *code, product[SQUASHED_GUID_SIZE];
5311 HKEY hkey;
5312 LONG res;
5313 DWORD count;
5315 squash_guid( package->ProductCode, product );
5316 if (!(code = msi_dup_property( package->db, szUpgradeCode )))
5318 WARN( "upgrade code not found\n" );
5319 return;
5321 if (!MSIREG_OpenUpgradeCodesKey( code, &hkey, FALSE ))
5323 RegDeleteValueW( hkey, product );
5324 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5325 RegCloseKey( hkey );
5326 if (!res && !count) MSIREG_DeleteUpgradeCodesKey( code );
5328 if (!MSIREG_OpenUserUpgradeCodesKey( code, &hkey, FALSE ))
5330 RegDeleteValueW( hkey, product );
5331 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5332 RegCloseKey( hkey );
5333 if (!res && !count) MSIREG_DeleteUserUpgradeCodesKey( code );
5335 if (!MSIREG_OpenClassesUpgradeCodesKey( code, &hkey, FALSE ))
5337 RegDeleteValueW( hkey, product );
5338 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5339 RegCloseKey( hkey );
5340 if (!res && !count) MSIREG_DeleteClassesUpgradeCodesKey( code );
5343 msi_free( code );
5346 static UINT ACTION_UnpublishProduct(MSIPACKAGE *package)
5348 MSIPATCHINFO *patch;
5350 MSIREG_DeleteProductKey(package->ProductCode);
5351 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5352 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5354 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5355 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5356 MSIREG_DeleteUserProductKey(package->ProductCode);
5357 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5359 remove_product_upgrade_code( package );
5361 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5363 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5364 if (!strcmpW( package->ProductCode, patch->products ))
5366 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5367 patch->delete_on_close = TRUE;
5369 /* FIXME: remove local patch package if this is the last product */
5371 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5372 package->delete_on_close = TRUE;
5374 msi_unpublish_icons( package );
5375 return ERROR_SUCCESS;
5378 static BOOL is_full_uninstall( MSIPACKAGE *package )
5380 WCHAR **features, *remove = msi_dup_property( package->db, szRemove );
5381 MSIFEATURE *feature;
5382 BOOL ret = TRUE;
5383 UINT i;
5385 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5387 if (feature->Action == INSTALLSTATE_LOCAL) ret = FALSE;
5390 features = msi_split_string( remove, ',' );
5391 for (i = 0; features && features[i]; i++)
5393 if (!strcmpW( features[i], szAll )) ret = TRUE;
5396 msi_free(features);
5397 msi_free(remove);
5398 return ret;
5401 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5403 UINT rc;
5405 /* first do the same as an InstallExecute */
5406 rc = execute_script(package, SCRIPT_INSTALL);
5407 if (rc != ERROR_SUCCESS)
5408 return rc;
5410 /* then handle commit actions */
5411 rc = execute_script(package, SCRIPT_COMMIT);
5412 if (rc != ERROR_SUCCESS)
5413 return rc;
5415 if (is_full_uninstall(package))
5416 rc = ACTION_UnpublishProduct(package);
5418 return rc;
5421 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5423 static const WCHAR RunOnce[] = {
5424 'S','o','f','t','w','a','r','e','\\',
5425 'M','i','c','r','o','s','o','f','t','\\',
5426 'W','i','n','d','o','w','s','\\',
5427 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5428 'R','u','n','O','n','c','e',0};
5429 static const WCHAR InstallRunOnce[] = {
5430 'S','o','f','t','w','a','r','e','\\',
5431 'M','i','c','r','o','s','o','f','t','\\',
5432 'W','i','n','d','o','w','s','\\',
5433 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5434 'I','n','s','t','a','l','l','e','r','\\',
5435 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5437 static const WCHAR msiexec_fmt[] = {
5438 '%','s',
5439 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5440 '\"','%','s','\"',0};
5441 static const WCHAR install_fmt[] = {
5442 '/','I',' ','\"','%','s','\"',' ',
5443 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5444 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5445 WCHAR buffer[256], sysdir[MAX_PATH], squashed_pc[SQUASHED_GUID_SIZE];
5446 HKEY hkey;
5448 squash_guid( package->ProductCode, squashed_pc );
5450 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5451 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5452 snprintfW( buffer, sizeof(buffer)/sizeof(buffer[0]), msiexec_fmt, sysdir, squashed_pc );
5454 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5455 RegCloseKey(hkey);
5457 TRACE("Reboot command %s\n",debugstr_w(buffer));
5459 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5460 sprintfW( buffer, install_fmt, package->ProductCode, squashed_pc );
5462 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5463 RegCloseKey(hkey);
5465 return ERROR_INSTALL_SUSPEND;
5468 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5470 static const WCHAR query[] =
5471 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5472 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5473 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5474 MSIRECORD *rec, *row;
5475 DWORD i, size = 0;
5476 va_list va;
5477 const WCHAR *str;
5478 WCHAR *data;
5480 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5482 rec = MSI_CreateRecord( count + 2 );
5483 str = MSI_RecordGetString( row, 1 );
5484 MSI_RecordSetStringW( rec, 0, str );
5485 msiobj_release( &row->hdr );
5486 MSI_RecordSetInteger( rec, 1, error );
5488 va_start( va, count );
5489 for (i = 0; i < count; i++)
5491 str = va_arg( va, const WCHAR *);
5492 MSI_RecordSetStringW( rec, i + 2, str );
5494 va_end( va );
5496 MSI_FormatRecordW( package, rec, NULL, &size );
5497 size++;
5498 data = msi_alloc( size * sizeof(WCHAR) );
5499 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5500 else data[0] = 0;
5501 msiobj_release( &rec->hdr );
5502 return data;
5505 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5507 DWORD attrib;
5508 UINT rc;
5511 * We are currently doing what should be done here in the top level Install
5512 * however for Administrative and uninstalls this step will be needed
5514 if (!package->PackagePath)
5515 return ERROR_SUCCESS;
5517 msi_set_sourcedir_props(package, TRUE);
5519 attrib = GetFileAttributesW(package->db->path);
5520 if (attrib == INVALID_FILE_ATTRIBUTES)
5522 LPWSTR prompt, msg;
5523 DWORD size = 0;
5525 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5526 package->Context, MSICODE_PRODUCT,
5527 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5528 if (rc == ERROR_MORE_DATA)
5530 prompt = msi_alloc(size * sizeof(WCHAR));
5531 MsiSourceListGetInfoW(package->ProductCode, NULL,
5532 package->Context, MSICODE_PRODUCT,
5533 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5535 else
5536 prompt = strdupW(package->db->path);
5538 msg = msi_build_error_string(package, 1302, 1, prompt);
5539 msi_free(prompt);
5540 while(attrib == INVALID_FILE_ATTRIBUTES)
5542 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5543 if (rc == IDCANCEL)
5545 msi_free(msg);
5546 return ERROR_INSTALL_USEREXIT;
5548 attrib = GetFileAttributesW(package->db->path);
5550 msi_free(msg);
5551 rc = ERROR_SUCCESS;
5553 else
5554 return ERROR_SUCCESS;
5556 return rc;
5559 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5561 HKEY hkey = 0;
5562 LPWSTR buffer, productid = NULL;
5563 UINT i, rc = ERROR_SUCCESS;
5564 MSIRECORD *uirow;
5566 static const WCHAR szPropKeys[][80] =
5568 {'P','r','o','d','u','c','t','I','D',0},
5569 {'U','S','E','R','N','A','M','E',0},
5570 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5571 {0},
5574 static const WCHAR szRegKeys[][80] =
5576 {'P','r','o','d','u','c','t','I','D',0},
5577 {'R','e','g','O','w','n','e','r',0},
5578 {'R','e','g','C','o','m','p','a','n','y',0},
5579 {0},
5582 if (msi_check_unpublish(package))
5584 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5585 goto end;
5588 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5589 if (!productid)
5590 goto end;
5592 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5593 NULL, &hkey, TRUE);
5594 if (rc != ERROR_SUCCESS)
5595 goto end;
5597 for( i = 0; szPropKeys[i][0]; i++ )
5599 buffer = msi_dup_property( package->db, szPropKeys[i] );
5600 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5601 msi_free( buffer );
5604 end:
5605 uirow = MSI_CreateRecord( 1 );
5606 MSI_RecordSetStringW( uirow, 1, productid );
5607 msi_ui_actiondata( package, szRegisterUser, uirow );
5608 msiobj_release( &uirow->hdr );
5610 msi_free(productid);
5611 RegCloseKey(hkey);
5612 return rc;
5616 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5618 UINT rc;
5620 package->script->InWhatSequence |= SEQUENCE_EXEC;
5621 rc = ACTION_ProcessExecSequence(package,FALSE);
5622 return rc;
5625 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5627 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5628 WCHAR productid_85[21], component_85[21], *ret;
5629 GUID clsid;
5630 DWORD sz;
5632 /* > is used if there is a component GUID and < if not. */
5634 productid_85[0] = 0;
5635 component_85[0] = 0;
5636 CLSIDFromString( package->ProductCode, &clsid );
5638 encode_base85_guid( &clsid, productid_85 );
5639 if (component)
5641 CLSIDFromString( component->ComponentId, &clsid );
5642 encode_base85_guid( &clsid, component_85 );
5645 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5646 debugstr_w(component_85));
5648 sz = 20 + strlenW( feature ) + 20 + 3;
5649 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5650 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5651 return ret;
5654 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5656 MSIPACKAGE *package = param;
5657 LPCWSTR compgroupid, component, feature, qualifier, text;
5658 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5659 HKEY hkey = NULL;
5660 UINT rc;
5661 MSICOMPONENT *comp;
5662 MSIFEATURE *feat;
5663 DWORD sz;
5664 MSIRECORD *uirow;
5665 int len;
5667 feature = MSI_RecordGetString(rec, 5);
5668 feat = msi_get_loaded_feature(package, feature);
5669 if (!feat)
5670 return ERROR_SUCCESS;
5672 feat->Action = msi_get_feature_action( package, feat );
5673 if (feat->Action != INSTALLSTATE_LOCAL &&
5674 feat->Action != INSTALLSTATE_SOURCE &&
5675 feat->Action != INSTALLSTATE_ADVERTISED)
5677 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5678 return ERROR_SUCCESS;
5681 component = MSI_RecordGetString(rec, 3);
5682 comp = msi_get_loaded_component(package, component);
5683 if (!comp)
5684 return ERROR_SUCCESS;
5686 compgroupid = MSI_RecordGetString(rec,1);
5687 qualifier = MSI_RecordGetString(rec,2);
5689 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5690 if (rc != ERROR_SUCCESS)
5691 goto end;
5693 advertise = msi_create_component_advertise_string( package, comp, feature );
5694 text = MSI_RecordGetString( rec, 4 );
5695 if (text)
5697 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5698 strcpyW( p, advertise );
5699 strcatW( p, text );
5700 msi_free( advertise );
5701 advertise = p;
5703 existing = msi_reg_get_val_str( hkey, qualifier );
5705 sz = strlenW( advertise ) + 1;
5706 if (existing)
5708 for (p = existing; *p; p += len)
5710 len = strlenW( p ) + 1;
5711 if (strcmpW( advertise, p )) sz += len;
5714 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5716 rc = ERROR_OUTOFMEMORY;
5717 goto end;
5719 q = output;
5720 if (existing)
5722 for (p = existing; *p; p += len)
5724 len = strlenW( p ) + 1;
5725 if (strcmpW( advertise, p ))
5727 memcpy( q, p, len * sizeof(WCHAR) );
5728 q += len;
5732 strcpyW( q, advertise );
5733 q[strlenW( q ) + 1] = 0;
5735 msi_reg_set_val_multi_str( hkey, qualifier, output );
5737 end:
5738 RegCloseKey(hkey);
5739 msi_free( output );
5740 msi_free( advertise );
5741 msi_free( existing );
5743 /* the UI chunk */
5744 uirow = MSI_CreateRecord( 2 );
5745 MSI_RecordSetStringW( uirow, 1, compgroupid );
5746 MSI_RecordSetStringW( uirow, 2, qualifier);
5747 msi_ui_actiondata( package, szPublishComponents, uirow );
5748 msiobj_release( &uirow->hdr );
5749 /* FIXME: call ui_progress? */
5751 return rc;
5755 * At present I am ignorning the advertised components part of this and only
5756 * focusing on the qualified component sets
5758 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5760 static const WCHAR query[] = {
5761 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5762 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5763 MSIQUERY *view;
5764 UINT rc;
5766 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5767 if (rc != ERROR_SUCCESS)
5768 return ERROR_SUCCESS;
5770 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5771 msiobj_release(&view->hdr);
5772 return rc;
5775 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5777 static const WCHAR szInstallerComponents[] = {
5778 'S','o','f','t','w','a','r','e','\\',
5779 'M','i','c','r','o','s','o','f','t','\\',
5780 'I','n','s','t','a','l','l','e','r','\\',
5781 'C','o','m','p','o','n','e','n','t','s','\\',0};
5783 MSIPACKAGE *package = param;
5784 LPCWSTR compgroupid, component, feature, qualifier;
5785 MSICOMPONENT *comp;
5786 MSIFEATURE *feat;
5787 MSIRECORD *uirow;
5788 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5789 LONG res;
5791 feature = MSI_RecordGetString( rec, 5 );
5792 feat = msi_get_loaded_feature( package, feature );
5793 if (!feat)
5794 return ERROR_SUCCESS;
5796 feat->Action = msi_get_feature_action( package, feat );
5797 if (feat->Action != INSTALLSTATE_ABSENT)
5799 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5800 return ERROR_SUCCESS;
5803 component = MSI_RecordGetString( rec, 3 );
5804 comp = msi_get_loaded_component( package, component );
5805 if (!comp)
5806 return ERROR_SUCCESS;
5808 compgroupid = MSI_RecordGetString( rec, 1 );
5809 qualifier = MSI_RecordGetString( rec, 2 );
5811 squash_guid( compgroupid, squashed );
5812 strcpyW( keypath, szInstallerComponents );
5813 strcatW( keypath, squashed );
5815 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5816 if (res != ERROR_SUCCESS)
5818 WARN("Unable to delete component key %d\n", res);
5821 uirow = MSI_CreateRecord( 2 );
5822 MSI_RecordSetStringW( uirow, 1, compgroupid );
5823 MSI_RecordSetStringW( uirow, 2, qualifier );
5824 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5825 msiobj_release( &uirow->hdr );
5827 return ERROR_SUCCESS;
5830 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5832 static const WCHAR query[] = {
5833 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5834 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5835 MSIQUERY *view;
5836 UINT rc;
5838 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5839 if (rc != ERROR_SUCCESS)
5840 return ERROR_SUCCESS;
5842 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5843 msiobj_release( &view->hdr );
5844 return rc;
5847 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5849 static const WCHAR query[] =
5850 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5851 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5852 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5853 MSIPACKAGE *package = param;
5854 MSICOMPONENT *component;
5855 MSIRECORD *row;
5856 MSIFILE *file;
5857 SC_HANDLE hscm = NULL, service = NULL;
5858 LPCWSTR comp, key;
5859 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5860 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5861 DWORD serv_type, start_type, err_control;
5862 BOOL is_vital;
5863 SERVICE_DESCRIPTIONW sd = {NULL};
5864 UINT ret = ERROR_SUCCESS;
5866 comp = MSI_RecordGetString( rec, 12 );
5867 component = msi_get_loaded_component( package, comp );
5868 if (!component)
5870 WARN("service component not found\n");
5871 goto done;
5873 component->Action = msi_get_component_action( package, component );
5874 if (component->Action != INSTALLSTATE_LOCAL)
5876 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5877 goto done;
5879 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5880 if (!hscm)
5882 ERR("Failed to open the SC Manager!\n");
5883 goto done;
5886 start_type = MSI_RecordGetInteger(rec, 5);
5887 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5888 goto done;
5890 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5891 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5892 serv_type = MSI_RecordGetInteger(rec, 4);
5893 err_control = MSI_RecordGetInteger(rec, 6);
5894 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5895 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5896 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5897 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5898 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5899 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5901 /* Should the complete install fail if CreateService fails? */
5902 is_vital = (err_control & msidbServiceInstallErrorControlVital);
5904 /* Remove the msidbServiceInstallErrorControlVital-flag from err_control.
5905 CreateService (under Windows) would fail if not. */
5906 err_control &= ~msidbServiceInstallErrorControlVital;
5908 /* fetch the service path */
5909 row = MSI_QueryGetRecord(package->db, query, comp);
5910 if (!row)
5912 ERR("Query failed\n");
5913 goto done;
5915 if (!(key = MSI_RecordGetString(row, 6)))
5917 msiobj_release(&row->hdr);
5918 goto done;
5920 file = msi_get_loaded_file(package, key);
5921 msiobj_release(&row->hdr);
5922 if (!file)
5924 ERR("Failed to load the service file\n");
5925 goto done;
5928 if (!args || !args[0]) image_path = file->TargetPath;
5929 else
5931 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5932 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5934 ret = ERROR_OUTOFMEMORY;
5935 goto done;
5938 strcpyW(image_path, file->TargetPath);
5939 strcatW(image_path, szSpace);
5940 strcatW(image_path, args);
5942 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5943 start_type, err_control, image_path, load_order,
5944 NULL, depends, serv_name, pass);
5946 if (!service)
5948 if (GetLastError() != ERROR_SERVICE_EXISTS)
5950 WARN("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5951 if (is_vital)
5952 ret = ERROR_INSTALL_FAILURE;
5956 else if (sd.lpDescription)
5958 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5959 WARN("failed to set service description %u\n", GetLastError());
5962 if (image_path != file->TargetPath) msi_free(image_path);
5963 done:
5964 if (service) CloseServiceHandle(service);
5965 if (hscm) CloseServiceHandle(hscm);
5966 msi_free(name);
5967 msi_free(disp);
5968 msi_free(sd.lpDescription);
5969 msi_free(load_order);
5970 msi_free(serv_name);
5971 msi_free(pass);
5972 msi_free(depends);
5973 msi_free(args);
5975 return ret;
5978 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5980 static const WCHAR query[] = {
5981 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5982 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5983 MSIQUERY *view;
5984 UINT rc;
5986 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5987 if (rc != ERROR_SUCCESS)
5988 return ERROR_SUCCESS;
5990 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5991 msiobj_release(&view->hdr);
5992 return rc;
5995 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5996 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5998 LPCWSTR *vector, *temp_vector;
5999 LPWSTR p, q;
6000 DWORD sep_len;
6002 static const WCHAR separator[] = {'[','~',']',0};
6004 *numargs = 0;
6005 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
6007 if (!args)
6008 return NULL;
6010 vector = msi_alloc(sizeof(LPWSTR));
6011 if (!vector)
6012 return NULL;
6014 p = args;
6017 (*numargs)++;
6018 vector[*numargs - 1] = p;
6020 if ((q = strstrW(p, separator)))
6022 *q = '\0';
6024 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
6025 if (!temp_vector)
6027 msi_free(vector);
6028 return NULL;
6030 vector = temp_vector;
6032 p = q + sep_len;
6034 } while (q);
6036 return vector;
6039 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
6041 MSIPACKAGE *package = param;
6042 MSICOMPONENT *comp;
6043 MSIRECORD *uirow;
6044 SC_HANDLE scm = NULL, service = NULL;
6045 LPCWSTR component, *vector = NULL;
6046 LPWSTR name, args, display_name = NULL;
6047 DWORD event, numargs, len, wait, dummy;
6048 UINT r = ERROR_FUNCTION_FAILED;
6049 SERVICE_STATUS_PROCESS status;
6050 ULONGLONG start_time;
6052 component = MSI_RecordGetString(rec, 6);
6053 comp = msi_get_loaded_component(package, component);
6054 if (!comp)
6055 return ERROR_SUCCESS;
6057 event = MSI_RecordGetInteger( rec, 3 );
6058 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6060 comp->Action = msi_get_component_action( package, comp );
6061 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
6062 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
6064 TRACE("not starting %s\n", debugstr_w(name));
6065 msi_free( name );
6066 return ERROR_SUCCESS;
6069 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6070 wait = MSI_RecordGetInteger(rec, 5);
6072 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
6073 if (!scm)
6075 ERR("Failed to open the service control manager\n");
6076 goto done;
6079 len = 0;
6080 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6081 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6083 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6084 GetServiceDisplayNameW( scm, name, display_name, &len );
6087 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6088 if (!service)
6090 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6091 goto done;
6094 vector = msi_service_args_to_vector(args, &numargs);
6096 if (!StartServiceW(service, numargs, vector) &&
6097 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6099 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6100 goto done;
6103 r = ERROR_SUCCESS;
6104 if (wait)
6106 /* wait for at most 30 seconds for the service to be up and running */
6107 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6108 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6110 TRACE("failed to query service status (%u)\n", GetLastError());
6111 goto done;
6113 start_time = GetTickCount64();
6114 while (status.dwCurrentState == SERVICE_START_PENDING)
6116 if (GetTickCount64() - start_time > 30000) break;
6117 Sleep(1000);
6118 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6119 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6121 TRACE("failed to query service status (%u)\n", GetLastError());
6122 goto done;
6125 if (status.dwCurrentState != SERVICE_RUNNING)
6127 WARN("service failed to start %u\n", status.dwCurrentState);
6128 r = ERROR_FUNCTION_FAILED;
6132 done:
6133 uirow = MSI_CreateRecord( 2 );
6134 MSI_RecordSetStringW( uirow, 1, display_name );
6135 MSI_RecordSetStringW( uirow, 2, name );
6136 msi_ui_actiondata( package, szStartServices, uirow );
6137 msiobj_release( &uirow->hdr );
6139 if (service) CloseServiceHandle(service);
6140 if (scm) CloseServiceHandle(scm);
6142 msi_free(name);
6143 msi_free(args);
6144 msi_free(vector);
6145 msi_free(display_name);
6146 return r;
6149 static UINT ACTION_StartServices( MSIPACKAGE *package )
6151 static const WCHAR query[] = {
6152 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6153 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6154 MSIQUERY *view;
6155 UINT rc;
6157 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6158 if (rc != ERROR_SUCCESS)
6159 return ERROR_SUCCESS;
6161 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6162 msiobj_release(&view->hdr);
6163 return rc;
6166 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6168 DWORD i, needed, count;
6169 ENUM_SERVICE_STATUSW *dependencies;
6170 SERVICE_STATUS ss;
6171 SC_HANDLE depserv;
6172 BOOL stopped, ret = FALSE;
6174 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6175 0, &needed, &count))
6176 return TRUE;
6178 if (GetLastError() != ERROR_MORE_DATA)
6179 return FALSE;
6181 dependencies = msi_alloc(needed);
6182 if (!dependencies)
6183 return FALSE;
6185 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6186 needed, &needed, &count))
6187 goto done;
6189 for (i = 0; i < count; i++)
6191 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6192 SERVICE_STOP | SERVICE_QUERY_STATUS);
6193 if (!depserv)
6194 goto done;
6196 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6197 CloseServiceHandle(depserv);
6198 if (!stopped)
6199 goto done;
6202 ret = TRUE;
6204 done:
6205 msi_free(dependencies);
6206 return ret;
6209 static UINT stop_service( LPCWSTR name )
6211 SC_HANDLE scm = NULL, service = NULL;
6212 SERVICE_STATUS status;
6213 SERVICE_STATUS_PROCESS ssp;
6214 DWORD needed;
6216 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6217 if (!scm)
6219 WARN("Failed to open the SCM: %d\n", GetLastError());
6220 goto done;
6223 service = OpenServiceW(scm, name,
6224 SERVICE_STOP |
6225 SERVICE_QUERY_STATUS |
6226 SERVICE_ENUMERATE_DEPENDENTS);
6227 if (!service)
6229 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6230 goto done;
6233 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6234 sizeof(SERVICE_STATUS_PROCESS), &needed))
6236 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6237 goto done;
6240 if (ssp.dwCurrentState == SERVICE_STOPPED)
6241 goto done;
6243 stop_service_dependents(scm, service);
6245 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6246 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6248 done:
6249 if (service) CloseServiceHandle(service);
6250 if (scm) CloseServiceHandle(scm);
6252 return ERROR_SUCCESS;
6255 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6257 MSIPACKAGE *package = param;
6258 MSICOMPONENT *comp;
6259 MSIRECORD *uirow;
6260 LPCWSTR component;
6261 WCHAR *name, *display_name = NULL;
6262 DWORD event, len;
6263 SC_HANDLE scm;
6265 component = MSI_RecordGetString( rec, 6 );
6266 comp = msi_get_loaded_component( package, component );
6267 if (!comp)
6268 return ERROR_SUCCESS;
6270 event = MSI_RecordGetInteger( rec, 3 );
6271 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6273 comp->Action = msi_get_component_action( package, comp );
6274 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6275 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6277 TRACE("not stopping %s\n", debugstr_w(name));
6278 msi_free( name );
6279 return ERROR_SUCCESS;
6282 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6283 if (!scm)
6285 ERR("Failed to open the service control manager\n");
6286 goto done;
6289 len = 0;
6290 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6291 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6293 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6294 GetServiceDisplayNameW( scm, name, display_name, &len );
6296 CloseServiceHandle( scm );
6298 stop_service( name );
6300 done:
6301 uirow = MSI_CreateRecord( 2 );
6302 MSI_RecordSetStringW( uirow, 1, display_name );
6303 MSI_RecordSetStringW( uirow, 2, name );
6304 msi_ui_actiondata( package, szStopServices, uirow );
6305 msiobj_release( &uirow->hdr );
6307 msi_free( name );
6308 msi_free( display_name );
6309 return ERROR_SUCCESS;
6312 static UINT ACTION_StopServices( MSIPACKAGE *package )
6314 static const WCHAR query[] = {
6315 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6316 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6317 MSIQUERY *view;
6318 UINT rc;
6320 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6321 if (rc != ERROR_SUCCESS)
6322 return ERROR_SUCCESS;
6324 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6325 msiobj_release(&view->hdr);
6326 return rc;
6329 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6331 MSIPACKAGE *package = param;
6332 MSICOMPONENT *comp;
6333 MSIRECORD *uirow;
6334 LPWSTR name = NULL, display_name = NULL;
6335 DWORD event, len;
6336 SC_HANDLE scm = NULL, service = NULL;
6338 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6339 if (!comp)
6340 return ERROR_SUCCESS;
6342 event = MSI_RecordGetInteger( rec, 3 );
6343 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6345 comp->Action = msi_get_component_action( package, comp );
6346 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6347 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6349 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6350 msi_free( name );
6351 return ERROR_SUCCESS;
6353 stop_service( name );
6355 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6356 if (!scm)
6358 WARN("Failed to open the SCM: %d\n", GetLastError());
6359 goto done;
6362 len = 0;
6363 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6364 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6366 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6367 GetServiceDisplayNameW( scm, name, display_name, &len );
6370 service = OpenServiceW( scm, name, DELETE );
6371 if (!service)
6373 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6374 goto done;
6377 if (!DeleteService( service ))
6378 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6380 done:
6381 uirow = MSI_CreateRecord( 2 );
6382 MSI_RecordSetStringW( uirow, 1, display_name );
6383 MSI_RecordSetStringW( uirow, 2, name );
6384 msi_ui_actiondata( package, szDeleteServices, uirow );
6385 msiobj_release( &uirow->hdr );
6387 if (service) CloseServiceHandle( service );
6388 if (scm) CloseServiceHandle( scm );
6389 msi_free( name );
6390 msi_free( display_name );
6392 return ERROR_SUCCESS;
6395 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6397 static const WCHAR query[] = {
6398 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6399 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6400 MSIQUERY *view;
6401 UINT rc;
6403 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6404 if (rc != ERROR_SUCCESS)
6405 return ERROR_SUCCESS;
6407 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6408 msiobj_release( &view->hdr );
6409 return rc;
6412 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6414 MSIPACKAGE *package = param;
6415 LPWSTR driver, driver_path, ptr;
6416 WCHAR outpath[MAX_PATH];
6417 MSIFILE *driver_file = NULL, *setup_file = NULL;
6418 MSICOMPONENT *comp;
6419 MSIRECORD *uirow;
6420 LPCWSTR desc, file_key, component;
6421 DWORD len, usage;
6422 UINT r = ERROR_SUCCESS;
6424 static const WCHAR driver_fmt[] = {
6425 'D','r','i','v','e','r','=','%','s',0};
6426 static const WCHAR setup_fmt[] = {
6427 'S','e','t','u','p','=','%','s',0};
6428 static const WCHAR usage_fmt[] = {
6429 'F','i','l','e','U','s','a','g','e','=','1',0};
6431 component = MSI_RecordGetString( rec, 2 );
6432 comp = msi_get_loaded_component( package, component );
6433 if (!comp)
6434 return ERROR_SUCCESS;
6436 comp->Action = msi_get_component_action( package, comp );
6437 if (comp->Action != INSTALLSTATE_LOCAL)
6439 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6440 return ERROR_SUCCESS;
6442 desc = MSI_RecordGetString(rec, 3);
6444 file_key = MSI_RecordGetString( rec, 4 );
6445 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6447 file_key = MSI_RecordGetString( rec, 5 );
6448 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6450 if (!driver_file)
6452 ERR("ODBC Driver entry not found!\n");
6453 return ERROR_FUNCTION_FAILED;
6456 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6457 if (setup_file)
6458 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6459 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6461 driver = msi_alloc(len * sizeof(WCHAR));
6462 if (!driver)
6463 return ERROR_OUTOFMEMORY;
6465 ptr = driver;
6466 lstrcpyW(ptr, desc);
6467 ptr += lstrlenW(ptr) + 1;
6469 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6470 ptr += len + 1;
6472 if (setup_file)
6474 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6475 ptr += len + 1;
6478 lstrcpyW(ptr, usage_fmt);
6479 ptr += lstrlenW(ptr) + 1;
6480 *ptr = '\0';
6482 if (!driver_file->TargetPath)
6484 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6485 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6487 driver_path = strdupW(driver_file->TargetPath);
6488 ptr = strrchrW(driver_path, '\\');
6489 if (ptr) *ptr = '\0';
6491 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6492 NULL, ODBC_INSTALL_COMPLETE, &usage))
6494 ERR("Failed to install SQL driver!\n");
6495 r = ERROR_FUNCTION_FAILED;
6498 uirow = MSI_CreateRecord( 5 );
6499 MSI_RecordSetStringW( uirow, 1, desc );
6500 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6501 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6502 msi_ui_actiondata( package, szInstallODBC, uirow );
6503 msiobj_release( &uirow->hdr );
6505 msi_free(driver);
6506 msi_free(driver_path);
6508 return r;
6511 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6513 MSIPACKAGE *package = param;
6514 LPWSTR translator, translator_path, ptr;
6515 WCHAR outpath[MAX_PATH];
6516 MSIFILE *translator_file = NULL, *setup_file = NULL;
6517 MSICOMPONENT *comp;
6518 MSIRECORD *uirow;
6519 LPCWSTR desc, file_key, component;
6520 DWORD len, usage;
6521 UINT r = ERROR_SUCCESS;
6523 static const WCHAR translator_fmt[] = {
6524 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6525 static const WCHAR setup_fmt[] = {
6526 'S','e','t','u','p','=','%','s',0};
6528 component = MSI_RecordGetString( rec, 2 );
6529 comp = msi_get_loaded_component( package, component );
6530 if (!comp)
6531 return ERROR_SUCCESS;
6533 comp->Action = msi_get_component_action( package, comp );
6534 if (comp->Action != INSTALLSTATE_LOCAL)
6536 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6537 return ERROR_SUCCESS;
6539 desc = MSI_RecordGetString(rec, 3);
6541 file_key = MSI_RecordGetString( rec, 4 );
6542 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6544 file_key = MSI_RecordGetString( rec, 5 );
6545 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6547 if (!translator_file)
6549 ERR("ODBC Translator entry not found!\n");
6550 return ERROR_FUNCTION_FAILED;
6553 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6554 if (setup_file)
6555 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6557 translator = msi_alloc(len * sizeof(WCHAR));
6558 if (!translator)
6559 return ERROR_OUTOFMEMORY;
6561 ptr = translator;
6562 lstrcpyW(ptr, desc);
6563 ptr += lstrlenW(ptr) + 1;
6565 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6566 ptr += len + 1;
6568 if (setup_file)
6570 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6571 ptr += len + 1;
6573 *ptr = '\0';
6575 translator_path = strdupW(translator_file->TargetPath);
6576 ptr = strrchrW(translator_path, '\\');
6577 if (ptr) *ptr = '\0';
6579 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6580 NULL, ODBC_INSTALL_COMPLETE, &usage))
6582 ERR("Failed to install SQL translator!\n");
6583 r = ERROR_FUNCTION_FAILED;
6586 uirow = MSI_CreateRecord( 5 );
6587 MSI_RecordSetStringW( uirow, 1, desc );
6588 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6589 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6590 msi_ui_actiondata( package, szInstallODBC, uirow );
6591 msiobj_release( &uirow->hdr );
6593 msi_free(translator);
6594 msi_free(translator_path);
6596 return r;
6599 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6601 MSIPACKAGE *package = param;
6602 MSICOMPONENT *comp;
6603 LPWSTR attrs;
6604 LPCWSTR desc, driver, component;
6605 WORD request = ODBC_ADD_SYS_DSN;
6606 INT registration;
6607 DWORD len;
6608 UINT r = ERROR_SUCCESS;
6609 MSIRECORD *uirow;
6611 static const WCHAR attrs_fmt[] = {
6612 'D','S','N','=','%','s',0 };
6614 component = MSI_RecordGetString( rec, 2 );
6615 comp = msi_get_loaded_component( package, component );
6616 if (!comp)
6617 return ERROR_SUCCESS;
6619 comp->Action = msi_get_component_action( package, comp );
6620 if (comp->Action != INSTALLSTATE_LOCAL)
6622 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6623 return ERROR_SUCCESS;
6626 desc = MSI_RecordGetString(rec, 3);
6627 driver = MSI_RecordGetString(rec, 4);
6628 registration = MSI_RecordGetInteger(rec, 5);
6630 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6631 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6633 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6634 attrs = msi_alloc(len * sizeof(WCHAR));
6635 if (!attrs)
6636 return ERROR_OUTOFMEMORY;
6638 len = sprintfW(attrs, attrs_fmt, desc);
6639 attrs[len + 1] = 0;
6641 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6643 ERR("Failed to install SQL data source!\n");
6644 r = ERROR_FUNCTION_FAILED;
6647 uirow = MSI_CreateRecord( 5 );
6648 MSI_RecordSetStringW( uirow, 1, desc );
6649 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6650 MSI_RecordSetInteger( uirow, 3, request );
6651 msi_ui_actiondata( package, szInstallODBC, uirow );
6652 msiobj_release( &uirow->hdr );
6654 msi_free(attrs);
6656 return r;
6659 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6661 static const WCHAR driver_query[] = {
6662 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6663 'O','D','B','C','D','r','i','v','e','r',0};
6664 static const WCHAR translator_query[] = {
6665 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6666 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6667 static const WCHAR source_query[] = {
6668 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6669 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6670 MSIQUERY *view;
6671 UINT rc;
6673 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6674 if (rc == ERROR_SUCCESS)
6676 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6677 msiobj_release(&view->hdr);
6678 if (rc != ERROR_SUCCESS)
6679 return rc;
6681 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6682 if (rc == ERROR_SUCCESS)
6684 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6685 msiobj_release(&view->hdr);
6686 if (rc != ERROR_SUCCESS)
6687 return rc;
6689 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6690 if (rc == ERROR_SUCCESS)
6692 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6693 msiobj_release(&view->hdr);
6694 if (rc != ERROR_SUCCESS)
6695 return rc;
6697 return ERROR_SUCCESS;
6700 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6702 MSIPACKAGE *package = param;
6703 MSICOMPONENT *comp;
6704 MSIRECORD *uirow;
6705 DWORD usage;
6706 LPCWSTR desc, component;
6708 component = MSI_RecordGetString( rec, 2 );
6709 comp = msi_get_loaded_component( package, component );
6710 if (!comp)
6711 return ERROR_SUCCESS;
6713 comp->Action = msi_get_component_action( package, comp );
6714 if (comp->Action != INSTALLSTATE_ABSENT)
6716 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6717 return ERROR_SUCCESS;
6720 desc = MSI_RecordGetString( rec, 3 );
6721 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6723 WARN("Failed to remove ODBC driver\n");
6725 else if (!usage)
6727 FIXME("Usage count reached 0\n");
6730 uirow = MSI_CreateRecord( 2 );
6731 MSI_RecordSetStringW( uirow, 1, desc );
6732 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6733 msi_ui_actiondata( package, szRemoveODBC, uirow );
6734 msiobj_release( &uirow->hdr );
6736 return ERROR_SUCCESS;
6739 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6741 MSIPACKAGE *package = param;
6742 MSICOMPONENT *comp;
6743 MSIRECORD *uirow;
6744 DWORD usage;
6745 LPCWSTR desc, component;
6747 component = MSI_RecordGetString( rec, 2 );
6748 comp = msi_get_loaded_component( package, component );
6749 if (!comp)
6750 return ERROR_SUCCESS;
6752 comp->Action = msi_get_component_action( package, comp );
6753 if (comp->Action != INSTALLSTATE_ABSENT)
6755 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6756 return ERROR_SUCCESS;
6759 desc = MSI_RecordGetString( rec, 3 );
6760 if (!SQLRemoveTranslatorW( desc, &usage ))
6762 WARN("Failed to remove ODBC translator\n");
6764 else if (!usage)
6766 FIXME("Usage count reached 0\n");
6769 uirow = MSI_CreateRecord( 2 );
6770 MSI_RecordSetStringW( uirow, 1, desc );
6771 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6772 msi_ui_actiondata( package, szRemoveODBC, uirow );
6773 msiobj_release( &uirow->hdr );
6775 return ERROR_SUCCESS;
6778 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6780 MSIPACKAGE *package = param;
6781 MSICOMPONENT *comp;
6782 MSIRECORD *uirow;
6783 LPWSTR attrs;
6784 LPCWSTR desc, driver, component;
6785 WORD request = ODBC_REMOVE_SYS_DSN;
6786 INT registration;
6787 DWORD len;
6789 static const WCHAR attrs_fmt[] = {
6790 'D','S','N','=','%','s',0 };
6792 component = MSI_RecordGetString( rec, 2 );
6793 comp = msi_get_loaded_component( package, component );
6794 if (!comp)
6795 return ERROR_SUCCESS;
6797 comp->Action = msi_get_component_action( package, comp );
6798 if (comp->Action != INSTALLSTATE_ABSENT)
6800 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6801 return ERROR_SUCCESS;
6804 desc = MSI_RecordGetString( rec, 3 );
6805 driver = MSI_RecordGetString( rec, 4 );
6806 registration = MSI_RecordGetInteger( rec, 5 );
6808 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6809 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6811 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6812 attrs = msi_alloc( len * sizeof(WCHAR) );
6813 if (!attrs)
6814 return ERROR_OUTOFMEMORY;
6816 FIXME("Use ODBCSourceAttribute table\n");
6818 len = sprintfW( attrs, attrs_fmt, desc );
6819 attrs[len + 1] = 0;
6821 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6823 WARN("Failed to remove ODBC data source\n");
6825 msi_free( attrs );
6827 uirow = MSI_CreateRecord( 3 );
6828 MSI_RecordSetStringW( uirow, 1, desc );
6829 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6830 MSI_RecordSetInteger( uirow, 3, request );
6831 msi_ui_actiondata( package, szRemoveODBC, uirow );
6832 msiobj_release( &uirow->hdr );
6834 return ERROR_SUCCESS;
6837 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6839 static const WCHAR driver_query[] = {
6840 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6841 'O','D','B','C','D','r','i','v','e','r',0};
6842 static const WCHAR translator_query[] = {
6843 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6844 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6845 static const WCHAR source_query[] = {
6846 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6847 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6848 MSIQUERY *view;
6849 UINT rc;
6851 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6852 if (rc == ERROR_SUCCESS)
6854 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6855 msiobj_release( &view->hdr );
6856 if (rc != ERROR_SUCCESS)
6857 return rc;
6859 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6860 if (rc == ERROR_SUCCESS)
6862 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6863 msiobj_release( &view->hdr );
6864 if (rc != ERROR_SUCCESS)
6865 return rc;
6867 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6868 if (rc == ERROR_SUCCESS)
6870 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6871 msiobj_release( &view->hdr );
6872 if (rc != ERROR_SUCCESS)
6873 return rc;
6875 return ERROR_SUCCESS;
6878 #define ENV_ACT_SETALWAYS 0x1
6879 #define ENV_ACT_SETABSENT 0x2
6880 #define ENV_ACT_REMOVE 0x4
6881 #define ENV_ACT_REMOVEMATCH 0x8
6883 #define ENV_MOD_MACHINE 0x20000000
6884 #define ENV_MOD_APPEND 0x40000000
6885 #define ENV_MOD_PREFIX 0x80000000
6886 #define ENV_MOD_MASK 0xC0000000
6888 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6890 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6892 LPCWSTR cptr = *name;
6894 static const WCHAR prefix[] = {'[','~',']',0};
6895 static const int prefix_len = 3;
6897 *flags = 0;
6898 while (*cptr)
6900 if (*cptr == '=')
6901 *flags |= ENV_ACT_SETALWAYS;
6902 else if (*cptr == '+')
6903 *flags |= ENV_ACT_SETABSENT;
6904 else if (*cptr == '-')
6905 *flags |= ENV_ACT_REMOVE;
6906 else if (*cptr == '!')
6907 *flags |= ENV_ACT_REMOVEMATCH;
6908 else if (*cptr == '*')
6909 *flags |= ENV_MOD_MACHINE;
6910 else
6911 break;
6913 cptr++;
6914 (*name)++;
6917 if (!*cptr)
6919 ERR("Missing environment variable\n");
6920 return ERROR_FUNCTION_FAILED;
6923 if (*value)
6925 LPCWSTR ptr = *value;
6926 if (!strncmpW(ptr, prefix, prefix_len))
6928 if (ptr[prefix_len] == szSemiColon[0])
6930 *flags |= ENV_MOD_APPEND;
6931 *value += lstrlenW(prefix);
6933 else
6935 *value = NULL;
6938 else if (lstrlenW(*value) >= prefix_len)
6940 ptr += lstrlenW(ptr) - prefix_len;
6941 if (!strcmpW( ptr, prefix ))
6943 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6945 *flags |= ENV_MOD_PREFIX;
6946 /* the "[~]" will be removed by deformat_string */;
6948 else
6950 *value = NULL;
6956 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6957 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6958 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6959 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6961 ERR("Invalid flags: %08x\n", *flags);
6962 return ERROR_FUNCTION_FAILED;
6965 if (!*flags)
6966 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6968 return ERROR_SUCCESS;
6971 static UINT open_env_key( DWORD flags, HKEY *key )
6973 static const WCHAR user_env[] =
6974 {'E','n','v','i','r','o','n','m','e','n','t',0};
6975 static const WCHAR machine_env[] =
6976 {'S','y','s','t','e','m','\\',
6977 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6978 'C','o','n','t','r','o','l','\\',
6979 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6980 'E','n','v','i','r','o','n','m','e','n','t',0};
6981 const WCHAR *env;
6982 HKEY root;
6983 LONG res;
6985 if (flags & ENV_MOD_MACHINE)
6987 env = machine_env;
6988 root = HKEY_LOCAL_MACHINE;
6990 else
6992 env = user_env;
6993 root = HKEY_CURRENT_USER;
6996 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6997 if (res != ERROR_SUCCESS)
6999 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
7000 return ERROR_FUNCTION_FAILED;
7003 return ERROR_SUCCESS;
7006 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
7008 MSIPACKAGE *package = param;
7009 LPCWSTR name, value, component;
7010 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
7011 DWORD flags, type, size, len, len_value = 0;
7012 UINT res;
7013 HKEY env = NULL;
7014 MSICOMPONENT *comp;
7015 MSIRECORD *uirow;
7016 int action = 0, found = 0;
7018 component = MSI_RecordGetString(rec, 4);
7019 comp = msi_get_loaded_component(package, component);
7020 if (!comp)
7021 return ERROR_SUCCESS;
7023 comp->Action = msi_get_component_action( package, comp );
7024 if (comp->Action != INSTALLSTATE_LOCAL)
7026 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
7027 return ERROR_SUCCESS;
7029 name = MSI_RecordGetString(rec, 2);
7030 value = MSI_RecordGetString(rec, 3);
7032 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7034 res = env_parse_flags(&name, &value, &flags);
7035 if (res != ERROR_SUCCESS || !value)
7036 goto done;
7038 if (value && !deformat_string(package, value, &deformatted))
7040 res = ERROR_OUTOFMEMORY;
7041 goto done;
7044 if ((value = deformatted))
7046 if (flags & ENV_MOD_PREFIX)
7048 p = strrchrW( value, ';' );
7049 len_value = p - value;
7051 else if (flags & ENV_MOD_APPEND)
7053 value = strchrW( value, ';' ) + 1;
7054 len_value = strlenW( value );
7056 else len_value = strlenW( value );
7059 res = open_env_key( flags, &env );
7060 if (res != ERROR_SUCCESS)
7061 goto done;
7063 if (flags & ENV_MOD_MACHINE)
7064 action |= 0x20000000;
7066 size = 0;
7067 type = REG_SZ;
7068 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
7069 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7070 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7071 goto done;
7073 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
7075 action = 0x2;
7077 /* Nothing to do. */
7078 if (!value)
7080 res = ERROR_SUCCESS;
7081 goto done;
7083 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
7084 newval = strdupW(value);
7085 if (!newval)
7087 res = ERROR_OUTOFMEMORY;
7088 goto done;
7091 else
7093 action = 0x1;
7095 /* Contrary to MSDN, +-variable to [~];path works */
7096 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7098 res = ERROR_SUCCESS;
7099 goto done;
7102 if (!(p = q = data = msi_alloc( size )))
7104 msi_free(deformatted);
7105 RegCloseKey(env);
7106 return ERROR_OUTOFMEMORY;
7109 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
7110 if (res != ERROR_SUCCESS)
7111 goto done;
7113 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7115 action = 0x4;
7116 res = RegDeleteValueW(env, name);
7117 if (res != ERROR_SUCCESS)
7118 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7119 goto done;
7122 for (;;)
7124 while (*q && *q != ';') q++;
7125 len = q - p;
7126 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
7127 (!p[len] || p[len] == ';'))
7129 found = 1;
7130 break;
7132 if (!*q) break;
7133 p = ++q;
7136 if (found)
7138 TRACE("string already set\n");
7139 goto done;
7142 size = (len_value + 1 + strlenW( data ) + 1) * sizeof(WCHAR);
7143 if (!(p = newval = msi_alloc( size )))
7145 res = ERROR_OUTOFMEMORY;
7146 goto done;
7149 if (flags & ENV_MOD_PREFIX)
7151 memcpy( newval, value, len_value * sizeof(WCHAR) );
7152 newval[len_value] = ';';
7153 p = newval + len_value + 1;
7154 action |= 0x80000000;
7157 strcpyW( p, data );
7159 if (flags & ENV_MOD_APPEND)
7161 p += strlenW( data );
7162 *p++ = ';';
7163 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
7164 action |= 0x40000000;
7167 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7168 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
7169 if (res)
7171 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7174 done:
7175 uirow = MSI_CreateRecord( 3 );
7176 MSI_RecordSetStringW( uirow, 1, name );
7177 MSI_RecordSetStringW( uirow, 2, newval );
7178 MSI_RecordSetInteger( uirow, 3, action );
7179 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7180 msiobj_release( &uirow->hdr );
7182 if (env) RegCloseKey(env);
7183 msi_free(deformatted);
7184 msi_free(data);
7185 msi_free(newval);
7186 return res;
7189 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7191 static const WCHAR query[] = {
7192 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7193 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7194 MSIQUERY *view;
7195 UINT rc;
7197 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7198 if (rc != ERROR_SUCCESS)
7199 return ERROR_SUCCESS;
7201 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7202 msiobj_release(&view->hdr);
7203 return rc;
7206 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7208 MSIPACKAGE *package = param;
7209 LPCWSTR name, value, component;
7210 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7211 DWORD flags, type, size, len, len_value = 0, len_new_value;
7212 HKEY env;
7213 MSICOMPONENT *comp;
7214 MSIRECORD *uirow;
7215 int action = 0;
7216 LONG res;
7217 UINT r;
7219 component = MSI_RecordGetString( rec, 4 );
7220 comp = msi_get_loaded_component( package, component );
7221 if (!comp)
7222 return ERROR_SUCCESS;
7224 comp->Action = msi_get_component_action( package, comp );
7225 if (comp->Action != INSTALLSTATE_ABSENT)
7227 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7228 return ERROR_SUCCESS;
7230 name = MSI_RecordGetString( rec, 2 );
7231 value = MSI_RecordGetString( rec, 3 );
7233 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7235 r = env_parse_flags( &name, &value, &flags );
7236 if (r != ERROR_SUCCESS)
7237 return r;
7239 if (!(flags & ENV_ACT_REMOVE))
7241 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7242 return ERROR_SUCCESS;
7245 if (value && !deformat_string( package, value, &deformatted ))
7246 return ERROR_OUTOFMEMORY;
7248 if ((value = deformatted))
7250 if (flags & ENV_MOD_PREFIX)
7252 p = strchrW( value, ';' );
7253 len_value = p - value;
7255 else if (flags & ENV_MOD_APPEND)
7257 value = strchrW( value, ';' ) + 1;
7258 len_value = strlenW( value );
7260 else len_value = strlenW( value );
7263 r = open_env_key( flags, &env );
7264 if (r != ERROR_SUCCESS)
7266 r = ERROR_SUCCESS;
7267 goto done;
7270 if (flags & ENV_MOD_MACHINE)
7271 action |= 0x20000000;
7273 size = 0;
7274 type = REG_SZ;
7275 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7276 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7277 goto done;
7279 if (!(new_value = msi_alloc( size ))) goto done;
7281 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7282 if (res != ERROR_SUCCESS)
7283 goto done;
7285 len_new_value = size / sizeof(WCHAR) - 1;
7286 p = q = new_value;
7287 for (;;)
7289 while (*q && *q != ';') q++;
7290 len = q - p;
7291 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7293 if (*q == ';') q++;
7294 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7295 break;
7297 if (!*q) break;
7298 p = ++q;
7301 if (!new_value[0] || !value)
7303 TRACE("removing %s\n", debugstr_w(name));
7304 res = RegDeleteValueW( env, name );
7305 if (res != ERROR_SUCCESS)
7306 WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
7308 else
7310 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7311 size = (strlenW( new_value ) + 1) * sizeof(WCHAR);
7312 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7313 if (res != ERROR_SUCCESS)
7314 WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7317 done:
7318 uirow = MSI_CreateRecord( 3 );
7319 MSI_RecordSetStringW( uirow, 1, name );
7320 MSI_RecordSetStringW( uirow, 2, value );
7321 MSI_RecordSetInteger( uirow, 3, action );
7322 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7323 msiobj_release( &uirow->hdr );
7325 if (env) RegCloseKey( env );
7326 msi_free( deformatted );
7327 msi_free( new_value );
7328 return r;
7331 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7333 static const WCHAR query[] = {
7334 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7335 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7336 MSIQUERY *view;
7337 UINT rc;
7339 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7340 if (rc != ERROR_SUCCESS)
7341 return ERROR_SUCCESS;
7343 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7344 msiobj_release( &view->hdr );
7345 return rc;
7348 UINT msi_validate_product_id( MSIPACKAGE *package )
7350 LPWSTR key, template, id;
7351 UINT r = ERROR_SUCCESS;
7353 id = msi_dup_property( package->db, szProductID );
7354 if (id)
7356 msi_free( id );
7357 return ERROR_SUCCESS;
7359 template = msi_dup_property( package->db, szPIDTemplate );
7360 key = msi_dup_property( package->db, szPIDKEY );
7361 if (key && template)
7363 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7364 r = msi_set_property( package->db, szProductID, key, -1 );
7366 msi_free( template );
7367 msi_free( key );
7368 return r;
7371 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7373 return msi_validate_product_id( package );
7376 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7378 TRACE("\n");
7379 package->need_reboot_at_end = 1;
7380 return ERROR_SUCCESS;
7383 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7385 static const WCHAR szAvailableFreeReg[] =
7386 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7387 MSIRECORD *uirow;
7388 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7390 TRACE("%p %d kilobytes\n", package, space);
7392 uirow = MSI_CreateRecord( 1 );
7393 MSI_RecordSetInteger( uirow, 1, space );
7394 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7395 msiobj_release( &uirow->hdr );
7397 return ERROR_SUCCESS;
7400 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7402 TRACE("%p\n", package);
7404 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7405 return ERROR_SUCCESS;
7408 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7410 FIXME("%p\n", package);
7411 return ERROR_SUCCESS;
7414 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7416 static const WCHAR driver_query[] = {
7417 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7418 'O','D','B','C','D','r','i','v','e','r',0};
7419 static const WCHAR translator_query[] = {
7420 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7421 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7422 MSIQUERY *view;
7423 UINT r, count;
7425 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7426 if (r == ERROR_SUCCESS)
7428 count = 0;
7429 r = MSI_IterateRecords( view, &count, NULL, package );
7430 msiobj_release( &view->hdr );
7431 if (r != ERROR_SUCCESS)
7432 return r;
7433 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7435 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7436 if (r == ERROR_SUCCESS)
7438 count = 0;
7439 r = MSI_IterateRecords( view, &count, NULL, package );
7440 msiobj_release( &view->hdr );
7441 if (r != ERROR_SUCCESS)
7442 return r;
7443 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7445 return ERROR_SUCCESS;
7448 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7450 static const WCHAR fmtW[] =
7451 {'m','s','i','e','x','e','c',' ','/','q','n',' ','/','i',' ','%','s',' ',
7452 'R','E','M','O','V','E','=','%','s',0};
7453 MSIPACKAGE *package = param;
7454 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7455 int attrs = MSI_RecordGetInteger( rec, 5 );
7456 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7457 WCHAR *product, *features, *cmd;
7458 STARTUPINFOW si;
7459 PROCESS_INFORMATION info;
7460 BOOL ret;
7462 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7463 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7465 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7467 len += strlenW( product );
7468 if (features)
7469 len += strlenW( features );
7470 else
7471 len += sizeof(szAll) / sizeof(szAll[0]);
7473 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7475 msi_free( product );
7476 msi_free( features );
7477 return ERROR_OUTOFMEMORY;
7479 sprintfW( cmd, fmtW, product, features ? features : szAll );
7480 msi_free( product );
7481 msi_free( features );
7483 memset( &si, 0, sizeof(STARTUPINFOW) );
7484 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7485 msi_free( cmd );
7486 if (!ret) return GetLastError();
7487 CloseHandle( info.hThread );
7489 WaitForSingleObject( info.hProcess, INFINITE );
7490 CloseHandle( info.hProcess );
7491 return ERROR_SUCCESS;
7494 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7496 static const WCHAR query[] = {
7497 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7498 MSIQUERY *view;
7499 UINT r;
7501 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7502 if (r == ERROR_SUCCESS)
7504 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7505 msiobj_release( &view->hdr );
7506 if (r != ERROR_SUCCESS)
7507 return r;
7509 return ERROR_SUCCESS;
7512 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7514 MSIPACKAGE *package = param;
7515 int attributes = MSI_RecordGetInteger( rec, 5 );
7517 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7519 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7520 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7521 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7522 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7523 HKEY hkey;
7524 UINT r;
7526 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7528 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7529 if (r != ERROR_SUCCESS)
7530 return ERROR_SUCCESS;
7532 else
7534 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7535 if (r != ERROR_SUCCESS)
7536 return ERROR_SUCCESS;
7538 RegCloseKey( hkey );
7540 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7541 debugstr_w(upgrade_code), debugstr_w(version_min),
7542 debugstr_w(version_max), debugstr_w(language));
7544 return ERROR_SUCCESS;
7547 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7549 static const WCHAR query[] = {
7550 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7551 'U','p','g','r','a','d','e',0};
7552 MSIQUERY *view;
7553 UINT r;
7555 if (msi_get_property_int( package->db, szInstalled, 0 ))
7557 TRACE("product is installed, skipping action\n");
7558 return ERROR_SUCCESS;
7560 if (msi_get_property_int( package->db, szPreselected, 0 ))
7562 TRACE("Preselected property is set, not migrating feature states\n");
7563 return ERROR_SUCCESS;
7565 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7566 if (r == ERROR_SUCCESS)
7568 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7569 msiobj_release( &view->hdr );
7570 if (r != ERROR_SUCCESS)
7571 return r;
7573 return ERROR_SUCCESS;
7576 static void bind_image( const char *filename, const char *path )
7578 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7580 WARN("failed to bind image %u\n", GetLastError());
7584 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7586 UINT i;
7587 MSIFILE *file;
7588 MSIPACKAGE *package = param;
7589 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7590 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7591 char *filenameA, *pathA;
7592 WCHAR *pathW, **path_list;
7594 if (!(file = msi_get_loaded_file( package, key )))
7596 WARN("file %s not found\n", debugstr_w(key));
7597 return ERROR_SUCCESS;
7599 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7600 path_list = msi_split_string( paths, ';' );
7601 if (!path_list) bind_image( filenameA, NULL );
7602 else
7604 for (i = 0; path_list[i] && path_list[i][0]; i++)
7606 deformat_string( package, path_list[i], &pathW );
7607 if ((pathA = strdupWtoA( pathW )))
7609 bind_image( filenameA, pathA );
7610 msi_free( pathA );
7612 msi_free( pathW );
7615 msi_free( path_list );
7616 msi_free( filenameA );
7617 return ERROR_SUCCESS;
7620 static UINT ACTION_BindImage( MSIPACKAGE *package )
7622 static const WCHAR query[] = {
7623 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7624 'B','i','n','d','I','m','a','g','e',0};
7625 MSIQUERY *view;
7626 UINT r;
7628 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7629 if (r == ERROR_SUCCESS)
7631 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7632 msiobj_release( &view->hdr );
7633 if (r != ERROR_SUCCESS)
7634 return r;
7636 return ERROR_SUCCESS;
7639 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7641 static const WCHAR query[] = {
7642 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7643 MSIQUERY *view;
7644 DWORD count = 0;
7645 UINT r;
7647 r = MSI_OpenQuery( package->db, &view, query, table );
7648 if (r == ERROR_SUCCESS)
7650 r = MSI_IterateRecords(view, &count, NULL, package);
7651 msiobj_release(&view->hdr);
7652 if (r != ERROR_SUCCESS)
7653 return r;
7655 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7656 return ERROR_SUCCESS;
7659 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7661 static const WCHAR table[] = {
7662 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7663 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7666 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7668 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7669 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7672 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7674 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7675 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7678 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7680 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7681 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7684 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7686 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7687 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7690 static const struct
7692 const WCHAR *action;
7693 UINT (*handler)(MSIPACKAGE *);
7694 const WCHAR *action_rollback;
7696 StandardActions[] =
7698 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7699 { szAppSearch, ACTION_AppSearch, NULL },
7700 { szBindImage, ACTION_BindImage, NULL },
7701 { szCCPSearch, ACTION_CCPSearch, NULL },
7702 { szCostFinalize, ACTION_CostFinalize, NULL },
7703 { szCostInitialize, ACTION_CostInitialize, NULL },
7704 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7705 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7706 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7707 { szDisableRollback, ACTION_DisableRollback, NULL },
7708 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7709 { szExecuteAction, ACTION_ExecuteAction, NULL },
7710 { szFileCost, ACTION_FileCost, NULL },
7711 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7712 { szForceReboot, ACTION_ForceReboot, NULL },
7713 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7714 { szInstallExecute, ACTION_InstallExecute, NULL },
7715 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7716 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7717 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7718 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7719 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7720 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7721 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7722 { szInstallValidate, ACTION_InstallValidate, NULL },
7723 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7724 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7725 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7726 { szMoveFiles, ACTION_MoveFiles, NULL },
7727 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7728 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7729 { szPatchFiles, ACTION_PatchFiles, NULL },
7730 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7731 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7732 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7733 { szPublishProduct, ACTION_PublishProduct, szUnpublishProduct },
7734 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7735 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7736 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7737 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7738 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7739 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7740 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7741 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7742 { szRegisterUser, ACTION_RegisterUser, NULL },
7743 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7744 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7745 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7746 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7747 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7748 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7749 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7750 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7751 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7752 { szResolveSource, ACTION_ResolveSource, NULL },
7753 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7754 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7755 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7756 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7757 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7758 { szStartServices, ACTION_StartServices, szStopServices },
7759 { szStopServices, ACTION_StopServices, szStartServices },
7760 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7761 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7762 { szUnpublishProduct, ACTION_UnpublishProduct, NULL }, /* for rollback only */
7763 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7764 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7765 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7766 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7767 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7768 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7769 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7770 { szValidateProductID, ACTION_ValidateProductID, NULL },
7771 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7772 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7773 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7774 { NULL, NULL, NULL }
7777 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7779 BOOL ret = FALSE;
7780 UINT i;
7782 i = 0;
7783 while (StandardActions[i].action != NULL)
7785 if (!strcmpW( StandardActions[i].action, action ))
7787 ui_actionstart( package, action );
7788 if (StandardActions[i].handler)
7790 ui_actioninfo( package, action, TRUE, 0 );
7791 *rc = StandardActions[i].handler( package );
7792 ui_actioninfo( package, action, FALSE, *rc );
7794 if (StandardActions[i].action_rollback && !package->need_rollback)
7796 TRACE("scheduling rollback action\n");
7797 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7800 else
7802 FIXME("unhandled standard action %s\n", debugstr_w(action));
7803 *rc = ERROR_SUCCESS;
7805 ret = TRUE;
7806 break;
7808 i++;
7810 return ret;
7813 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7815 UINT rc = ERROR_SUCCESS;
7816 BOOL handled;
7818 TRACE("Performing action (%s)\n", debugstr_w(action));
7820 handled = ACTION_HandleStandardAction(package, action, &rc);
7822 if (!handled)
7823 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7825 if (!handled)
7827 WARN("unhandled msi action %s\n", debugstr_w(action));
7828 rc = ERROR_FUNCTION_NOT_CALLED;
7831 return rc;
7834 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7836 UINT rc = ERROR_SUCCESS;
7837 BOOL handled = FALSE;
7839 TRACE("Performing action (%s)\n", debugstr_w(action));
7841 package->action_progress_increment = 0;
7842 handled = ACTION_HandleStandardAction(package, action, &rc);
7844 if (!handled)
7845 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7847 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7848 handled = TRUE;
7850 if (!handled)
7852 WARN("unhandled msi action %s\n", debugstr_w(action));
7853 rc = ERROR_FUNCTION_NOT_CALLED;
7856 return rc;
7859 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7861 UINT rc = ERROR_SUCCESS;
7862 MSIRECORD *row;
7864 static const WCHAR query[] =
7865 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7866 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7867 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7868 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7869 static const WCHAR ui_query[] =
7870 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7871 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7872 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7873 ' ', '=',' ','%','i',0};
7875 if (needs_ui_sequence(package))
7876 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7877 else
7878 row = MSI_QueryGetRecord(package->db, query, seq);
7880 if (row)
7882 LPCWSTR action, cond;
7884 TRACE("Running the actions\n");
7886 /* check conditions */
7887 cond = MSI_RecordGetString(row, 2);
7889 /* this is a hack to skip errors in the condition code */
7890 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7892 msiobj_release(&row->hdr);
7893 return ERROR_SUCCESS;
7896 action = MSI_RecordGetString(row, 1);
7897 if (!action)
7899 ERR("failed to fetch action\n");
7900 msiobj_release(&row->hdr);
7901 return ERROR_FUNCTION_FAILED;
7904 if (needs_ui_sequence(package))
7905 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7906 else
7907 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7909 msiobj_release(&row->hdr);
7912 return rc;
7915 /****************************************************
7916 * TOP level entry points
7917 *****************************************************/
7919 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7920 LPCWSTR szCommandLine )
7922 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7923 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7924 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7925 WCHAR *reinstall, *remove, *patch, *productcode;
7926 BOOL ui_exists;
7927 UINT rc;
7929 msi_set_property( package->db, szAction, szInstall, -1 );
7931 package->script->InWhatSequence = SEQUENCE_INSTALL;
7933 if (szPackagePath)
7935 LPWSTR p, dir;
7936 LPCWSTR file;
7938 dir = strdupW(szPackagePath);
7939 p = strrchrW(dir, '\\');
7940 if (p)
7942 *(++p) = 0;
7943 file = szPackagePath + (p - dir);
7945 else
7947 msi_free(dir);
7948 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7949 GetCurrentDirectoryW(MAX_PATH, dir);
7950 lstrcatW(dir, szBackSlash);
7951 file = szPackagePath;
7954 msi_free( package->PackagePath );
7955 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7956 if (!package->PackagePath)
7958 msi_free(dir);
7959 return ERROR_OUTOFMEMORY;
7962 lstrcpyW(package->PackagePath, dir);
7963 lstrcatW(package->PackagePath, file);
7964 msi_free(dir);
7966 msi_set_sourcedir_props(package, FALSE);
7969 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7970 if (rc != ERROR_SUCCESS)
7971 return rc;
7973 msi_apply_transforms( package );
7974 msi_apply_patches( package );
7976 patch = msi_dup_property( package->db, szPatch );
7977 remove = msi_dup_property( package->db, szRemove );
7978 reinstall = msi_dup_property( package->db, szReinstall );
7979 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7981 TRACE("setting REINSTALL property to ALL\n");
7982 msi_set_property( package->db, szReinstall, szAll, -1 );
7983 package->full_reinstall = 1;
7986 msi_set_original_database_property( package->db, szPackagePath );
7987 msi_parse_command_line( package, szCommandLine, FALSE );
7988 msi_adjust_privilege_properties( package );
7989 msi_set_context( package );
7991 productcode = msi_dup_property( package->db, szProductCode );
7992 if (strcmpiW( productcode, package->ProductCode ))
7994 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
7995 msi_free( package->ProductCode );
7996 package->ProductCode = productcode;
7998 else msi_free( productcode );
8000 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
8002 TRACE("disabling rollback\n");
8003 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
8006 if (needs_ui_sequence( package))
8008 package->script->InWhatSequence |= SEQUENCE_UI;
8009 rc = ACTION_ProcessUISequence(package);
8010 ui_exists = ui_sequence_exists(package);
8011 if (rc == ERROR_SUCCESS || !ui_exists)
8013 package->script->InWhatSequence |= SEQUENCE_EXEC;
8014 rc = ACTION_ProcessExecSequence(package, ui_exists);
8017 else
8018 rc = ACTION_ProcessExecSequence(package, FALSE);
8020 /* process the ending type action */
8021 if (rc == ERROR_SUCCESS)
8022 ACTION_PerformActionSequence(package, -1);
8023 else if (rc == ERROR_INSTALL_USEREXIT)
8024 ACTION_PerformActionSequence(package, -2);
8025 else if (rc == ERROR_INSTALL_SUSPEND)
8026 ACTION_PerformActionSequence(package, -4);
8027 else /* failed */
8029 ACTION_PerformActionSequence(package, -3);
8030 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
8032 package->need_rollback = TRUE;
8036 /* finish up running custom actions */
8037 ACTION_FinishCustomActions(package);
8039 if (package->need_rollback && !reinstall)
8041 WARN("installation failed, running rollback script\n");
8042 execute_script( package, SCRIPT_ROLLBACK );
8044 msi_free( reinstall );
8045 msi_free( remove );
8046 msi_free( patch );
8048 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
8049 return ERROR_SUCCESS_REBOOT_REQUIRED;
8051 return rc;