po: Update Lithuanian translation.
[wine.git] / dlls / msi / action.c
blob048d5983a9e6a2b2f98131d6fbc393f680437697
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 "winuser.h"
34 #include "shlobj.h"
35 #include "objbase.h"
36 #include "mscoree.h"
37 #include "shlwapi.h"
38 #include "imagehlp.h"
39 #include "winver.h"
41 #include "msipriv.h"
42 #include "resource.h"
44 #define REG_PROGRESS_VALUE 13200
45 #define COMPONENT_PROGRESS_VALUE 24000
47 WINE_DEFAULT_DEBUG_CHANNEL(msi);
49 static const WCHAR szCreateFolders[] =
50 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
51 static const WCHAR szCostFinalize[] =
52 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
53 static const WCHAR szWriteRegistryValues[] =
54 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
55 static const WCHAR szFileCost[] =
56 {'F','i','l','e','C','o','s','t',0};
57 static const WCHAR szInstallInitialize[] =
58 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
59 static const WCHAR szInstallValidate[] =
60 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
61 static const WCHAR szLaunchConditions[] =
62 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
63 static const WCHAR szProcessComponents[] =
64 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
65 static const WCHAR szRegisterTypeLibraries[] =
66 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
67 static const WCHAR szCreateShortcuts[] =
68 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
69 static const WCHAR szPublishProduct[] =
70 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
71 static const WCHAR szWriteIniValues[] =
72 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
73 static const WCHAR szSelfRegModules[] =
74 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
75 static const WCHAR szPublishFeatures[] =
76 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
77 static const WCHAR szRegisterProduct[] =
78 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
79 static const WCHAR szInstallExecute[] =
80 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
81 static const WCHAR szInstallExecuteAgain[] =
82 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
83 static const WCHAR szInstallFinalize[] =
84 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
85 static const WCHAR szForceReboot[] =
86 {'F','o','r','c','e','R','e','b','o','o','t',0};
87 static const WCHAR szResolveSource[] =
88 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
89 static const WCHAR szAllocateRegistrySpace[] =
90 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
91 static const WCHAR szBindImage[] =
92 {'B','i','n','d','I','m','a','g','e',0};
93 static const WCHAR szDeleteServices[] =
94 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
95 static const WCHAR szDisableRollback[] =
96 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
97 static const WCHAR szExecuteAction[] =
98 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
99 static const WCHAR szInstallAdminPackage[] =
100 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
101 static const WCHAR szInstallSFPCatalogFile[] =
102 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
103 static const WCHAR szIsolateComponents[] =
104 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
105 static const WCHAR szMigrateFeatureStates[] =
106 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
107 static const WCHAR szInstallODBC[] =
108 {'I','n','s','t','a','l','l','O','D','B','C',0};
109 static const WCHAR szInstallServices[] =
110 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
111 static const WCHAR szPublishComponents[] =
112 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
113 static const WCHAR szRegisterComPlus[] =
114 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
115 static const WCHAR szRegisterUser[] =
116 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
117 static const WCHAR szRemoveEnvironmentStrings[] =
118 {'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};
119 static const WCHAR szRemoveExistingProducts[] =
120 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
121 static const WCHAR szRemoveFolders[] =
122 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
123 static const WCHAR szRemoveIniValues[] =
124 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
125 static const WCHAR szRemoveODBC[] =
126 {'R','e','m','o','v','e','O','D','B','C',0};
127 static const WCHAR szRemoveRegistryValues[] =
128 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
129 static const WCHAR szRemoveShortcuts[] =
130 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
131 static const WCHAR szRMCCPSearch[] =
132 {'R','M','C','C','P','S','e','a','r','c','h',0};
133 static const WCHAR szScheduleReboot[] =
134 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
135 static const WCHAR szSelfUnregModules[] =
136 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
137 static const WCHAR szSetODBCFolders[] =
138 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
139 static const WCHAR szStartServices[] =
140 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
141 static const WCHAR szStopServices[] =
142 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
143 static const WCHAR szUnpublishComponents[] =
144 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
145 static const WCHAR szUnpublishFeatures[] =
146 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
147 static const WCHAR szUnpublishProduct[] =
148 {'U','n','p','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
149 static const WCHAR szUnregisterComPlus[] =
150 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
151 static const WCHAR szUnregisterTypeLibraries[] =
152 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
153 static const WCHAR szValidateProductID[] =
154 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
155 static const WCHAR szWriteEnvironmentStrings[] =
156 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static const WCHAR szINSTALL[] =
158 {'I','N','S','T','A','L','L',0};
160 static INT ui_actionstart(MSIPACKAGE *package, LPCWSTR action, LPCWSTR description, LPCWSTR template)
162 static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
163 '`','A','c','t','i','o','n','T','e','x','t','`',' ','W','H','E','R','E',' ',
164 '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
165 MSIRECORD *row, *textrow;
166 INT rc;
168 textrow = MSI_QueryGetRecord(package->db, query, action);
169 if (textrow)
171 description = MSI_RecordGetString(textrow, 2);
172 template = MSI_RecordGetString(textrow, 3);
175 row = MSI_CreateRecord(3);
176 if (!row) return -1;
177 MSI_RecordSetStringW(row, 1, action);
178 MSI_RecordSetStringW(row, 2, description);
179 MSI_RecordSetStringW(row, 3, template);
180 rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
181 if (textrow) msiobj_release(&textrow->hdr);
182 msiobj_release(&row->hdr);
183 return rc;
186 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
187 INT rc)
189 MSIRECORD *row;
190 WCHAR *template;
192 template = msi_get_error_message(package->db, start ? MSIERR_INFO_ACTIONSTART : MSIERR_INFO_ACTIONENDED);
194 row = MSI_CreateRecord(2);
195 if (!row)
197 msi_free(template);
198 return;
200 MSI_RecordSetStringW(row, 0, template);
201 MSI_RecordSetStringW(row, 1, action);
202 MSI_RecordSetInteger(row, 2, start ? package->LastActionResult : rc);
203 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
204 msiobj_release(&row->hdr);
205 msi_free(template);
206 if (!start) package->LastActionResult = rc;
209 enum parse_state
211 state_whitespace,
212 state_token,
213 state_quote
216 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
218 enum parse_state state = state_quote;
219 const WCHAR *p;
220 WCHAR *out = value;
221 BOOL ignore, in_quotes = FALSE;
222 int count = 0, len = 0;
224 for (p = str; *p; p++)
226 ignore = FALSE;
227 switch (state)
229 case state_whitespace:
230 switch (*p)
232 case ' ':
233 in_quotes = TRUE;
234 ignore = TRUE;
235 len++;
236 break;
237 case '"':
238 state = state_quote;
239 if (in_quotes && p[1] != '\"') count--;
240 else count++;
241 break;
242 default:
243 state = state_token;
244 in_quotes = TRUE;
245 len++;
246 break;
248 break;
250 case state_token:
251 switch (*p)
253 case '"':
254 state = state_quote;
255 if (in_quotes) count--;
256 else count++;
257 break;
258 case ' ':
259 state = state_whitespace;
260 if (!count) goto done;
261 in_quotes = TRUE;
262 len++;
263 break;
264 default:
265 if (count) in_quotes = TRUE;
266 len++;
267 break;
269 break;
271 case state_quote:
272 switch (*p)
274 case '"':
275 if (in_quotes && p[1] != '\"') count--;
276 else count++;
277 break;
278 case ' ':
279 state = state_whitespace;
280 if (!count || (count > 1 && !len)) goto done;
281 in_quotes = TRUE;
282 len++;
283 break;
284 default:
285 state = state_token;
286 if (count) in_quotes = TRUE;
287 len++;
288 break;
290 break;
292 default: break;
294 if (!ignore && value) *out++ = *p;
295 if (!count) in_quotes = FALSE;
298 done:
299 if (value)
301 if (!len) *value = 0;
302 else *out = 0;
305 if(quotes) *quotes = count;
306 return p - str;
309 static void remove_quotes( WCHAR *str )
311 WCHAR *p = str;
312 int len = lstrlenW( str );
314 while ((p = wcschr( p, '"' )))
316 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
317 p++;
321 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
322 BOOL preserve_case )
324 LPCWSTR ptr, ptr2;
325 int num_quotes;
326 DWORD len;
327 WCHAR *prop, *val;
328 UINT r;
330 if (!szCommandLine)
331 return ERROR_SUCCESS;
333 ptr = szCommandLine;
334 while (*ptr)
336 while (*ptr == ' ') ptr++;
337 if (!*ptr) break;
339 ptr2 = wcschr( ptr, '=' );
340 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
342 len = ptr2 - ptr;
343 if (!len) return ERROR_INVALID_COMMAND_LINE;
345 while (ptr[len - 1] == ' ') len--;
347 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
348 memcpy( prop, ptr, len * sizeof(WCHAR) );
349 prop[len] = 0;
350 if (!preserve_case) wcsupr( prop );
352 ptr2++;
353 while (*ptr2 == ' ') ptr2++;
355 num_quotes = 0;
356 val = msi_alloc( (lstrlenW( ptr2 ) + 1) * sizeof(WCHAR) );
357 len = parse_prop( ptr2, val, &num_quotes );
358 if (num_quotes % 2)
360 WARN("unbalanced quotes\n");
361 msi_free( val );
362 msi_free( prop );
363 return ERROR_INVALID_COMMAND_LINE;
365 remove_quotes( val );
366 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
368 r = msi_set_property( package->db, prop, val, -1 );
369 if (r == ERROR_SUCCESS && !wcscmp( prop, szSourceDir ))
370 msi_reset_source_folders( package );
372 msi_free( val );
373 msi_free( prop );
375 ptr = ptr2 + len;
378 return ERROR_SUCCESS;
381 const WCHAR *msi_get_command_line_option(const WCHAR *cmd, const WCHAR *option, UINT *len)
383 DWORD opt_len = lstrlenW(option);
385 if (!cmd)
386 return NULL;
388 while (*cmd)
390 BOOL found = FALSE;
392 while (*cmd == ' ') cmd++;
393 if (!*cmd) break;
395 if(!wcsnicmp(cmd, option, opt_len))
396 found = TRUE;
398 cmd = wcschr( cmd, '=' );
399 if(!cmd) break;
400 cmd++;
401 while (*cmd == ' ') cmd++;
402 if (!*cmd) break;
404 *len = parse_prop( cmd, NULL, NULL);
405 if (found) return cmd;
406 cmd += *len;
409 return NULL;
412 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
414 LPCWSTR pc;
415 LPWSTR p, *ret = NULL;
416 UINT count = 0;
418 if (!str)
419 return ret;
421 /* count the number of substrings */
422 for ( pc = str, count = 0; pc; count++ )
424 pc = wcschr( pc, sep );
425 if (pc)
426 pc++;
429 /* allocate space for an array of substring pointers and the substrings */
430 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
431 (lstrlenW(str)+1) * sizeof(WCHAR) );
432 if (!ret)
433 return ret;
435 /* copy the string and set the pointers */
436 p = (LPWSTR) &ret[count+1];
437 lstrcpyW( p, str );
438 for( count = 0; (ret[count] = p); count++ )
440 p = wcschr( p, sep );
441 if (p)
442 *p++ = 0;
445 return ret;
448 static BOOL ui_sequence_exists( MSIPACKAGE *package )
450 static const WCHAR query [] = {
451 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
452 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
453 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
454 MSIQUERY *view;
455 DWORD count = 0;
457 if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
459 MSI_IterateRecords( view, &count, NULL, package );
460 msiobj_release( &view->hdr );
462 return count != 0;
465 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
467 WCHAR *source, *check, *p, *db;
468 DWORD len;
470 if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
471 return ERROR_OUTOFMEMORY;
473 if (!(p = wcsrchr( db, '\\' )) && !(p = wcsrchr( db, '/' )))
475 msi_free(db);
476 return ERROR_SUCCESS;
478 len = p - db + 2;
479 source = msi_alloc( len * sizeof(WCHAR) );
480 lstrcpynW( source, db, len );
481 msi_free( db );
483 check = msi_dup_property( package->db, szSourceDir );
484 if (!check || replace)
486 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
487 if (r == ERROR_SUCCESS)
488 msi_reset_source_folders( package );
490 msi_free( check );
492 check = msi_dup_property( package->db, szSOURCEDIR );
493 if (!check || replace)
494 msi_set_property( package->db, szSOURCEDIR, source, -1 );
496 msi_free( check );
497 msi_free( source );
499 return ERROR_SUCCESS;
502 static BOOL needs_ui_sequence(MSIPACKAGE *package)
504 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
507 UINT msi_set_context(MSIPACKAGE *package)
509 UINT r = msi_locate_product( package->ProductCode, &package->Context );
510 if (r != ERROR_SUCCESS)
512 int num = msi_get_property_int( package->db, szAllUsers, 0 );
513 if (num == 1 || num == 2)
514 package->Context = MSIINSTALLCONTEXT_MACHINE;
515 else
516 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
518 return ERROR_SUCCESS;
521 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
523 UINT rc;
524 LPCWSTR cond, action;
525 MSIPACKAGE *package = param;
527 action = MSI_RecordGetString(row,1);
528 if (!action)
530 ERR("Error is retrieving action name\n");
531 return ERROR_FUNCTION_FAILED;
534 /* check conditions */
535 cond = MSI_RecordGetString(row,2);
537 /* this is a hack to skip errors in the condition code */
538 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
540 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
541 return ERROR_SUCCESS;
544 rc = ACTION_PerformAction(package, action);
546 msi_dialog_check_messages( NULL );
548 if (rc == ERROR_FUNCTION_NOT_CALLED)
549 rc = ERROR_SUCCESS;
551 if (rc != ERROR_SUCCESS)
552 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
554 if (package->need_reboot_now)
556 TRACE("action %s asked for immediate reboot, suspending installation\n",
557 debugstr_w(action));
558 rc = ACTION_ForceReboot( package );
560 return rc;
563 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
565 static const WCHAR query[] = {
566 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
567 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
568 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
569 '`','S','e','q','u','e','n','c','e','`',0};
570 MSIQUERY *view;
571 UINT r;
573 TRACE("%p %s\n", package, debugstr_w(table));
575 r = MSI_OpenQuery( package->db, &view, query, table );
576 if (r == ERROR_SUCCESS)
578 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
579 msiobj_release(&view->hdr);
581 return r;
584 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package)
586 static const WCHAR query[] = {
587 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
588 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
589 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
590 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','0',' ',
591 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
592 MSIQUERY *view;
593 UINT rc;
595 if (package->ExecuteSequenceRun)
597 TRACE("Execute Sequence already Run\n");
598 return ERROR_SUCCESS;
601 package->ExecuteSequenceRun = TRUE;
603 rc = MSI_OpenQuery(package->db, &view, query);
604 if (rc == ERROR_SUCCESS)
606 TRACE("Running the actions\n");
608 msi_set_property( package->db, szSourceDir, NULL, -1 );
609 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
610 msiobj_release(&view->hdr);
612 return rc;
615 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
617 static const WCHAR query[] = {
618 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
619 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
620 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
621 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
622 MSIQUERY *view;
623 UINT rc;
625 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
626 if (rc == ERROR_SUCCESS)
628 TRACE("Running the actions\n");
629 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
630 msiobj_release(&view->hdr);
632 return rc;
635 /********************************************************
636 * ACTION helper functions and functions that perform the actions
637 *******************************************************/
638 static UINT ACTION_HandleCustomAction(MSIPACKAGE *package, LPCWSTR action)
640 UINT arc;
641 INT uirc;
643 uirc = ui_actionstart(package, action, NULL, NULL);
644 if (uirc == IDCANCEL)
645 return ERROR_INSTALL_USEREXIT;
646 ui_actioninfo(package, action, TRUE, 0);
647 arc = ACTION_CustomAction(package, action);
648 uirc = !arc;
650 if (arc == ERROR_FUNCTION_NOT_CALLED && needs_ui_sequence(package))
652 uirc = ACTION_ShowDialog(package, action);
653 switch (uirc)
655 case -1:
656 return ERROR_SUCCESS; /* stop immediately */
657 case 0: arc = ERROR_FUNCTION_NOT_CALLED; break;
658 case 1: arc = ERROR_SUCCESS; break;
659 case 2: arc = ERROR_INSTALL_USEREXIT; break;
660 case 3: arc = ERROR_INSTALL_FAILURE; break;
661 case 4: arc = ERROR_INSTALL_SUSPEND; break;
662 case 5: arc = ERROR_MORE_DATA; break;
663 case 6: arc = ERROR_INVALID_HANDLE_STATE; break;
664 case 7: arc = ERROR_INVALID_DATA; break;
665 case 8: arc = ERROR_INSTALL_ALREADY_RUNNING; break;
666 case 9: arc = ERROR_INSTALL_PACKAGE_REJECTED; break;
667 default: arc = ERROR_FUNCTION_FAILED; break;
671 ui_actioninfo(package, action, FALSE, uirc);
673 return arc;
676 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
678 MSICOMPONENT *comp;
680 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
682 if (!wcscmp( Component, comp->Component )) return comp;
684 return NULL;
687 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
689 MSIFEATURE *feature;
691 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
693 if (!wcscmp( Feature, feature->Feature )) return feature;
695 return NULL;
698 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
700 MSIFILE *file;
702 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
704 if (!wcscmp( key, file->File )) return file;
706 return NULL;
709 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
711 MSIFOLDER *folder;
713 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
715 if (!wcscmp( dir, folder->Directory )) return folder;
717 return NULL;
720 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
722 MSIRECORD *row;
724 row = MSI_CreateRecord( 4 );
725 MSI_RecordSetInteger( row, 1, a );
726 MSI_RecordSetInteger( row, 2, b );
727 MSI_RecordSetInteger( row, 3, c );
728 MSI_RecordSetInteger( row, 4, d );
729 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
730 msiobj_release( &row->hdr );
732 msi_dialog_check_messages( NULL );
735 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
737 if (!comp->Enabled)
739 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
740 return INSTALLSTATE_UNKNOWN;
742 if (package->need_rollback) return comp->Installed;
743 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
745 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
746 return INSTALLSTATE_UNKNOWN;
748 return comp->ActionRequest;
751 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
753 if (package->need_rollback) return feature->Installed;
754 return feature->ActionRequest;
757 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
759 MSIPACKAGE *package = param;
760 LPCWSTR dir, component, full_path;
761 MSIRECORD *uirow;
762 MSIFOLDER *folder;
763 MSICOMPONENT *comp;
765 component = MSI_RecordGetString(row, 2);
766 if (!component)
767 return ERROR_SUCCESS;
769 comp = msi_get_loaded_component(package, component);
770 if (!comp)
771 return ERROR_SUCCESS;
773 comp->Action = msi_get_component_action( package, comp );
774 if (comp->Action != INSTALLSTATE_LOCAL)
776 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
777 return ERROR_SUCCESS;
780 dir = MSI_RecordGetString(row,1);
781 if (!dir)
783 ERR("Unable to get folder id\n");
784 return ERROR_SUCCESS;
787 uirow = MSI_CreateRecord(1);
788 MSI_RecordSetStringW(uirow, 1, dir);
789 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
790 msiobj_release(&uirow->hdr);
792 full_path = msi_get_target_folder( package, dir );
793 if (!full_path)
795 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
796 return ERROR_SUCCESS;
798 TRACE("folder is %s\n", debugstr_w(full_path));
800 folder = msi_get_loaded_folder( package, dir );
801 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( package, full_path );
802 folder->State = FOLDER_STATE_CREATED;
804 return ERROR_SUCCESS;
807 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
809 static const WCHAR query[] = {
810 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
811 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
812 MSIQUERY *view;
813 UINT rc;
815 if (package->script == SCRIPT_NONE)
816 return msi_schedule_action(package, SCRIPT_INSTALL, szCreateFolders);
818 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
819 if (rc != ERROR_SUCCESS)
820 return ERROR_SUCCESS;
822 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
823 msiobj_release(&view->hdr);
824 return rc;
827 static void remove_persistent_folder( MSIFOLDER *folder )
829 FolderList *fl;
831 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
833 remove_persistent_folder( fl->folder );
835 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
837 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
841 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
843 MSIPACKAGE *package = param;
844 LPCWSTR dir, component, full_path;
845 MSIRECORD *uirow;
846 MSIFOLDER *folder;
847 MSICOMPONENT *comp;
849 component = MSI_RecordGetString(row, 2);
850 if (!component)
851 return ERROR_SUCCESS;
853 comp = msi_get_loaded_component(package, component);
854 if (!comp)
855 return ERROR_SUCCESS;
857 comp->Action = msi_get_component_action( package, comp );
858 if (comp->Action != INSTALLSTATE_ABSENT)
860 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
861 return ERROR_SUCCESS;
864 dir = MSI_RecordGetString( row, 1 );
865 if (!dir)
867 ERR("Unable to get folder id\n");
868 return ERROR_SUCCESS;
871 full_path = msi_get_target_folder( package, dir );
872 if (!full_path)
874 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
875 return ERROR_SUCCESS;
877 TRACE("folder is %s\n", debugstr_w(full_path));
879 uirow = MSI_CreateRecord( 1 );
880 MSI_RecordSetStringW( uirow, 1, dir );
881 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
882 msiobj_release( &uirow->hdr );
884 folder = msi_get_loaded_folder( package, dir );
885 remove_persistent_folder( folder );
886 return ERROR_SUCCESS;
889 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
891 static const WCHAR query[] = {
892 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
893 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
894 MSIQUERY *view;
895 UINT rc;
897 if (package->script == SCRIPT_NONE)
898 return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveFolders);
900 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
901 if (rc != ERROR_SUCCESS)
902 return ERROR_SUCCESS;
904 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
905 msiobj_release( &view->hdr );
906 return rc;
909 static UINT load_component( MSIRECORD *row, LPVOID param )
911 MSIPACKAGE *package = param;
912 MSICOMPONENT *comp;
914 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
915 if (!comp)
916 return ERROR_FUNCTION_FAILED;
918 list_add_tail( &package->components, &comp->entry );
920 /* fill in the data */
921 comp->Component = msi_dup_record_field( row, 1 );
923 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
925 comp->ComponentId = msi_dup_record_field( row, 2 );
926 comp->Directory = msi_dup_record_field( row, 3 );
927 comp->Attributes = MSI_RecordGetInteger(row,4);
928 comp->Condition = msi_dup_record_field( row, 5 );
929 comp->KeyPath = msi_dup_record_field( row, 6 );
931 comp->Installed = INSTALLSTATE_UNKNOWN;
932 comp->Action = INSTALLSTATE_UNKNOWN;
933 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
935 comp->assembly = msi_load_assembly( package, comp );
936 return ERROR_SUCCESS;
939 UINT msi_load_all_components( MSIPACKAGE *package )
941 static const WCHAR query[] = {
942 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
943 '`','C','o','m','p','o','n','e','n','t','`',0};
944 MSIQUERY *view;
945 UINT r;
947 if (!list_empty(&package->components))
948 return ERROR_SUCCESS;
950 r = MSI_DatabaseOpenViewW( package->db, query, &view );
951 if (r != ERROR_SUCCESS)
952 return r;
954 r = MSI_IterateRecords(view, NULL, load_component, package);
955 msiobj_release(&view->hdr);
956 return r;
959 typedef struct {
960 MSIPACKAGE *package;
961 MSIFEATURE *feature;
962 } _ilfs;
964 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
966 ComponentList *cl;
968 cl = msi_alloc( sizeof (*cl) );
969 if ( !cl )
970 return ERROR_NOT_ENOUGH_MEMORY;
971 cl->component = comp;
972 list_add_tail( &feature->Components, &cl->entry );
974 return ERROR_SUCCESS;
977 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
979 FeatureList *fl;
981 fl = msi_alloc( sizeof(*fl) );
982 if ( !fl )
983 return ERROR_NOT_ENOUGH_MEMORY;
984 fl->feature = child;
985 list_add_tail( &parent->Children, &fl->entry );
987 return ERROR_SUCCESS;
990 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
992 _ilfs* ilfs = param;
993 LPCWSTR component;
994 MSICOMPONENT *comp;
996 component = MSI_RecordGetString(row,1);
998 /* check to see if the component is already loaded */
999 comp = msi_get_loaded_component( ilfs->package, component );
1000 if (!comp)
1002 WARN("ignoring unknown component %s\n", debugstr_w(component));
1003 return ERROR_SUCCESS;
1005 add_feature_component( ilfs->feature, comp );
1006 comp->Enabled = TRUE;
1008 return ERROR_SUCCESS;
1011 static UINT load_feature(MSIRECORD * row, LPVOID param)
1013 static const WCHAR query[] = {
1014 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1015 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1016 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1017 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1018 MSIPACKAGE *package = param;
1019 MSIFEATURE *feature;
1020 MSIQUERY *view;
1021 _ilfs ilfs;
1022 UINT rc;
1024 /* fill in the data */
1026 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1027 if (!feature)
1028 return ERROR_NOT_ENOUGH_MEMORY;
1030 list_init( &feature->Children );
1031 list_init( &feature->Components );
1033 feature->Feature = msi_dup_record_field( row, 1 );
1035 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1037 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1038 feature->Title = msi_dup_record_field( row, 3 );
1039 feature->Description = msi_dup_record_field( row, 4 );
1041 if (!MSI_RecordIsNull(row,5))
1042 feature->Display = MSI_RecordGetInteger(row,5);
1044 feature->Level= MSI_RecordGetInteger(row,6);
1045 feature->Directory = msi_dup_record_field( row, 7 );
1046 feature->Attributes = MSI_RecordGetInteger(row,8);
1048 feature->Installed = INSTALLSTATE_UNKNOWN;
1049 feature->Action = INSTALLSTATE_UNKNOWN;
1050 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1052 list_add_tail( &package->features, &feature->entry );
1054 /* load feature components */
1056 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1057 if (rc != ERROR_SUCCESS)
1058 return ERROR_SUCCESS;
1060 ilfs.package = package;
1061 ilfs.feature = feature;
1063 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1064 msiobj_release(&view->hdr);
1065 return rc;
1068 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1070 MSIPACKAGE *package = param;
1071 MSIFEATURE *parent, *child;
1073 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1074 if (!child)
1075 return ERROR_FUNCTION_FAILED;
1077 if (!child->Feature_Parent)
1078 return ERROR_SUCCESS;
1080 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1081 if (!parent)
1082 return ERROR_FUNCTION_FAILED;
1084 add_feature_child( parent, child );
1085 return ERROR_SUCCESS;
1088 UINT msi_load_all_features( MSIPACKAGE *package )
1090 static const WCHAR query[] = {
1091 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1092 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1093 '`','D','i','s','p','l','a','y','`',0};
1094 MSIQUERY *view;
1095 UINT r;
1097 if (!list_empty(&package->features))
1098 return ERROR_SUCCESS;
1100 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1101 if (r != ERROR_SUCCESS)
1102 return r;
1104 r = MSI_IterateRecords( view, NULL, load_feature, package );
1105 if (r != ERROR_SUCCESS)
1107 msiobj_release( &view->hdr );
1108 return r;
1110 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1111 msiobj_release( &view->hdr );
1112 return r;
1115 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1117 if (!p)
1118 return p;
1119 p = wcschr(p, ch);
1120 if (!p)
1121 return p;
1122 *p = 0;
1123 return p+1;
1126 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1128 static const WCHAR query[] = {
1129 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1130 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1131 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1132 MSIQUERY *view = NULL;
1133 MSIRECORD *row = NULL;
1134 UINT r;
1136 TRACE("%s\n", debugstr_w(file->File));
1138 r = MSI_OpenQuery(package->db, &view, query, file->File);
1139 if (r != ERROR_SUCCESS)
1140 goto done;
1142 r = MSI_ViewExecute(view, NULL);
1143 if (r != ERROR_SUCCESS)
1144 goto done;
1146 r = MSI_ViewFetch(view, &row);
1147 if (r != ERROR_SUCCESS)
1148 goto done;
1150 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1151 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1152 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1153 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1154 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1156 done:
1157 if (view) msiobj_release(&view->hdr);
1158 if (row) msiobj_release(&row->hdr);
1159 return r;
1162 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1164 MSIRECORD *row;
1165 static const WCHAR query[] = {
1166 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1167 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1168 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1170 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1171 if (!row)
1173 WARN("query failed\n");
1174 return ERROR_FUNCTION_FAILED;
1177 file->disk_id = MSI_RecordGetInteger( row, 1 );
1178 msiobj_release( &row->hdr );
1179 return ERROR_SUCCESS;
1182 static UINT load_file(MSIRECORD *row, LPVOID param)
1184 MSIPACKAGE* package = param;
1185 LPCWSTR component;
1186 MSIFILE *file;
1188 /* fill in the data */
1190 file = msi_alloc_zero( sizeof (MSIFILE) );
1191 if (!file)
1192 return ERROR_NOT_ENOUGH_MEMORY;
1194 file->File = msi_dup_record_field( row, 1 );
1196 component = MSI_RecordGetString( row, 2 );
1197 file->Component = msi_get_loaded_component( package, component );
1199 if (!file->Component)
1201 WARN("Component not found: %s\n", debugstr_w(component));
1202 msi_free(file->File);
1203 msi_free(file);
1204 return ERROR_SUCCESS;
1207 file->FileName = msi_dup_record_field( row, 3 );
1208 msi_reduce_to_long_filename( file->FileName );
1210 file->ShortName = msi_dup_record_field( row, 3 );
1211 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1213 file->FileSize = MSI_RecordGetInteger( row, 4 );
1214 file->Version = msi_dup_record_field( row, 5 );
1215 file->Language = msi_dup_record_field( row, 6 );
1216 file->Attributes = MSI_RecordGetInteger( row, 7 );
1217 file->Sequence = MSI_RecordGetInteger( row, 8 );
1219 file->state = msifs_invalid;
1221 /* if the compressed bits are not set in the file attributes,
1222 * then read the information from the package word count property
1224 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1226 file->IsCompressed = FALSE;
1228 else if (file->Attributes &
1229 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1231 file->IsCompressed = TRUE;
1233 else if (file->Attributes & msidbFileAttributesNoncompressed)
1235 file->IsCompressed = FALSE;
1237 else
1239 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1242 load_file_hash(package, file);
1243 load_file_disk_id(package, file);
1245 TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1247 list_add_tail( &package->files, &file->entry );
1249 return ERROR_SUCCESS;
1252 static UINT load_all_files(MSIPACKAGE *package)
1254 static const WCHAR query[] = {
1255 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1256 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1257 '`','S','e','q','u','e','n','c','e','`', 0};
1258 MSIQUERY *view;
1259 UINT rc;
1261 if (!list_empty(&package->files))
1262 return ERROR_SUCCESS;
1264 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1265 if (rc != ERROR_SUCCESS)
1266 return ERROR_SUCCESS;
1268 rc = MSI_IterateRecords(view, NULL, load_file, package);
1269 msiobj_release(&view->hdr);
1270 return rc;
1273 static UINT load_media( MSIRECORD *row, LPVOID param )
1275 MSIPACKAGE *package = param;
1276 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1277 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1279 /* FIXME: load external cabinets and directory sources too */
1280 if (!cabinet || cabinet[0] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
1281 return ERROR_SUCCESS;
1283 return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1286 static UINT load_all_media( MSIPACKAGE *package )
1288 static const WCHAR query[] = {
1289 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1290 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1291 '`','D','i','s','k','I','d','`',0};
1292 MSIQUERY *view;
1293 UINT r;
1295 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1296 if (r != ERROR_SUCCESS)
1297 return ERROR_SUCCESS;
1299 r = MSI_IterateRecords( view, NULL, load_media, package );
1300 msiobj_release( &view->hdr );
1301 return r;
1304 static UINT load_patch_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
1306 static const WCHAR query[] =
1307 {'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1308 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1309 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','u',0};
1310 MSIRECORD *rec;
1312 if (!(rec = MSI_QueryGetRecord( package->db, query, patch->Sequence )))
1314 WARN("query failed\n");
1315 return ERROR_FUNCTION_FAILED;
1318 patch->disk_id = MSI_RecordGetInteger( rec, 1 );
1319 msiobj_release( &rec->hdr );
1320 return ERROR_SUCCESS;
1323 static UINT load_patch(MSIRECORD *row, LPVOID param)
1325 MSIPACKAGE *package = param;
1326 MSIFILEPATCH *patch;
1327 const WCHAR *file_key;
1329 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1330 if (!patch)
1331 return ERROR_NOT_ENOUGH_MEMORY;
1333 file_key = MSI_RecordGetString( row, 1 );
1334 patch->File = msi_get_loaded_file( package, file_key );
1335 if (!patch->File)
1337 ERR("Failed to find target for patch in File table\n");
1338 msi_free(patch);
1339 return ERROR_FUNCTION_FAILED;
1342 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1343 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1344 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1346 /* FIXME:
1347 * Header field - for patch validation.
1348 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1351 load_patch_disk_id( package, patch );
1353 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1355 list_add_tail( &package->filepatches, &patch->entry );
1357 return ERROR_SUCCESS;
1360 static UINT load_all_patches(MSIPACKAGE *package)
1362 static const WCHAR query[] = {
1363 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1364 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1365 '`','S','e','q','u','e','n','c','e','`',0};
1366 MSIQUERY *view;
1367 UINT rc;
1369 if (!list_empty(&package->filepatches))
1370 return ERROR_SUCCESS;
1372 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1373 if (rc != ERROR_SUCCESS)
1374 return ERROR_SUCCESS;
1376 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1377 msiobj_release(&view->hdr);
1378 return rc;
1381 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1383 static const WCHAR query[] = {
1384 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1385 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1386 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1387 MSIQUERY *view;
1389 folder->persistent = FALSE;
1390 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1392 if (!MSI_ViewExecute( view, NULL ))
1394 MSIRECORD *rec;
1395 if (!MSI_ViewFetch( view, &rec ))
1397 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1398 folder->persistent = TRUE;
1399 msiobj_release( &rec->hdr );
1402 msiobj_release( &view->hdr );
1404 return ERROR_SUCCESS;
1407 static UINT load_folder( MSIRECORD *row, LPVOID param )
1409 MSIPACKAGE *package = param;
1410 static WCHAR szEmpty[] = { 0 };
1411 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1412 MSIFOLDER *folder;
1414 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1415 list_init( &folder->children );
1416 folder->Directory = msi_dup_record_field( row, 1 );
1417 folder->Parent = msi_dup_record_field( row, 2 );
1418 p = msi_dup_record_field(row, 3);
1420 TRACE("%s\n", debugstr_w(folder->Directory));
1422 /* split src and target dir */
1423 tgt_short = p;
1424 src_short = folder_split_path( p, ':' );
1426 /* split the long and short paths */
1427 tgt_long = folder_split_path( tgt_short, '|' );
1428 src_long = folder_split_path( src_short, '|' );
1430 /* check for no-op dirs */
1431 if (tgt_short && !wcscmp( szDot, tgt_short ))
1432 tgt_short = szEmpty;
1433 if (src_short && !wcscmp( szDot, src_short ))
1434 src_short = szEmpty;
1436 if (!tgt_long)
1437 tgt_long = tgt_short;
1439 if (!src_short) {
1440 src_short = tgt_short;
1441 src_long = tgt_long;
1444 if (!src_long)
1445 src_long = src_short;
1447 /* FIXME: use the target short path too */
1448 folder->TargetDefault = strdupW(tgt_long);
1449 folder->SourceShortPath = strdupW(src_short);
1450 folder->SourceLongPath = strdupW(src_long);
1451 msi_free(p);
1453 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1454 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1455 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1457 load_folder_persistence( package, folder );
1459 list_add_tail( &package->folders, &folder->entry );
1460 return ERROR_SUCCESS;
1463 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1465 FolderList *fl;
1467 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1468 fl->folder = child;
1469 list_add_tail( &parent->children, &fl->entry );
1470 return ERROR_SUCCESS;
1473 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1475 MSIPACKAGE *package = param;
1476 MSIFOLDER *parent, *child;
1478 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1479 return ERROR_FUNCTION_FAILED;
1481 if (!child->Parent) return ERROR_SUCCESS;
1483 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1484 return ERROR_FUNCTION_FAILED;
1486 return add_folder_child( parent, child );
1489 static UINT load_all_folders( MSIPACKAGE *package )
1491 static const WCHAR query[] = {
1492 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1493 '`','D','i','r','e','c','t','o','r','y','`',0};
1494 MSIQUERY *view;
1495 UINT r;
1497 if (!list_empty(&package->folders))
1498 return ERROR_SUCCESS;
1500 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1501 if (r != ERROR_SUCCESS)
1502 return r;
1504 r = MSI_IterateRecords( view, NULL, load_folder, package );
1505 if (r != ERROR_SUCCESS)
1507 msiobj_release( &view->hdr );
1508 return r;
1510 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1511 msiobj_release( &view->hdr );
1512 return r;
1515 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1517 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1518 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1520 load_all_folders( package );
1521 msi_load_all_components( package );
1522 msi_load_all_features( package );
1523 load_all_files( package );
1524 load_all_patches( package );
1525 load_all_media( package );
1527 return ERROR_SUCCESS;
1530 static UINT execute_script( MSIPACKAGE *package, UINT script )
1532 UINT i, rc = ERROR_SUCCESS;
1534 TRACE("executing script %u\n", script);
1536 package->script = script;
1538 if (script == SCRIPT_ROLLBACK)
1540 for (i = package->script_actions_count[script]; i > 0; i--)
1542 rc = ACTION_PerformAction(package, package->script_actions[script][i-1]);
1543 if (rc != ERROR_SUCCESS)
1545 ERR("Execution of script %i halted; action %s returned %u\n",
1546 script, debugstr_w(package->script_actions[script][i-1]), rc);
1547 break;
1551 else
1553 for (i = 0; i < package->script_actions_count[script]; i++)
1555 rc = ACTION_PerformAction(package, package->script_actions[script][i]);
1556 if (rc != ERROR_SUCCESS)
1558 ERR("Execution of script %i halted; action %s returned %u\n",
1559 script, debugstr_w(package->script_actions[script][i]), rc);
1560 break;
1565 package->script = SCRIPT_NONE;
1567 msi_free_action_script(package, script);
1568 return rc;
1571 static UINT ACTION_FileCost(MSIPACKAGE *package)
1573 return ERROR_SUCCESS;
1576 static void get_client_counts( MSIPACKAGE *package )
1578 MSICOMPONENT *comp;
1579 HKEY hkey;
1581 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1583 if (!comp->ComponentId) continue;
1585 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1586 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1588 comp->num_clients = 0;
1589 continue;
1591 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1592 NULL, NULL, NULL, NULL );
1593 RegCloseKey( hkey );
1597 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1599 MSICOMPONENT *comp;
1600 UINT r;
1602 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1604 if (!comp->ComponentId) continue;
1606 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1607 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1608 &comp->Installed );
1609 if (r == ERROR_SUCCESS) continue;
1611 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1612 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1613 &comp->Installed );
1614 if (r == ERROR_SUCCESS) continue;
1616 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1617 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1618 &comp->Installed );
1619 if (r == ERROR_SUCCESS) continue;
1621 comp->Installed = INSTALLSTATE_ABSENT;
1625 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1627 MSIFEATURE *feature;
1629 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1631 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1633 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1634 feature->Installed = INSTALLSTATE_ABSENT;
1635 else
1636 feature->Installed = state;
1640 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1642 return (feature->Level > 0 && feature->Level <= level);
1645 static BOOL process_state_property(MSIPACKAGE* package, int level,
1646 LPCWSTR property, INSTALLSTATE state)
1648 LPWSTR override;
1649 MSIFEATURE *feature;
1650 BOOL remove = !wcscmp(property, szRemove);
1651 BOOL reinstall = !wcscmp(property, szReinstall);
1653 override = msi_dup_property( package->db, property );
1654 if (!override)
1655 return FALSE;
1657 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1659 if (feature->Level <= 0)
1660 continue;
1662 if (reinstall)
1663 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1664 else if (remove)
1665 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1667 if (!wcsicmp( override, szAll ))
1669 feature->Action = state;
1670 feature->ActionRequest = state;
1672 else
1674 LPWSTR ptr = override;
1675 LPWSTR ptr2 = wcschr(override,',');
1677 while (ptr)
1679 int len = ptr2 - ptr;
1681 if ((ptr2 && lstrlenW(feature->Feature) == len && !wcsncmp(ptr, feature->Feature, len))
1682 || (!ptr2 && !wcscmp(ptr, feature->Feature)))
1684 feature->Action = state;
1685 feature->ActionRequest = state;
1686 break;
1688 if (ptr2)
1690 ptr=ptr2+1;
1691 ptr2 = wcschr(ptr,',');
1693 else
1694 break;
1698 msi_free(override);
1699 return TRUE;
1702 static BOOL process_overrides( MSIPACKAGE *package, int level )
1704 static const WCHAR szAddLocal[] =
1705 {'A','D','D','L','O','C','A','L',0};
1706 static const WCHAR szAddSource[] =
1707 {'A','D','D','S','O','U','R','C','E',0};
1708 static const WCHAR szAdvertise[] =
1709 {'A','D','V','E','R','T','I','S','E',0};
1710 BOOL ret = FALSE;
1712 /* all these activation/deactivation things happen in order and things
1713 * later on the list override things earlier on the list.
1715 * 0 INSTALLLEVEL processing
1716 * 1 ADDLOCAL
1717 * 2 REMOVE
1718 * 3 ADDSOURCE
1719 * 4 ADDDEFAULT
1720 * 5 REINSTALL
1721 * 6 ADVERTISE
1722 * 7 COMPADDLOCAL
1723 * 8 COMPADDSOURCE
1724 * 9 FILEADDLOCAL
1725 * 10 FILEADDSOURCE
1726 * 11 FILEADDDEFAULT
1728 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1729 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1730 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1731 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1732 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1734 if (ret)
1735 msi_set_property( package->db, szPreselected, szOne, -1 );
1737 return ret;
1740 static void disable_children( MSIFEATURE *feature, int level )
1742 FeatureList *fl;
1744 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1746 if (!is_feature_selected( feature, level ))
1748 TRACE("child %s (level %d request %d) follows disabled parent %s (level %d request %d)\n",
1749 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1750 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1752 fl->feature->Level = feature->Level;
1753 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1754 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1756 disable_children( fl->feature, level );
1760 static void follow_parent( MSIFEATURE *feature )
1762 FeatureList *fl;
1764 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1766 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1768 TRACE("child %s (level %d request %d) follows parent %s (level %d request %d)\n",
1769 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1770 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1772 fl->feature->Action = feature->Action;
1773 fl->feature->ActionRequest = feature->ActionRequest;
1775 follow_parent( fl->feature );
1779 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1781 int level;
1782 MSICOMPONENT* component;
1783 MSIFEATURE *feature;
1785 TRACE("Checking Install Level\n");
1787 level = msi_get_property_int(package->db, szInstallLevel, 1);
1789 if (msi_get_property_int( package->db, szPreselected, 0 ))
1791 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1793 if (!is_feature_selected( feature, level )) continue;
1795 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1797 if (feature->Installed == INSTALLSTATE_ABSENT)
1799 feature->Action = INSTALLSTATE_UNKNOWN;
1800 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1802 else
1804 feature->Action = feature->Installed;
1805 feature->ActionRequest = feature->Installed;
1809 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1811 if (feature->Feature_Parent) continue;
1812 disable_children( feature, level );
1813 follow_parent( feature );
1816 else if (!msi_get_property_int( package->db, szInstalled, 0 ))
1818 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1820 if (!is_feature_selected( feature, level )) continue;
1822 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1824 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1826 feature->Action = INSTALLSTATE_SOURCE;
1827 feature->ActionRequest = INSTALLSTATE_SOURCE;
1829 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1831 feature->Action = INSTALLSTATE_ADVERTISED;
1832 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1834 else
1836 feature->Action = INSTALLSTATE_LOCAL;
1837 feature->ActionRequest = INSTALLSTATE_LOCAL;
1841 /* disable child features of unselected parent or follow parent */
1842 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1844 if (feature->Feature_Parent) continue;
1845 disable_children( feature, level );
1846 follow_parent( feature );
1850 /* now we want to set component state based based on feature state */
1851 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1853 ComponentList *cl;
1855 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1856 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1857 feature->ActionRequest, feature->Action);
1859 /* features with components that have compressed files are made local */
1860 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1862 if (cl->component->ForceLocalState &&
1863 feature->ActionRequest == INSTALLSTATE_SOURCE)
1865 feature->Action = INSTALLSTATE_LOCAL;
1866 feature->ActionRequest = INSTALLSTATE_LOCAL;
1867 break;
1871 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1873 component = cl->component;
1875 switch (feature->ActionRequest)
1877 case INSTALLSTATE_ABSENT:
1878 component->anyAbsent = 1;
1879 break;
1880 case INSTALLSTATE_ADVERTISED:
1881 component->hasAdvertisedFeature = 1;
1882 break;
1883 case INSTALLSTATE_SOURCE:
1884 component->hasSourceFeature = 1;
1885 break;
1886 case INSTALLSTATE_LOCAL:
1887 component->hasLocalFeature = 1;
1888 break;
1889 case INSTALLSTATE_DEFAULT:
1890 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1891 component->hasAdvertisedFeature = 1;
1892 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1893 component->hasSourceFeature = 1;
1894 else
1895 component->hasLocalFeature = 1;
1896 break;
1897 case INSTALLSTATE_UNKNOWN:
1898 if (feature->Installed == INSTALLSTATE_ADVERTISED)
1899 component->hasAdvertisedFeature = 1;
1900 if (feature->Installed == INSTALLSTATE_SOURCE)
1901 component->hasSourceFeature = 1;
1902 if (feature->Installed == INSTALLSTATE_LOCAL)
1903 component->hasLocalFeature = 1;
1904 break;
1905 default:
1906 break;
1911 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1913 /* check if it's local or source */
1914 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1915 (component->hasLocalFeature || component->hasSourceFeature))
1917 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1918 !component->ForceLocalState)
1920 component->Action = INSTALLSTATE_SOURCE;
1921 component->ActionRequest = INSTALLSTATE_SOURCE;
1923 else
1925 component->Action = INSTALLSTATE_LOCAL;
1926 component->ActionRequest = INSTALLSTATE_LOCAL;
1928 continue;
1931 /* if any feature is local, the component must be local too */
1932 if (component->hasLocalFeature)
1934 component->Action = INSTALLSTATE_LOCAL;
1935 component->ActionRequest = INSTALLSTATE_LOCAL;
1936 continue;
1938 if (component->hasSourceFeature)
1940 component->Action = INSTALLSTATE_SOURCE;
1941 component->ActionRequest = INSTALLSTATE_SOURCE;
1942 continue;
1944 if (component->hasAdvertisedFeature)
1946 component->Action = INSTALLSTATE_ADVERTISED;
1947 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1948 continue;
1950 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1951 if (component->anyAbsent && component->ComponentId)
1953 component->Action = INSTALLSTATE_ABSENT;
1954 component->ActionRequest = INSTALLSTATE_ABSENT;
1958 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1960 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1962 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1963 component->Action = INSTALLSTATE_LOCAL;
1964 component->ActionRequest = INSTALLSTATE_LOCAL;
1967 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1968 component->Installed == INSTALLSTATE_SOURCE &&
1969 component->hasSourceFeature)
1971 component->Action = INSTALLSTATE_UNKNOWN;
1972 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1975 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1976 component->num_clients++;
1977 else if (component->Action == INSTALLSTATE_ABSENT)
1979 component->num_clients--;
1981 if (component->num_clients > 0)
1983 TRACE("multiple clients uses %s - disallowing uninstallation\n", debugstr_w(component->Component));
1984 component->Action = INSTALLSTATE_UNKNOWN;
1988 TRACE("component %s (installed %d request %d action %d)\n",
1989 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1992 return ERROR_SUCCESS;
1995 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1997 MSIPACKAGE *package = param;
1998 LPCWSTR name;
1999 MSIFEATURE *feature;
2001 name = MSI_RecordGetString( row, 1 );
2003 feature = msi_get_loaded_feature( package, name );
2004 if (!feature)
2005 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2006 else
2008 LPCWSTR Condition;
2009 Condition = MSI_RecordGetString(row,3);
2011 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2013 int level = MSI_RecordGetInteger(row,2);
2014 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2015 feature->Level = level;
2018 return ERROR_SUCCESS;
2021 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2023 DWORD ms, ls;
2025 msi_parse_version_string( version, &ms, &ls );
2027 if (fi->dwFileVersionMS > ms) return 1;
2028 else if (fi->dwFileVersionMS < ms) return -1;
2029 else if (fi->dwFileVersionLS > ls) return 1;
2030 else if (fi->dwFileVersionLS < ls) return -1;
2031 return 0;
2034 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2036 DWORD ms1, ms2;
2038 msi_parse_version_string( ver1, &ms1, NULL );
2039 msi_parse_version_string( ver2, &ms2, NULL );
2041 if (ms1 > ms2) return 1;
2042 else if (ms1 < ms2) return -1;
2043 return 0;
2046 static WCHAR *create_temp_dir( MSIDATABASE *db )
2048 static UINT id;
2049 WCHAR *ret;
2051 if (!db->tempfolder)
2053 WCHAR tmp[MAX_PATH];
2054 UINT len = ARRAY_SIZE( tmp );
2056 if (msi_get_property( db, szTempFolder, tmp, &len ) ||
2057 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
2059 GetTempPathW( MAX_PATH, tmp );
2061 if (!(db->tempfolder = strdupW( tmp ))) return NULL;
2064 if ((ret = msi_alloc( (lstrlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
2066 for (;;)
2068 if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret ))
2070 msi_free( ret );
2071 return NULL;
2073 if (CreateDirectoryW( ret, NULL )) break;
2077 return ret;
2081 * msi_build_directory_name()
2083 * This function is to save messing round with directory names
2084 * It handles adding backslashes between path segments,
2085 * and can add \ at the end of the directory name if told to.
2087 * It takes a variable number of arguments.
2088 * It always allocates a new string for the result, so make sure
2089 * to free the return value when finished with it.
2091 * The first arg is the number of path segments that follow.
2092 * The arguments following count are a list of path segments.
2093 * A path segment may be NULL.
2095 * Path segments will be added with a \ separating them.
2096 * A \ will not be added after the last segment, however if the
2097 * last segment is NULL, then the last character will be a \
2099 WCHAR *msi_build_directory_name( DWORD count, ... )
2101 DWORD sz = 1, i;
2102 WCHAR *dir;
2103 va_list va;
2105 va_start( va, count );
2106 for (i = 0; i < count; i++)
2108 const WCHAR *str = va_arg( va, const WCHAR * );
2109 if (str) sz += lstrlenW( str ) + 1;
2111 va_end( va );
2113 dir = msi_alloc( sz * sizeof(WCHAR) );
2114 dir[0] = 0;
2116 va_start( va, count );
2117 for (i = 0; i < count; i++)
2119 const WCHAR *str = va_arg( va, const WCHAR * );
2120 if (!str) continue;
2121 lstrcatW( dir, str );
2122 if ( i + 1 != count && dir[0] && dir[lstrlenW( dir ) - 1] != '\\') lstrcatW( dir, szBackSlash );
2124 va_end( va );
2125 return dir;
2128 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2130 return comp->assembly && !comp->assembly->application;
2133 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2135 msi_free( file->TargetPath );
2136 if (msi_is_global_assembly( file->Component ))
2138 MSIASSEMBLY *assembly = file->Component->assembly;
2140 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2141 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2143 else
2145 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2146 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2149 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2152 static UINT calculate_file_cost( MSIPACKAGE *package )
2154 VS_FIXEDFILEINFO *file_version;
2155 WCHAR *font_version;
2156 MSIFILE *file;
2158 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2160 MSICOMPONENT *comp = file->Component;
2161 DWORD file_size;
2163 if (!comp->Enabled) continue;
2165 if (file->IsCompressed)
2166 comp->ForceLocalState = TRUE;
2168 set_target_path( package, file );
2170 if ((comp->assembly && !comp->assembly->installed) ||
2171 msi_get_file_attributes( package, file->TargetPath ) == INVALID_FILE_ATTRIBUTES)
2173 comp->Cost += file->FileSize;
2174 continue;
2176 file_size = msi_get_disk_file_size( package, file->TargetPath );
2177 TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2179 if (file->Version)
2181 if ((file_version = msi_get_disk_file_version( package, file->TargetPath )))
2183 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2185 comp->Cost += file->FileSize - file_size;
2187 msi_free( file_version );
2188 continue;
2190 else if ((font_version = msi_get_font_file_version( package, file->TargetPath )))
2192 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2194 comp->Cost += file->FileSize - file_size;
2196 msi_free( font_version );
2197 continue;
2200 if (file_size != file->FileSize)
2202 comp->Cost += file->FileSize - file_size;
2206 return ERROR_SUCCESS;
2209 WCHAR *msi_normalize_path( const WCHAR *in )
2211 const WCHAR *p = in;
2212 WCHAR *q, *ret;
2213 int n, len = lstrlenW( in ) + 2;
2215 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2217 len = 0;
2218 while (1)
2220 /* copy until the end of the string or a space */
2221 while (*p != ' ' && (*q = *p))
2223 p++, len++;
2224 /* reduce many backslashes to one */
2225 if (*p != '\\' || *q != '\\')
2226 q++;
2229 /* quit at the end of the string */
2230 if (!*p)
2231 break;
2233 /* count the number of spaces */
2234 n = 0;
2235 while (p[n] == ' ')
2236 n++;
2238 /* if it's leading or trailing space, skip it */
2239 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2240 p += n;
2241 else /* copy n spaces */
2242 while (n && (*q++ = *p++)) n--;
2244 while (q - ret > 0 && q[-1] == ' ') q--;
2245 if (q - ret > 0 && q[-1] != '\\')
2247 q[0] = '\\';
2248 q[1] = 0;
2250 return ret;
2253 static WCHAR *get_install_location( MSIPACKAGE *package )
2255 HKEY hkey;
2256 WCHAR *path;
2258 if (!package->ProductCode) return NULL;
2259 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2260 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2262 msi_free( path );
2263 path = NULL;
2265 RegCloseKey( hkey );
2266 return path;
2269 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2271 FolderList *fl;
2272 MSIFOLDER *folder, *parent, *child;
2273 WCHAR *path, *normalized_path;
2275 TRACE("resolving %s\n", debugstr_w(name));
2277 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2279 if (!wcscmp( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2281 if (!(path = get_install_location( package )) &&
2282 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2284 path = msi_dup_property( package->db, szRootDrive );
2287 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2289 if (folder->Parent && wcscmp( folder->Directory, folder->Parent ))
2291 parent = msi_get_loaded_folder( package, folder->Parent );
2292 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2294 else
2295 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2298 normalized_path = msi_normalize_path( path );
2299 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2300 msi_free( path );
2302 msi_free( folder->ResolvedTarget );
2303 folder->ResolvedTarget = normalized_path;
2305 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2307 child = fl->folder;
2308 msi_resolve_target_folder( package, child->Directory, load_prop );
2310 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2313 static ULONGLONG get_volume_space_required( MSIPACKAGE *package )
2315 MSICOMPONENT *comp;
2316 ULONGLONG ret = 0;
2318 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2320 if (comp->Action == INSTALLSTATE_LOCAL) ret += comp->Cost;
2322 return ret;
2325 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2327 static const WCHAR query[] =
2328 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2329 '`','C','o','n','d','i','t','i','o','n','`',0};
2330 static const WCHAR szOutOfDiskSpace[] =
2331 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2332 static const WCHAR szPrimaryFolder[] =
2333 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2334 static const WCHAR szPrimaryVolumePath[] =
2335 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2336 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2337 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2338 'A','v','a','i','l','a','b','l','e',0};
2339 static const WCHAR szPrimaryVolumeSpaceRequired[] =
2340 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2341 'R','e','q','u','i','r','e','d',0};
2342 static const WCHAR szPrimaryVolumeSpaceRemaining[] =
2343 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2344 'R','e','m','a','i','n','i','n','g',0};
2345 static const WCHAR szOutOfNoRbDiskSpace[] =
2346 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2347 MSICOMPONENT *comp;
2348 MSIQUERY *view;
2349 WCHAR *level, *primary_key, *primary_folder;
2350 UINT rc;
2352 TRACE("Building directory properties\n");
2353 msi_resolve_target_folder( package, szTargetDir, TRUE );
2355 TRACE("Evaluating component conditions\n");
2356 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2358 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2360 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2361 comp->Enabled = FALSE;
2363 else
2364 comp->Enabled = TRUE;
2366 get_client_counts( package );
2368 /* read components states from the registry */
2369 ACTION_GetComponentInstallStates(package);
2370 ACTION_GetFeatureInstallStates(package);
2372 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2374 TRACE("Evaluating feature conditions\n");
2376 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2377 if (rc == ERROR_SUCCESS)
2379 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2380 msiobj_release( &view->hdr );
2381 if (rc != ERROR_SUCCESS)
2382 return rc;
2386 TRACE("Calculating file cost\n");
2387 calculate_file_cost( package );
2389 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2390 /* set default run level if not set */
2391 level = msi_dup_property( package->db, szInstallLevel );
2392 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2393 msi_free(level);
2395 if ((rc = MSI_SetFeatureStates( package ))) return rc;
2397 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2399 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2401 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2402 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2404 static const WCHAR fmtW[] = {'%','l','u',0};
2405 ULARGE_INTEGER free;
2406 ULONGLONG required;
2407 WCHAR buf[21];
2409 primary_folder[2] = 0;
2410 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2412 swprintf( buf, ARRAY_SIZE(buf), fmtW, free.QuadPart / 512 );
2413 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2415 required = get_volume_space_required( package );
2416 swprintf( buf, ARRAY_SIZE(buf), fmtW, required / 512 );
2417 msi_set_property( package->db, szPrimaryVolumeSpaceRequired, buf, -1 );
2419 swprintf( buf, ARRAY_SIZE(buf), fmtW, (free.QuadPart - required) / 512 );
2420 msi_set_property( package->db, szPrimaryVolumeSpaceRemaining, buf, -1 );
2421 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2423 msi_free( primary_folder );
2425 msi_free( primary_key );
2428 /* FIXME: check volume disk space */
2429 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2430 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2432 return ERROR_SUCCESS;
2435 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2437 BYTE *data;
2439 if (!value)
2441 *size = sizeof(WCHAR);
2442 *type = REG_SZ;
2443 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2444 return data;
2446 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2448 if (value[1]=='x')
2450 LPWSTR ptr;
2451 CHAR byte[5];
2452 LPWSTR deformated = NULL;
2453 int count;
2455 deformat_string(package, &value[2], &deformated);
2457 /* binary value type */
2458 ptr = deformated;
2459 *type = REG_BINARY;
2460 if (lstrlenW(ptr)%2)
2461 *size = (lstrlenW(ptr)/2)+1;
2462 else
2463 *size = lstrlenW(ptr)/2;
2465 data = msi_alloc(*size);
2467 byte[0] = '0';
2468 byte[1] = 'x';
2469 byte[4] = 0;
2470 count = 0;
2471 /* if uneven pad with a zero in front */
2472 if (lstrlenW(ptr)%2)
2474 byte[2]= '0';
2475 byte[3]= *ptr;
2476 ptr++;
2477 data[count] = (BYTE)strtol(byte,NULL,0);
2478 count ++;
2479 TRACE("Uneven byte count\n");
2481 while (*ptr)
2483 byte[2]= *ptr;
2484 ptr++;
2485 byte[3]= *ptr;
2486 ptr++;
2487 data[count] = (BYTE)strtol(byte,NULL,0);
2488 count ++;
2490 msi_free(deformated);
2492 TRACE("Data %i bytes(%i)\n",*size,count);
2494 else
2496 LPWSTR deformated;
2497 LPWSTR p;
2498 DWORD d = 0;
2499 deformat_string(package, &value[1], &deformated);
2501 *type=REG_DWORD;
2502 *size = sizeof(DWORD);
2503 data = msi_alloc(*size);
2504 p = deformated;
2505 if (*p == '-')
2506 p++;
2507 while (*p)
2509 if ( (*p < '0') || (*p > '9') )
2510 break;
2511 d *= 10;
2512 d += (*p - '0');
2513 p++;
2515 if (deformated[0] == '-')
2516 d = -d;
2517 *(LPDWORD)data = d;
2518 TRACE("DWORD %i\n",*(LPDWORD)data);
2520 msi_free(deformated);
2523 else
2525 const WCHAR *ptr = value;
2527 *type = REG_SZ;
2528 if (value[0] == '#')
2530 ptr++; len--;
2531 if (value[1] == '%')
2533 ptr++; len--;
2534 *type = REG_EXPAND_SZ;
2537 data = (BYTE *)msi_strdupW( ptr, len );
2538 if (len > lstrlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2539 *size = (len + 1) * sizeof(WCHAR);
2541 return data;
2544 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2546 const WCHAR *ret;
2548 switch (root)
2550 case -1:
2551 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2553 *root_key = HKEY_LOCAL_MACHINE;
2554 ret = szHLM;
2556 else
2558 *root_key = HKEY_CURRENT_USER;
2559 ret = szHCU;
2561 break;
2562 case 0:
2563 *root_key = HKEY_CLASSES_ROOT;
2564 ret = szHCR;
2565 break;
2566 case 1:
2567 *root_key = HKEY_CURRENT_USER;
2568 ret = szHCU;
2569 break;
2570 case 2:
2571 *root_key = HKEY_LOCAL_MACHINE;
2572 ret = szHLM;
2573 break;
2574 case 3:
2575 *root_key = HKEY_USERS;
2576 ret = szHU;
2577 break;
2578 default:
2579 ERR("Unknown root %i\n", root);
2580 return NULL;
2583 return ret;
2586 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2588 REGSAM view = 0;
2589 if (is_wow64 || is_64bit)
2590 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2591 return view;
2594 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access )
2596 WCHAR *subkey, *p, *q;
2597 HKEY hkey, ret = NULL;
2598 LONG res;
2600 access |= get_registry_view( comp );
2602 if (!(subkey = strdupW( path ))) return NULL;
2603 p = subkey;
2604 if ((q = wcschr( p, '\\' ))) *q = 0;
2605 if (create)
2606 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2607 else
2608 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2609 if (res)
2611 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2612 msi_free( subkey );
2613 return NULL;
2615 if (q && q[1])
2617 ret = open_key( comp, hkey, q + 1, create, access );
2618 RegCloseKey( hkey );
2620 else ret = hkey;
2621 msi_free( subkey );
2622 return ret;
2625 static BOOL is_special_entry( const WCHAR *name )
2627 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2630 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2632 const WCHAR *p = str;
2633 WCHAR **ret;
2634 int i = 0;
2636 *count = 0;
2637 if (!str) return NULL;
2638 while ((p - str) < len)
2640 p += lstrlenW( p ) + 1;
2641 (*count)++;
2643 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2644 p = str;
2645 while ((p - str) < len)
2647 if (!(ret[i] = strdupW( p )))
2649 for (; i >= 0; i--) msi_free( ret[i] );
2650 msi_free( ret );
2651 return NULL;
2653 p += lstrlenW( p ) + 1;
2654 i++;
2656 return ret;
2659 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2660 WCHAR **right, DWORD right_count, DWORD *size )
2662 WCHAR *ret, *p;
2663 unsigned int i;
2665 *size = sizeof(WCHAR);
2666 for (i = 0; i < left_count; i++) *size += (lstrlenW( left[i] ) + 1) * sizeof(WCHAR);
2667 for (i = 0; i < right_count; i++) *size += (lstrlenW( right[i] ) + 1) * sizeof(WCHAR);
2669 if (!(ret = p = msi_alloc( *size ))) return NULL;
2671 for (i = 0; i < left_count; i++)
2673 lstrcpyW( p, left[i] );
2674 p += lstrlenW( p ) + 1;
2676 for (i = 0; i < right_count; i++)
2678 lstrcpyW( p, right[i] );
2679 p += lstrlenW( p ) + 1;
2681 *p = 0;
2682 return ret;
2685 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2686 WCHAR **new, DWORD new_count )
2688 DWORD ret = old_count;
2689 unsigned int i, j, k;
2691 for (i = 0; i < new_count; i++)
2693 for (j = 0; j < old_count; j++)
2695 if (old[j] && !wcscmp( new[i], old[j] ))
2697 msi_free( old[j] );
2698 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2699 old[k] = NULL;
2700 ret--;
2704 return ret;
2707 enum join_op
2709 JOIN_OP_APPEND,
2710 JOIN_OP_PREPEND,
2711 JOIN_OP_REPLACE
2714 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2715 WCHAR **new, DWORD new_count, DWORD *size )
2717 switch (op)
2719 case JOIN_OP_APPEND:
2720 old_count = remove_duplicate_values( old, old_count, new, new_count );
2721 return flatten_multi_string_values( old, old_count, new, new_count, size );
2723 case JOIN_OP_PREPEND:
2724 old_count = remove_duplicate_values( old, old_count, new, new_count );
2725 return flatten_multi_string_values( new, new_count, old, old_count, size );
2727 case JOIN_OP_REPLACE:
2728 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2730 default:
2731 ERR("unhandled join op %u\n", op);
2732 return NULL;
2736 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2737 BYTE *new_value, DWORD new_size, DWORD *size )
2739 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2740 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2741 enum join_op op = JOIN_OP_REPLACE;
2742 WCHAR **old = NULL, **new = NULL;
2743 BYTE *ret;
2745 if (new_size / sizeof(WCHAR) - 1 > 1)
2747 new_ptr = (const WCHAR *)new_value;
2748 new_len = new_size / sizeof(WCHAR) - 1;
2750 if (!new_ptr[0] && new_ptr[new_len - 1])
2752 op = JOIN_OP_APPEND;
2753 new_len--;
2754 new_ptr++;
2756 else if (new_ptr[0] && !new_ptr[new_len - 1])
2758 op = JOIN_OP_PREPEND;
2759 new_len--;
2761 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2763 op = JOIN_OP_REPLACE;
2764 new_len -= 2;
2765 new_ptr++;
2767 new = split_multi_string_values( new_ptr, new_len, &new_count );
2769 if (old_size / sizeof(WCHAR) - 1 > 1)
2771 old_ptr = (const WCHAR *)old_value;
2772 old_len = old_size / sizeof(WCHAR) - 1;
2773 old = split_multi_string_values( old_ptr, old_len, &old_count );
2775 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2776 for (i = 0; i < old_count; i++) msi_free( old[i] );
2777 for (i = 0; i < new_count; i++) msi_free( new[i] );
2778 msi_free( old );
2779 msi_free( new );
2780 return ret;
2783 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2785 BYTE *ret;
2786 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2787 if (!(ret = msi_alloc( *size ))) return NULL;
2788 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2789 return ret;
2792 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2794 MSIPACKAGE *package = param;
2795 BYTE *new_value, *old_value = NULL;
2796 HKEY root_key, hkey;
2797 DWORD type, old_type, new_size, old_size = 0;
2798 LPWSTR deformated, uikey;
2799 const WCHAR *szRoot, *component, *name, *key, *str;
2800 MSICOMPONENT *comp;
2801 MSIRECORD * uirow;
2802 INT root;
2803 BOOL check_first = FALSE;
2804 int len;
2806 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2808 component = MSI_RecordGetString(row, 6);
2809 comp = msi_get_loaded_component(package,component);
2810 if (!comp)
2811 return ERROR_SUCCESS;
2813 comp->Action = msi_get_component_action( package, comp );
2814 if (comp->Action != INSTALLSTATE_LOCAL && comp->Action != INSTALLSTATE_SOURCE)
2816 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2817 return ERROR_SUCCESS;
2820 name = MSI_RecordGetString(row, 4);
2821 if( MSI_RecordIsNull(row,5) && name )
2823 /* null values can have special meanings */
2824 if (name[0]=='-' && name[1] == 0)
2825 return ERROR_SUCCESS;
2826 if ((name[0] == '+' || name[0] == '*') && !name[1])
2827 check_first = TRUE;
2830 root = MSI_RecordGetInteger(row,2);
2831 key = MSI_RecordGetString(row, 3);
2833 szRoot = get_root_key( package, root, &root_key );
2834 if (!szRoot)
2835 return ERROR_SUCCESS;
2837 deformat_string(package, key , &deformated);
2838 uikey = msi_alloc( (lstrlenW(deformated) + lstrlenW(szRoot) + 1) * sizeof(WCHAR) );
2839 lstrcpyW(uikey,szRoot);
2840 lstrcatW(uikey,deformated);
2842 if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE )))
2844 ERR("Could not create key %s\n", debugstr_w(deformated));
2845 msi_free(uikey);
2846 msi_free(deformated);
2847 return ERROR_FUNCTION_FAILED;
2849 msi_free( deformated );
2850 str = msi_record_get_string( row, 5, NULL );
2851 len = deformat_string( package, str, &deformated );
2852 new_value = parse_value( package, deformated, len, &type, &new_size );
2854 msi_free( deformated );
2855 deformat_string(package, name, &deformated);
2857 if (!is_special_entry( name ))
2859 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2860 if (type == REG_MULTI_SZ)
2862 BYTE *new;
2863 if (old_value && old_type != REG_MULTI_SZ)
2865 msi_free( old_value );
2866 old_value = NULL;
2867 old_size = 0;
2869 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2870 msi_free( new_value );
2871 new_value = new;
2873 if (!check_first)
2875 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2876 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2878 else if (!old_value)
2880 if (deformated || new_size)
2882 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2883 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2886 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2888 RegCloseKey(hkey);
2890 uirow = MSI_CreateRecord(3);
2891 MSI_RecordSetStringW(uirow,2,deformated);
2892 MSI_RecordSetStringW(uirow,1,uikey);
2893 if (type == REG_SZ || type == REG_EXPAND_SZ)
2894 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2895 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
2896 msiobj_release( &uirow->hdr );
2898 msi_free(new_value);
2899 msi_free(old_value);
2900 msi_free(deformated);
2901 msi_free(uikey);
2903 return ERROR_SUCCESS;
2906 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2908 static const WCHAR query[] = {
2909 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2910 '`','R','e','g','i','s','t','r','y','`',0};
2911 MSIQUERY *view;
2912 UINT rc;
2914 if (package->script == SCRIPT_NONE)
2915 return msi_schedule_action(package, SCRIPT_INSTALL, szWriteRegistryValues);
2917 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2918 if (rc != ERROR_SUCCESS)
2919 return ERROR_SUCCESS;
2921 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2922 msiobj_release(&view->hdr);
2923 return rc;
2926 static int is_key_empty(const MSICOMPONENT *comp, HKEY root, const WCHAR *path)
2928 DWORD subkeys, values;
2929 HKEY key;
2930 LONG res;
2932 key = open_key(comp, root, path, FALSE, KEY_READ);
2933 if (!key) return 0;
2935 res = RegQueryInfoKeyW(key, 0, 0, 0, &subkeys, 0, 0, &values, 0, 0, 0, 0);
2936 RegCloseKey(key);
2938 return !res && !subkeys && !values;
2941 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2943 LONG res = ERROR_SUCCESS;
2944 REGSAM access = get_registry_view( comp );
2945 WCHAR *subkey, *p;
2946 HKEY hkey;
2948 if (!(subkey = strdupW( path ))) return;
2951 if ((p = wcsrchr( subkey, '\\' )))
2953 *p = 0;
2954 if (!p[1]) continue; /* trailing backslash */
2955 hkey = open_key( comp, root, subkey, FALSE, READ_CONTROL );
2956 if (!hkey) break;
2957 if (!is_key_empty(comp, hkey, p + 1))
2959 RegCloseKey(hkey);
2960 break;
2962 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2963 RegCloseKey( hkey );
2965 else if (is_key_empty(comp, root, subkey))
2966 res = RegDeleteKeyExW( root, subkey, access, 0 );
2967 if (res)
2969 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2970 break;
2972 } while (p);
2973 msi_free( subkey );
2976 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
2978 LONG res;
2979 HKEY hkey;
2981 if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE )))
2983 if ((res = RegDeleteValueW( hkey, value )))
2984 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2986 RegCloseKey( hkey );
2987 if (is_key_empty(comp, root, path))
2989 TRACE("removing empty key %s\n", debugstr_w(path));
2990 delete_key( comp, root, path );
2995 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2997 LONG res;
2998 HKEY hkey;
3000 if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return;
3001 res = RegDeleteTreeW( hkey, NULL );
3002 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3003 delete_key( comp, root, path );
3004 RegCloseKey( hkey );
3007 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3009 MSIPACKAGE *package = param;
3010 LPCWSTR component, name, key_str, root_key_str;
3011 LPWSTR deformated_key, deformated_name, ui_key_str;
3012 MSICOMPONENT *comp;
3013 MSIRECORD *uirow;
3014 BOOL delete_key = FALSE;
3015 HKEY hkey_root;
3016 UINT size;
3017 INT root;
3019 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3021 component = MSI_RecordGetString( row, 6 );
3022 comp = msi_get_loaded_component( package, component );
3023 if (!comp)
3024 return ERROR_SUCCESS;
3026 comp->Action = msi_get_component_action( package, comp );
3027 if (comp->Action != INSTALLSTATE_ABSENT)
3029 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3030 return ERROR_SUCCESS;
3033 name = MSI_RecordGetString( row, 4 );
3034 if (MSI_RecordIsNull( row, 5 ) && name )
3036 if (name[0] == '+' && !name[1])
3037 return ERROR_SUCCESS;
3038 if ((name[0] == '-' || name[0] == '*') && !name[1])
3040 delete_key = TRUE;
3041 name = NULL;
3045 root = MSI_RecordGetInteger( row, 2 );
3046 key_str = MSI_RecordGetString( row, 3 );
3048 root_key_str = get_root_key( package, root, &hkey_root );
3049 if (!root_key_str)
3050 return ERROR_SUCCESS;
3052 deformat_string( package, key_str, &deformated_key );
3053 size = lstrlenW( deformated_key ) + lstrlenW( root_key_str ) + 1;
3054 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3055 lstrcpyW( ui_key_str, root_key_str );
3056 lstrcatW( ui_key_str, deformated_key );
3058 deformat_string( package, name, &deformated_name );
3060 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3061 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3062 msi_free( deformated_key );
3064 uirow = MSI_CreateRecord( 2 );
3065 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3066 MSI_RecordSetStringW( uirow, 2, deformated_name );
3067 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3068 msiobj_release( &uirow->hdr );
3070 msi_free( ui_key_str );
3071 msi_free( deformated_name );
3072 return ERROR_SUCCESS;
3075 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3077 MSIPACKAGE *package = param;
3078 LPCWSTR component, name, key_str, root_key_str;
3079 LPWSTR deformated_key, deformated_name, ui_key_str;
3080 MSICOMPONENT *comp;
3081 MSIRECORD *uirow;
3082 BOOL delete_key = FALSE;
3083 HKEY hkey_root;
3084 UINT size;
3085 INT root;
3087 component = MSI_RecordGetString( row, 5 );
3088 comp = msi_get_loaded_component( package, component );
3089 if (!comp)
3090 return ERROR_SUCCESS;
3092 comp->Action = msi_get_component_action( package, comp );
3093 if (comp->Action != INSTALLSTATE_LOCAL)
3095 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3096 return ERROR_SUCCESS;
3099 if ((name = MSI_RecordGetString( row, 4 )))
3101 if (name[0] == '-' && !name[1])
3103 delete_key = TRUE;
3104 name = NULL;
3108 root = MSI_RecordGetInteger( row, 2 );
3109 key_str = MSI_RecordGetString( row, 3 );
3111 root_key_str = get_root_key( package, root, &hkey_root );
3112 if (!root_key_str)
3113 return ERROR_SUCCESS;
3115 deformat_string( package, key_str, &deformated_key );
3116 size = lstrlenW( deformated_key ) + lstrlenW( root_key_str ) + 1;
3117 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3118 lstrcpyW( ui_key_str, root_key_str );
3119 lstrcatW( ui_key_str, deformated_key );
3121 deformat_string( package, name, &deformated_name );
3123 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3124 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3125 msi_free( deformated_key );
3127 uirow = MSI_CreateRecord( 2 );
3128 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3129 MSI_RecordSetStringW( uirow, 2, deformated_name );
3130 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3131 msiobj_release( &uirow->hdr );
3133 msi_free( ui_key_str );
3134 msi_free( deformated_name );
3135 return ERROR_SUCCESS;
3138 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3140 static const WCHAR registry_query[] = {
3141 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3142 '`','R','e','g','i','s','t','r','y','`',0};
3143 static const WCHAR remove_registry_query[] = {
3144 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3145 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3146 MSIQUERY *view;
3147 UINT rc;
3149 if (package->script == SCRIPT_NONE)
3150 return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveRegistryValues);
3152 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3153 if (rc == ERROR_SUCCESS)
3155 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3156 msiobj_release( &view->hdr );
3157 if (rc != ERROR_SUCCESS)
3158 return rc;
3160 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3161 if (rc == ERROR_SUCCESS)
3163 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3164 msiobj_release( &view->hdr );
3165 if (rc != ERROR_SUCCESS)
3166 return rc;
3168 return ERROR_SUCCESS;
3171 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3173 return ERROR_SUCCESS;
3177 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3179 static const WCHAR query[]= {
3180 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3181 '`','R','e','g','i','s','t','r','y','`',0};
3182 MSICOMPONENT *comp;
3183 DWORD total = 0, count = 0;
3184 MSIQUERY *view;
3185 MSIFEATURE *feature;
3186 MSIFILE *file;
3187 UINT rc;
3189 TRACE("InstallValidate\n");
3191 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3192 if (rc == ERROR_SUCCESS)
3194 rc = MSI_IterateRecords( view, &count, NULL, package );
3195 msiobj_release( &view->hdr );
3196 if (rc != ERROR_SUCCESS)
3197 return rc;
3198 total += count * REG_PROGRESS_VALUE;
3200 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3201 total += COMPONENT_PROGRESS_VALUE;
3203 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3204 total += file->FileSize;
3206 msi_ui_progress( package, 0, total, 0, 0 );
3208 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3210 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3211 debugstr_w(feature->Feature), feature->Installed,
3212 feature->ActionRequest, feature->Action);
3214 return ERROR_SUCCESS;
3217 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3219 MSIPACKAGE* package = param;
3220 LPCWSTR cond = NULL;
3221 LPCWSTR message = NULL;
3222 UINT r;
3224 static const WCHAR title[]=
3225 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3227 cond = MSI_RecordGetString(row,1);
3229 r = MSI_EvaluateConditionW(package,cond);
3230 if (r == MSICONDITION_FALSE)
3232 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3234 LPWSTR deformated;
3235 message = MSI_RecordGetString(row,2);
3236 deformat_string(package,message,&deformated);
3237 MessageBoxW(NULL,deformated,title,MB_OK);
3238 msi_free(deformated);
3241 return ERROR_INSTALL_FAILURE;
3244 return ERROR_SUCCESS;
3247 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3249 static const WCHAR query[] = {
3250 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3251 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3252 MSIQUERY *view;
3253 UINT rc;
3255 TRACE("Checking launch conditions\n");
3257 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3258 if (rc != ERROR_SUCCESS)
3259 return ERROR_SUCCESS;
3261 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3262 msiobj_release(&view->hdr);
3263 return rc;
3266 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3269 if (!cmp->KeyPath)
3270 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3272 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3274 static const WCHAR query[] = {
3275 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3276 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3277 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3278 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3279 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3280 MSIRECORD *row;
3281 UINT root, len;
3282 LPWSTR deformated, buffer, deformated_name;
3283 LPCWSTR key, name;
3285 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3286 if (!row)
3287 return NULL;
3289 root = MSI_RecordGetInteger(row,2);
3290 key = MSI_RecordGetString(row, 3);
3291 name = MSI_RecordGetString(row, 4);
3292 deformat_string(package, key , &deformated);
3293 deformat_string(package, name, &deformated_name);
3295 len = lstrlenW(deformated) + 6;
3296 if (deformated_name)
3297 len+=lstrlenW(deformated_name);
3299 buffer = msi_alloc( len *sizeof(WCHAR));
3301 if (deformated_name)
3302 swprintf(buffer,len,fmt2,root,deformated,deformated_name);
3303 else
3304 swprintf(buffer,len,fmt,root,deformated);
3306 msi_free(deformated);
3307 msi_free(deformated_name);
3308 msiobj_release(&row->hdr);
3310 return buffer;
3312 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3314 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3315 return NULL;
3317 else
3319 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3321 if (file)
3322 return strdupW( file->TargetPath );
3324 return NULL;
3327 static HKEY open_shared_dlls_key( MSICOMPONENT *comp, BOOL create, REGSAM access )
3329 static const WCHAR path[] =
3330 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
3331 'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3332 'S','h','a','r','e','d','D','L','L','s',0};
3333 return open_key( comp, HKEY_LOCAL_MACHINE, path, create, access );
3336 static UINT get_shared_dlls_count( MSICOMPONENT *comp )
3338 DWORD count, type, sz = sizeof(count);
3339 HKEY hkey = open_shared_dlls_key( comp, FALSE, KEY_READ );
3340 if (RegQueryValueExW( hkey, comp->FullKeypath, NULL, &type, (BYTE *)&count, &sz )) count = 0;
3341 RegCloseKey( hkey );
3342 return count;
3345 static void write_shared_dlls_count( MSICOMPONENT *comp, const WCHAR *path, INT count )
3347 HKEY hkey = open_shared_dlls_key( comp, TRUE, KEY_SET_VALUE );
3348 if (count > 0)
3349 msi_reg_set_val_dword( hkey, path, count );
3350 else
3351 RegDeleteValueW( hkey, path );
3352 RegCloseKey(hkey);
3355 static void refcount_component( MSIPACKAGE *package, MSICOMPONENT *comp )
3357 MSIFEATURE *feature;
3358 INT count = 0;
3359 BOOL write = FALSE;
3361 /* only refcount DLLs */
3362 if (!comp->KeyPath || comp->assembly || comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3363 comp->Attributes & msidbComponentAttributesODBCDataSource)
3364 write = FALSE;
3365 else
3367 count = get_shared_dlls_count( comp );
3368 write = (count > 0);
3369 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3370 write = TRUE;
3373 /* increment counts */
3374 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3376 ComponentList *cl;
3378 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3379 continue;
3381 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3383 if ( cl->component == comp )
3384 count++;
3388 /* decrement counts */
3389 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3391 ComponentList *cl;
3393 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3394 continue;
3396 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3398 if ( cl->component == comp )
3399 count--;
3403 /* ref count all the files in the component */
3404 if (write)
3406 MSIFILE *file;
3408 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3410 if (file->Component == comp)
3411 write_shared_dlls_count( comp, file->TargetPath, count );
3415 /* add a count for permanent */
3416 if (comp->Attributes & msidbComponentAttributesPermanent)
3417 count ++;
3419 comp->RefCount = count;
3421 if (write)
3422 write_shared_dlls_count( comp, comp->FullKeypath, comp->RefCount );
3425 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3427 if (comp->assembly)
3429 static const WCHAR prefixW[] = {'<','\\',0};
3430 DWORD len = lstrlenW( prefixW ) + lstrlenW( comp->assembly->display_name );
3431 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3433 if (keypath)
3435 lstrcpyW( keypath, prefixW );
3436 lstrcatW( keypath, comp->assembly->display_name );
3438 return keypath;
3440 return resolve_keypath( package, comp );
3443 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3445 WCHAR squashed_pc[SQUASHED_GUID_SIZE], squashed_cc[SQUASHED_GUID_SIZE];
3446 UINT rc;
3447 MSICOMPONENT *comp;
3448 HKEY hkey;
3450 TRACE("\n");
3452 msi_set_sourcedir_props(package, FALSE);
3454 if (package->script == SCRIPT_NONE)
3455 return msi_schedule_action(package, SCRIPT_INSTALL, szProcessComponents);
3457 squash_guid( package->ProductCode, squashed_pc );
3459 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3461 MSIRECORD *uirow;
3462 INSTALLSTATE action;
3464 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3465 if (!comp->ComponentId)
3466 continue;
3468 squash_guid( comp->ComponentId, squashed_cc );
3469 msi_free( comp->FullKeypath );
3470 comp->FullKeypath = build_full_keypath( package, comp );
3472 refcount_component( package, comp );
3474 if (package->need_rollback) action = comp->Installed;
3475 else action = comp->ActionRequest;
3477 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3478 debugstr_w(comp->Component), debugstr_w(squashed_cc),
3479 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3481 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3483 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3484 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3485 else
3486 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3488 if (rc != ERROR_SUCCESS)
3489 continue;
3491 if (comp->Attributes & msidbComponentAttributesPermanent)
3493 static const WCHAR szPermKey[] =
3494 { '0','0','0','0','0','0','0','0','0','0','0','0',
3495 '0','0','0','0','0','0','0','0','0','0','0','0',
3496 '0','0','0','0','0','0','0','0',0 };
3498 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3500 if (action == INSTALLSTATE_LOCAL)
3501 msi_reg_set_val_str( hkey, squashed_pc, comp->FullKeypath );
3502 else
3504 MSIFILE *file;
3505 MSIRECORD *row;
3506 LPWSTR ptr, ptr2;
3507 WCHAR source[MAX_PATH];
3508 WCHAR base[MAX_PATH];
3509 LPWSTR sourcepath;
3511 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3512 static const WCHAR query[] = {
3513 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3514 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3515 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3516 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3517 '`','D','i','s','k','I','d','`',0};
3519 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3520 continue;
3522 if (!(row = MSI_QueryGetRecord(package->db, query, file->Sequence)))
3523 return ERROR_FUNCTION_FAILED;
3525 swprintf(source, ARRAY_SIZE(source), fmt, MSI_RecordGetInteger(row, 1));
3526 ptr2 = wcsrchr(source, '\\') + 1;
3527 msiobj_release(&row->hdr);
3529 lstrcpyW(base, package->PackagePath);
3530 ptr = wcsrchr(base, '\\');
3531 *(ptr + 1) = '\0';
3533 sourcepath = msi_resolve_file_source(package, file);
3534 ptr = sourcepath + lstrlenW(base);
3535 lstrcpyW(ptr2, ptr);
3536 msi_free(sourcepath);
3538 msi_reg_set_val_str( hkey, squashed_pc, source );
3540 RegCloseKey(hkey);
3542 else if (action == INSTALLSTATE_ABSENT)
3544 if (comp->num_clients <= 0)
3546 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3547 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3548 else
3549 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3551 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3553 else
3555 LONG res;
3557 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3558 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3559 else
3560 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3562 if (rc != ERROR_SUCCESS)
3564 WARN( "failed to open component key %u\n", rc );
3565 continue;
3567 res = RegDeleteValueW( hkey, squashed_pc );
3568 RegCloseKey(hkey);
3569 if (res) WARN( "failed to delete component value %d\n", res );
3573 /* UI stuff */
3574 uirow = MSI_CreateRecord(3);
3575 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3576 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3577 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3578 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3579 msiobj_release( &uirow->hdr );
3581 return ERROR_SUCCESS;
3584 typedef struct {
3585 CLSID clsid;
3586 LPWSTR source;
3588 LPWSTR path;
3589 ITypeLib *ptLib;
3590 } typelib_struct;
3592 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3593 LPWSTR lpszName, LONG_PTR lParam)
3595 TLIBATTR *attr;
3596 typelib_struct *tl_struct = (typelib_struct*) lParam;
3597 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3598 int sz;
3599 HRESULT res;
3601 if (!IS_INTRESOURCE(lpszName))
3603 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3604 return TRUE;
3607 sz = lstrlenW(tl_struct->source)+4;
3609 if ((INT_PTR)lpszName == 1)
3610 tl_struct->path = strdupW(tl_struct->source);
3611 else
3613 tl_struct->path = msi_alloc(sz * sizeof(WCHAR));
3614 swprintf(tl_struct->path,sz,fmt,tl_struct->source, lpszName);
3617 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3618 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3619 if (FAILED(res))
3621 msi_free(tl_struct->path);
3622 tl_struct->path = NULL;
3624 return TRUE;
3627 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3628 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3630 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3631 return FALSE;
3634 msi_free(tl_struct->path);
3635 tl_struct->path = NULL;
3637 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3638 ITypeLib_Release(tl_struct->ptLib);
3640 return TRUE;
3643 static HMODULE msi_load_library( MSIPACKAGE *package, const WCHAR *filename, DWORD flags )
3645 HMODULE module;
3646 msi_disable_fs_redirection( package );
3647 module = LoadLibraryExW( filename, NULL, flags );
3648 msi_revert_fs_redirection( package );
3649 return module;
3652 static HRESULT msi_load_typelib( MSIPACKAGE *package, const WCHAR *filename, REGKIND kind, ITypeLib **lib )
3654 HRESULT hr;
3655 msi_disable_fs_redirection( package );
3656 hr = LoadTypeLibEx( filename, kind, lib );
3657 msi_revert_fs_redirection( package );
3658 return hr;
3661 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3663 MSIPACKAGE* package = param;
3664 LPCWSTR component;
3665 MSICOMPONENT *comp;
3666 MSIFILE *file;
3667 typelib_struct tl_struct;
3668 ITypeLib *tlib;
3669 HMODULE module;
3670 HRESULT hr;
3672 component = MSI_RecordGetString(row,3);
3673 comp = msi_get_loaded_component(package,component);
3674 if (!comp)
3675 return ERROR_SUCCESS;
3677 comp->Action = msi_get_component_action( package, comp );
3678 if (comp->Action != INSTALLSTATE_LOCAL)
3680 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3681 return ERROR_SUCCESS;
3684 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3686 TRACE("component has no key path\n");
3687 return ERROR_SUCCESS;
3689 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3691 module = msi_load_library( package, file->TargetPath, LOAD_LIBRARY_AS_DATAFILE );
3692 if (module)
3694 LPCWSTR guid;
3695 guid = MSI_RecordGetString(row,1);
3696 CLSIDFromString( guid, &tl_struct.clsid);
3697 tl_struct.source = strdupW( file->TargetPath );
3698 tl_struct.path = NULL;
3700 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3701 (LONG_PTR)&tl_struct);
3703 if (tl_struct.path)
3705 LPCWSTR helpid, help_path = NULL;
3706 HRESULT res;
3708 helpid = MSI_RecordGetString(row,6);
3710 if (helpid) help_path = msi_get_target_folder( package, helpid );
3711 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3713 if (FAILED(res))
3714 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3715 else
3716 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3718 ITypeLib_Release(tl_struct.ptLib);
3719 msi_free(tl_struct.path);
3721 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3723 FreeLibrary(module);
3724 msi_free(tl_struct.source);
3726 else
3728 hr = msi_load_typelib( package, file->TargetPath, REGKIND_REGISTER, &tlib );
3729 if (FAILED(hr))
3731 ERR("Failed to load type library: %08x\n", hr);
3732 return ERROR_INSTALL_FAILURE;
3735 ITypeLib_Release(tlib);
3738 return ERROR_SUCCESS;
3741 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3743 static const WCHAR query[] = {
3744 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3745 '`','T','y','p','e','L','i','b','`',0};
3746 MSIQUERY *view;
3747 UINT rc;
3749 if (package->script == SCRIPT_NONE)
3750 return msi_schedule_action(package, SCRIPT_INSTALL, szRegisterTypeLibraries);
3752 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3753 if (rc != ERROR_SUCCESS)
3754 return ERROR_SUCCESS;
3756 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3757 msiobj_release(&view->hdr);
3758 return rc;
3761 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3763 MSIPACKAGE *package = param;
3764 LPCWSTR component, guid;
3765 MSICOMPONENT *comp;
3766 GUID libid;
3767 UINT version;
3768 LCID language;
3769 SYSKIND syskind;
3770 HRESULT hr;
3772 component = MSI_RecordGetString( row, 3 );
3773 comp = msi_get_loaded_component( package, component );
3774 if (!comp)
3775 return ERROR_SUCCESS;
3777 comp->Action = msi_get_component_action( package, comp );
3778 if (comp->Action != INSTALLSTATE_ABSENT)
3780 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3781 return ERROR_SUCCESS;
3783 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3785 guid = MSI_RecordGetString( row, 1 );
3786 CLSIDFromString( guid, &libid );
3787 version = MSI_RecordGetInteger( row, 4 );
3788 language = MSI_RecordGetInteger( row, 2 );
3790 #ifdef _WIN64
3791 syskind = SYS_WIN64;
3792 #else
3793 syskind = SYS_WIN32;
3794 #endif
3796 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3797 if (FAILED(hr))
3799 WARN("Failed to unregister typelib: %08x\n", hr);
3802 return ERROR_SUCCESS;
3805 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3807 static const WCHAR query[] = {
3808 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3809 '`','T','y','p','e','L','i','b','`',0};
3810 MSIQUERY *view;
3811 UINT rc;
3813 if (package->script == SCRIPT_NONE)
3814 return msi_schedule_action(package, SCRIPT_INSTALL, szUnregisterTypeLibraries);
3816 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3817 if (rc != ERROR_SUCCESS)
3818 return ERROR_SUCCESS;
3820 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3821 msiobj_release( &view->hdr );
3822 return rc;
3825 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3827 static const WCHAR szlnk[] = {'.','l','n','k',0};
3828 LPCWSTR directory, extension, link_folder;
3829 LPWSTR link_file, filename;
3831 directory = MSI_RecordGetString( row, 2 );
3832 link_folder = msi_get_target_folder( package, directory );
3833 if (!link_folder)
3835 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3836 return NULL;
3838 /* may be needed because of a bug somewhere else */
3839 msi_create_full_path( package, link_folder );
3841 filename = msi_dup_record_field( row, 3 );
3842 msi_reduce_to_long_filename( filename );
3844 extension = wcsrchr( filename, '.' );
3845 if (!extension || wcsicmp( extension, szlnk ))
3847 int len = lstrlenW( filename );
3848 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3849 memcpy( filename + len, szlnk, sizeof(szlnk) );
3851 link_file = msi_build_directory_name( 2, link_folder, filename );
3852 msi_free( filename );
3854 return link_file;
3857 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3859 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3860 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3861 WCHAR *folder, *dest, *path;
3863 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3864 folder = msi_dup_property( package->db, szWindowsFolder );
3865 else
3867 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3868 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3869 msi_free( appdata );
3871 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3872 msi_create_full_path( package, dest );
3873 path = msi_build_directory_name( 2, dest, icon_name );
3874 msi_free( folder );
3875 msi_free( dest );
3876 return path;
3879 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3881 MSIPACKAGE *package = param;
3882 LPWSTR link_file, deformated, path;
3883 LPCWSTR component, target;
3884 MSICOMPONENT *comp;
3885 IShellLinkW *sl = NULL;
3886 IPersistFile *pf = NULL;
3887 HRESULT res;
3889 component = MSI_RecordGetString(row, 4);
3890 comp = msi_get_loaded_component(package, component);
3891 if (!comp)
3892 return ERROR_SUCCESS;
3894 comp->Action = msi_get_component_action( package, comp );
3895 if (comp->Action != INSTALLSTATE_LOCAL)
3897 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3898 return ERROR_SUCCESS;
3900 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3902 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3903 &IID_IShellLinkW, (LPVOID *) &sl );
3905 if (FAILED( res ))
3907 ERR("CLSID_ShellLink not available\n");
3908 goto err;
3911 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3912 if (FAILED( res ))
3914 ERR("QueryInterface(IID_IPersistFile) failed\n");
3915 goto err;
3918 target = MSI_RecordGetString(row, 5);
3919 if (wcschr(target, '['))
3921 deformat_string( package, target, &path );
3922 TRACE("target path is %s\n", debugstr_w(path));
3923 IShellLinkW_SetPath( sl, path );
3924 msi_free( path );
3926 else
3928 FIXME("poorly handled shortcut format, advertised shortcut\n");
3929 path = resolve_keypath( package, comp );
3930 IShellLinkW_SetPath( sl, path );
3931 msi_free( path );
3934 if (!MSI_RecordIsNull(row,6))
3936 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3937 deformat_string(package, arguments, &deformated);
3938 IShellLinkW_SetArguments(sl,deformated);
3939 msi_free(deformated);
3942 if (!MSI_RecordIsNull(row,7))
3944 LPCWSTR description = MSI_RecordGetString(row, 7);
3945 IShellLinkW_SetDescription(sl, description);
3948 if (!MSI_RecordIsNull(row,8))
3949 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3951 if (!MSI_RecordIsNull(row,9))
3953 INT index;
3954 LPCWSTR icon = MSI_RecordGetString(row, 9);
3956 path = msi_build_icon_path(package, icon);
3957 index = MSI_RecordGetInteger(row,10);
3959 /* no value means 0 */
3960 if (index == MSI_NULL_INTEGER)
3961 index = 0;
3963 IShellLinkW_SetIconLocation(sl, path, index);
3964 msi_free(path);
3967 if (!MSI_RecordIsNull(row,11))
3968 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3970 if (!MSI_RecordIsNull(row,12))
3972 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3973 full_path = msi_get_target_folder( package, wkdir );
3974 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3977 link_file = get_link_file(package, row);
3978 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3980 msi_disable_fs_redirection( package );
3981 IPersistFile_Save(pf, link_file, FALSE);
3982 msi_revert_fs_redirection( package );
3984 msi_free(link_file);
3986 err:
3987 if (pf)
3988 IPersistFile_Release( pf );
3989 if (sl)
3990 IShellLinkW_Release( sl );
3992 return ERROR_SUCCESS;
3995 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3997 static const WCHAR query[] = {
3998 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3999 '`','S','h','o','r','t','c','u','t','`',0};
4000 MSIQUERY *view;
4001 HRESULT res;
4002 UINT rc;
4004 if (package->script == SCRIPT_NONE)
4005 return msi_schedule_action(package, SCRIPT_INSTALL, szCreateShortcuts);
4007 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4008 if (rc != ERROR_SUCCESS)
4009 return ERROR_SUCCESS;
4011 res = CoInitialize( NULL );
4013 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4014 msiobj_release(&view->hdr);
4016 if (SUCCEEDED(res)) CoUninitialize();
4017 return rc;
4020 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4022 MSIPACKAGE *package = param;
4023 LPWSTR link_file;
4024 LPCWSTR component;
4025 MSICOMPONENT *comp;
4027 component = MSI_RecordGetString( row, 4 );
4028 comp = msi_get_loaded_component( package, component );
4029 if (!comp)
4030 return ERROR_SUCCESS;
4032 comp->Action = msi_get_component_action( package, comp );
4033 if (comp->Action != INSTALLSTATE_ABSENT)
4035 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4036 return ERROR_SUCCESS;
4038 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
4040 link_file = get_link_file( package, row );
4041 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4042 if (!msi_delete_file( package, link_file )) WARN("Failed to remove shortcut file %u\n", GetLastError());
4043 msi_free( link_file );
4045 return ERROR_SUCCESS;
4048 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4050 static const WCHAR query[] = {
4051 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4052 '`','S','h','o','r','t','c','u','t','`',0};
4053 MSIQUERY *view;
4054 UINT rc;
4056 if (package->script == SCRIPT_NONE)
4057 return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveShortcuts);
4059 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4060 if (rc != ERROR_SUCCESS)
4061 return ERROR_SUCCESS;
4063 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4064 msiobj_release( &view->hdr );
4065 return rc;
4068 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4070 MSIPACKAGE *package = param;
4071 HANDLE handle;
4072 WCHAR *icon_path;
4073 const WCHAR *filename;
4074 char buffer[1024];
4075 DWORD sz;
4076 UINT rc;
4078 filename = MSI_RecordGetString( row, 1 );
4079 if (!filename)
4081 ERR("Unable to get filename\n");
4082 return ERROR_SUCCESS;
4085 icon_path = msi_build_icon_path( package, filename );
4087 TRACE("Creating icon file at %s\n", debugstr_w(icon_path));
4089 handle = msi_create_file( package, icon_path, GENERIC_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL );
4090 if (handle == INVALID_HANDLE_VALUE)
4092 ERR("Unable to create file %s\n", debugstr_w(icon_path));
4093 msi_free( icon_path );
4094 return ERROR_SUCCESS;
4099 DWORD count;
4100 sz = 1024;
4101 rc = MSI_RecordReadStream( row, 2, buffer, &sz );
4102 if (rc != ERROR_SUCCESS)
4104 ERR("Failed to get stream\n");
4105 msi_delete_file( package, icon_path );
4106 break;
4108 WriteFile( handle, buffer, sz, &count, NULL );
4109 } while (sz == 1024);
4111 msi_free( icon_path );
4112 CloseHandle( handle );
4114 return ERROR_SUCCESS;
4117 static UINT msi_publish_icons(MSIPACKAGE *package)
4119 static const WCHAR query[]= {
4120 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4121 '`','I','c','o','n','`',0};
4122 MSIQUERY *view;
4123 UINT r;
4125 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4126 if (r == ERROR_SUCCESS)
4128 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4129 msiobj_release(&view->hdr);
4130 if (r != ERROR_SUCCESS)
4131 return r;
4133 return ERROR_SUCCESS;
4136 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4138 UINT r;
4139 HKEY source;
4140 LPWSTR buffer;
4141 MSIMEDIADISK *disk;
4142 MSISOURCELISTINFO *info;
4144 r = RegCreateKeyW(hkey, szSourceList, &source);
4145 if (r != ERROR_SUCCESS)
4146 return r;
4148 RegCloseKey(source);
4150 buffer = wcsrchr(package->PackagePath, '\\') + 1;
4151 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4152 package->Context, MSICODE_PRODUCT,
4153 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4154 if (r != ERROR_SUCCESS)
4155 return r;
4157 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4158 package->Context, MSICODE_PRODUCT,
4159 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4160 if (r != ERROR_SUCCESS)
4161 return r;
4163 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4164 package->Context, MSICODE_PRODUCT,
4165 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4166 if (r != ERROR_SUCCESS)
4167 return r;
4169 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4171 if (!wcscmp( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4172 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4173 info->options, info->value);
4174 else
4175 MsiSourceListSetInfoW(package->ProductCode, NULL,
4176 info->context, info->options,
4177 info->property, info->value);
4180 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4182 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4183 disk->context, disk->options,
4184 disk->disk_id, disk->volume_label, disk->disk_prompt);
4187 return ERROR_SUCCESS;
4190 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4192 static const WCHAR szARPProductIcon[] =
4193 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4194 static const WCHAR szAssignment[] =
4195 {'A','s','s','i','g','n','m','e','n','t',0};
4196 static const WCHAR szAdvertiseFlags[] =
4197 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4198 static const WCHAR szClients[] =
4199 {'C','l','i','e','n','t','s',0};
4200 static const WCHAR szColon[] = {':',0};
4201 WCHAR *buffer, *ptr, *guids, packcode[SQUASHED_GUID_SIZE];
4202 DWORD langid;
4204 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4205 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4206 msi_free(buffer);
4208 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4209 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4211 /* FIXME */
4212 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4214 buffer = msi_dup_property(package->db, szARPProductIcon);
4215 if (buffer)
4217 LPWSTR path = msi_build_icon_path(package, buffer);
4218 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4219 msi_free(path);
4220 msi_free(buffer);
4223 buffer = msi_dup_property(package->db, szProductVersion);
4224 if (buffer)
4226 DWORD verdword = msi_version_str_to_dword(buffer);
4227 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4228 msi_free(buffer);
4231 msi_reg_set_val_dword(hkey, szAssignment, 0);
4232 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4233 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4234 msi_reg_set_val_str(hkey, szClients, szColon);
4236 if (!(guids = msi_get_package_code(package->db))) return ERROR_OUTOFMEMORY;
4237 if ((ptr = wcschr(guids, ';'))) *ptr = 0;
4238 squash_guid(guids, packcode);
4239 msi_free( guids);
4240 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4242 return ERROR_SUCCESS;
4245 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4247 UINT r;
4248 HKEY hkey;
4249 WCHAR *upgrade, squashed_pc[SQUASHED_GUID_SIZE];
4251 upgrade = msi_dup_property(package->db, szUpgradeCode);
4252 if (!upgrade)
4253 return ERROR_SUCCESS;
4255 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4256 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4257 else
4258 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4260 if (r != ERROR_SUCCESS)
4262 WARN("failed to open upgrade code key\n");
4263 msi_free(upgrade);
4264 return ERROR_SUCCESS;
4266 squash_guid(package->ProductCode, squashed_pc);
4267 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4268 RegCloseKey(hkey);
4269 msi_free(upgrade);
4270 return ERROR_SUCCESS;
4273 static BOOL msi_check_publish(MSIPACKAGE *package)
4275 MSIFEATURE *feature;
4277 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4279 feature->Action = msi_get_feature_action( package, feature );
4280 if (feature->Action == INSTALLSTATE_LOCAL || feature->Action == INSTALLSTATE_SOURCE)
4281 return TRUE;
4284 return FALSE;
4287 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4289 MSIFEATURE *feature;
4291 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4293 feature->Action = msi_get_feature_action( package, feature );
4294 if (feature->Action != INSTALLSTATE_ABSENT)
4295 return FALSE;
4298 return TRUE;
4301 static UINT msi_publish_patches( MSIPACKAGE *package )
4303 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4304 WCHAR patch_squashed[GUID_SIZE];
4305 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4306 LONG res;
4307 MSIPATCHINFO *patch;
4308 UINT r;
4309 WCHAR *p, *all_patches = NULL;
4310 DWORD len = 0;
4312 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4313 if (r != ERROR_SUCCESS)
4314 return ERROR_FUNCTION_FAILED;
4316 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4317 if (res != ERROR_SUCCESS)
4319 r = ERROR_FUNCTION_FAILED;
4320 goto done;
4323 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4324 if (r != ERROR_SUCCESS)
4325 goto done;
4327 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4329 squash_guid( patch->patchcode, patch_squashed );
4330 len += lstrlenW( patch_squashed ) + 1;
4333 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4334 if (!all_patches)
4335 goto done;
4337 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4339 HKEY patch_key;
4341 squash_guid( patch->patchcode, p );
4342 p += lstrlenW( p ) + 1;
4344 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4345 (const BYTE *)patch->transforms,
4346 (lstrlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4347 if (res != ERROR_SUCCESS)
4348 goto done;
4350 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4351 if (r != ERROR_SUCCESS)
4352 goto done;
4354 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4355 (lstrlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4356 RegCloseKey( patch_key );
4357 if (res != ERROR_SUCCESS)
4358 goto done;
4360 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4362 res = GetLastError();
4363 ERR("Unable to copy patch package %d\n", res);
4364 goto done;
4366 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4367 if (res != ERROR_SUCCESS)
4368 goto done;
4370 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state,
4371 sizeof(patch->state) );
4372 if (res != ERROR_SUCCESS)
4374 RegCloseKey( patch_key );
4375 goto done;
4378 res = RegSetValueExW( patch_key, szUninstallable, 0, REG_DWORD, (const BYTE *)&patch->uninstallable,
4379 sizeof(patch->uninstallable) );
4380 RegCloseKey( patch_key );
4381 if (res != ERROR_SUCCESS)
4382 goto done;
4385 all_patches[len] = 0;
4386 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4387 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4388 if (res != ERROR_SUCCESS)
4389 goto done;
4391 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4392 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4393 if (res != ERROR_SUCCESS)
4394 r = ERROR_FUNCTION_FAILED;
4396 done:
4397 RegCloseKey( product_patches_key );
4398 RegCloseKey( patches_key );
4399 RegCloseKey( product_key );
4400 msi_free( all_patches );
4401 return r;
4404 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4406 UINT rc;
4407 HKEY hukey = NULL, hudkey = NULL;
4408 MSIRECORD *uirow;
4409 BOOL republish = FALSE;
4411 if (package->script == SCRIPT_NONE)
4412 return msi_schedule_action(package, SCRIPT_INSTALL, szPublishProduct);
4414 if (!list_empty(&package->patches))
4416 rc = msi_publish_patches(package);
4417 if (rc != ERROR_SUCCESS)
4418 goto end;
4421 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4422 &hukey, FALSE);
4423 if (rc == ERROR_SUCCESS)
4425 WCHAR *package_code;
4427 package_code = msi_reg_get_val_str(hukey, INSTALLPROPERTY_PACKAGECODEW);
4428 if (package_code)
4430 WCHAR *guid;
4432 guid = msi_get_package_code(package->db);
4433 if (guid)
4435 WCHAR packed[SQUASHED_GUID_SIZE];
4437 squash_guid(guid, packed);
4438 msi_free(guid);
4439 if (!wcscmp(packed, package_code))
4441 TRACE("re-publishing product - new package\n");
4442 republish = TRUE;
4445 msi_free(package_code);
4449 /* FIXME: also need to publish if the product is in advertise mode */
4450 if (!republish && !msi_check_publish(package))
4452 if (hukey)
4453 RegCloseKey(hukey);
4454 return ERROR_SUCCESS;
4457 if (!hukey)
4459 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4460 &hukey, TRUE);
4461 if (rc != ERROR_SUCCESS)
4462 goto end;
4465 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4466 NULL, &hudkey, TRUE);
4467 if (rc != ERROR_SUCCESS)
4468 goto end;
4470 rc = msi_publish_upgrade_code(package);
4471 if (rc != ERROR_SUCCESS)
4472 goto end;
4474 rc = msi_publish_product_properties(package, hukey);
4475 if (rc != ERROR_SUCCESS)
4476 goto end;
4478 rc = msi_publish_sourcelist(package, hukey);
4479 if (rc != ERROR_SUCCESS)
4480 goto end;
4482 rc = msi_publish_icons(package);
4484 end:
4485 uirow = MSI_CreateRecord( 1 );
4486 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4487 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4488 msiobj_release( &uirow->hdr );
4490 RegCloseKey(hukey);
4491 RegCloseKey(hudkey);
4492 return rc;
4495 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4497 WCHAR *filename, *ptr, *folder, *ret;
4498 const WCHAR *dirprop;
4500 filename = msi_dup_record_field( row, 2 );
4501 if (filename && (ptr = wcschr( filename, '|' )))
4502 ptr++;
4503 else
4504 ptr = filename;
4506 dirprop = MSI_RecordGetString( row, 3 );
4507 if (dirprop)
4509 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4510 if (!folder) folder = msi_dup_property( package->db, dirprop );
4512 else
4513 folder = msi_dup_property( package->db, szWindowsFolder );
4515 if (!folder)
4517 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4518 msi_free( filename );
4519 return NULL;
4522 ret = msi_build_directory_name( 2, folder, ptr );
4524 msi_free( filename );
4525 msi_free( folder );
4526 return ret;
4529 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4531 MSIPACKAGE *package = param;
4532 LPCWSTR component, section, key, value, identifier;
4533 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4534 MSIRECORD * uirow;
4535 INT action;
4536 MSICOMPONENT *comp;
4538 component = MSI_RecordGetString(row, 8);
4539 comp = msi_get_loaded_component(package,component);
4540 if (!comp)
4541 return ERROR_SUCCESS;
4543 comp->Action = msi_get_component_action( package, comp );
4544 if (comp->Action != INSTALLSTATE_LOCAL)
4546 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4547 return ERROR_SUCCESS;
4550 identifier = MSI_RecordGetString(row,1);
4551 section = MSI_RecordGetString(row,4);
4552 key = MSI_RecordGetString(row,5);
4553 value = MSI_RecordGetString(row,6);
4554 action = MSI_RecordGetInteger(row,7);
4556 deformat_string(package,section,&deformated_section);
4557 deformat_string(package,key,&deformated_key);
4558 deformat_string(package,value,&deformated_value);
4560 fullname = get_ini_file_name(package, row);
4562 if (action == 0)
4564 TRACE("Adding value %s to section %s in %s\n",
4565 debugstr_w(deformated_key), debugstr_w(deformated_section),
4566 debugstr_w(fullname));
4567 WritePrivateProfileStringW(deformated_section, deformated_key,
4568 deformated_value, fullname);
4570 else if (action == 1)
4572 WCHAR returned[10];
4573 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4574 returned, 10, fullname);
4575 if (returned[0] == 0)
4577 TRACE("Adding value %s to section %s in %s\n",
4578 debugstr_w(deformated_key), debugstr_w(deformated_section),
4579 debugstr_w(fullname));
4581 WritePrivateProfileStringW(deformated_section, deformated_key,
4582 deformated_value, fullname);
4585 else if (action == 3)
4586 FIXME("Append to existing section not yet implemented\n");
4588 uirow = MSI_CreateRecord(4);
4589 MSI_RecordSetStringW(uirow,1,identifier);
4590 MSI_RecordSetStringW(uirow,2,deformated_section);
4591 MSI_RecordSetStringW(uirow,3,deformated_key);
4592 MSI_RecordSetStringW(uirow,4,deformated_value);
4593 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4594 msiobj_release( &uirow->hdr );
4596 msi_free(fullname);
4597 msi_free(deformated_key);
4598 msi_free(deformated_value);
4599 msi_free(deformated_section);
4600 return ERROR_SUCCESS;
4603 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4605 static const WCHAR query[] = {
4606 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4607 '`','I','n','i','F','i','l','e','`',0};
4608 MSIQUERY *view;
4609 UINT rc;
4611 if (package->script == SCRIPT_NONE)
4612 return msi_schedule_action(package, SCRIPT_INSTALL, szWriteIniValues);
4614 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4615 if (rc != ERROR_SUCCESS)
4616 return ERROR_SUCCESS;
4618 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4619 msiobj_release(&view->hdr);
4620 return rc;
4623 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4625 MSIPACKAGE *package = param;
4626 LPCWSTR component, section, key, value, identifier;
4627 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4628 MSICOMPONENT *comp;
4629 MSIRECORD *uirow;
4630 INT action;
4632 component = MSI_RecordGetString( row, 8 );
4633 comp = msi_get_loaded_component( package, component );
4634 if (!comp)
4635 return ERROR_SUCCESS;
4637 comp->Action = msi_get_component_action( package, comp );
4638 if (comp->Action != INSTALLSTATE_ABSENT)
4640 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4641 return ERROR_SUCCESS;
4644 identifier = MSI_RecordGetString( row, 1 );
4645 section = MSI_RecordGetString( row, 4 );
4646 key = MSI_RecordGetString( row, 5 );
4647 value = MSI_RecordGetString( row, 6 );
4648 action = MSI_RecordGetInteger( row, 7 );
4650 deformat_string( package, section, &deformated_section );
4651 deformat_string( package, key, &deformated_key );
4652 deformat_string( package, value, &deformated_value );
4654 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4656 filename = get_ini_file_name( package, row );
4658 TRACE("Removing key %s from section %s in %s\n",
4659 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4661 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4663 WARN("Unable to remove key %u\n", GetLastError());
4665 msi_free( filename );
4667 else
4668 FIXME("Unsupported action %d\n", action);
4671 uirow = MSI_CreateRecord( 4 );
4672 MSI_RecordSetStringW( uirow, 1, identifier );
4673 MSI_RecordSetStringW( uirow, 2, deformated_section );
4674 MSI_RecordSetStringW( uirow, 3, deformated_key );
4675 MSI_RecordSetStringW( uirow, 4, deformated_value );
4676 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4677 msiobj_release( &uirow->hdr );
4679 msi_free( deformated_key );
4680 msi_free( deformated_value );
4681 msi_free( deformated_section );
4682 return ERROR_SUCCESS;
4685 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4687 MSIPACKAGE *package = param;
4688 LPCWSTR component, section, key, value, identifier;
4689 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4690 MSICOMPONENT *comp;
4691 MSIRECORD *uirow;
4692 INT action;
4694 component = MSI_RecordGetString( row, 8 );
4695 comp = msi_get_loaded_component( package, component );
4696 if (!comp)
4697 return ERROR_SUCCESS;
4699 comp->Action = msi_get_component_action( package, comp );
4700 if (comp->Action != INSTALLSTATE_LOCAL)
4702 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4703 return ERROR_SUCCESS;
4706 identifier = MSI_RecordGetString( row, 1 );
4707 section = MSI_RecordGetString( row, 4 );
4708 key = MSI_RecordGetString( row, 5 );
4709 value = MSI_RecordGetString( row, 6 );
4710 action = MSI_RecordGetInteger( row, 7 );
4712 deformat_string( package, section, &deformated_section );
4713 deformat_string( package, key, &deformated_key );
4714 deformat_string( package, value, &deformated_value );
4716 if (action == msidbIniFileActionRemoveLine)
4718 filename = get_ini_file_name( package, row );
4720 TRACE("Removing key %s from section %s in %s\n",
4721 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4723 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4725 WARN("Unable to remove key %u\n", GetLastError());
4727 msi_free( filename );
4729 else
4730 FIXME("Unsupported action %d\n", action);
4732 uirow = MSI_CreateRecord( 4 );
4733 MSI_RecordSetStringW( uirow, 1, identifier );
4734 MSI_RecordSetStringW( uirow, 2, deformated_section );
4735 MSI_RecordSetStringW( uirow, 3, deformated_key );
4736 MSI_RecordSetStringW( uirow, 4, deformated_value );
4737 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4738 msiobj_release( &uirow->hdr );
4740 msi_free( deformated_key );
4741 msi_free( deformated_value );
4742 msi_free( deformated_section );
4743 return ERROR_SUCCESS;
4746 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4748 static const WCHAR query[] = {
4749 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4750 '`','I','n','i','F','i','l','e','`',0};
4751 static const WCHAR remove_query[] = {
4752 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4753 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4754 MSIQUERY *view;
4755 UINT rc;
4757 if (package->script == SCRIPT_NONE)
4758 return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveIniValues);
4760 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4761 if (rc == ERROR_SUCCESS)
4763 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4764 msiobj_release( &view->hdr );
4765 if (rc != ERROR_SUCCESS)
4766 return rc;
4768 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4769 if (rc == ERROR_SUCCESS)
4771 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4772 msiobj_release( &view->hdr );
4773 if (rc != ERROR_SUCCESS)
4774 return rc;
4776 return ERROR_SUCCESS;
4779 static void register_dll( const WCHAR *dll, BOOL unregister )
4781 static const WCHAR regW[] =
4782 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4783 static const WCHAR unregW[] =
4784 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4785 PROCESS_INFORMATION pi;
4786 STARTUPINFOW si;
4787 WCHAR *cmd;
4789 if (!(cmd = msi_alloc( lstrlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4791 if (unregister) swprintf( cmd, lstrlenW(dll) + ARRAY_SIZE(unregW), unregW, dll );
4792 else swprintf( cmd, lstrlenW(dll) + ARRAY_SIZE(unregW), regW, dll );
4794 memset( &si, 0, sizeof(STARTUPINFOW) );
4795 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4797 CloseHandle( pi.hThread );
4798 msi_dialog_check_messages( pi.hProcess );
4799 CloseHandle( pi.hProcess );
4801 msi_free( cmd );
4804 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4806 MSIPACKAGE *package = param;
4807 LPCWSTR filename;
4808 MSIFILE *file;
4809 MSIRECORD *uirow;
4811 filename = MSI_RecordGetString( row, 1 );
4812 file = msi_get_loaded_file( package, filename );
4813 if (!file)
4815 WARN("unable to find file %s\n", debugstr_w(filename));
4816 return ERROR_SUCCESS;
4818 file->Component->Action = msi_get_component_action( package, file->Component );
4819 if (file->Component->Action != INSTALLSTATE_LOCAL)
4821 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4822 return ERROR_SUCCESS;
4825 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4826 register_dll( file->TargetPath, FALSE );
4828 uirow = MSI_CreateRecord( 2 );
4829 MSI_RecordSetStringW( uirow, 1, file->File );
4830 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4831 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4832 msiobj_release( &uirow->hdr );
4834 return ERROR_SUCCESS;
4837 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4839 static const WCHAR query[] = {
4840 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4841 '`','S','e','l','f','R','e','g','`',0};
4842 MSIQUERY *view;
4843 UINT rc;
4845 if (package->script == SCRIPT_NONE)
4846 return msi_schedule_action(package, SCRIPT_INSTALL, szSelfRegModules);
4848 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4849 if (rc != ERROR_SUCCESS)
4850 return ERROR_SUCCESS;
4852 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4853 msiobj_release(&view->hdr);
4854 return rc;
4857 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4859 MSIPACKAGE *package = param;
4860 LPCWSTR filename;
4861 MSIFILE *file;
4862 MSIRECORD *uirow;
4864 filename = MSI_RecordGetString( row, 1 );
4865 file = msi_get_loaded_file( package, filename );
4866 if (!file)
4868 WARN("unable to find file %s\n", debugstr_w(filename));
4869 return ERROR_SUCCESS;
4871 file->Component->Action = msi_get_component_action( package, file->Component );
4872 if (file->Component->Action != INSTALLSTATE_ABSENT)
4874 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4875 return ERROR_SUCCESS;
4878 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4879 register_dll( file->TargetPath, TRUE );
4881 uirow = MSI_CreateRecord( 2 );
4882 MSI_RecordSetStringW( uirow, 1, file->File );
4883 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4884 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4885 msiobj_release( &uirow->hdr );
4887 return ERROR_SUCCESS;
4890 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4892 static const WCHAR query[] = {
4893 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4894 '`','S','e','l','f','R','e','g','`',0};
4895 MSIQUERY *view;
4896 UINT rc;
4898 if (package->script == SCRIPT_NONE)
4899 return msi_schedule_action(package, SCRIPT_INSTALL, szSelfUnregModules);
4901 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4902 if (rc != ERROR_SUCCESS)
4903 return ERROR_SUCCESS;
4905 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4906 msiobj_release( &view->hdr );
4907 return rc;
4910 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4912 MSIFEATURE *feature;
4913 UINT rc;
4914 HKEY hkey = NULL, userdata = NULL;
4916 if (package->script == SCRIPT_NONE)
4917 return msi_schedule_action(package, SCRIPT_INSTALL, szPublishFeatures);
4919 if (!msi_check_publish(package))
4920 return ERROR_SUCCESS;
4922 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4923 &hkey, TRUE);
4924 if (rc != ERROR_SUCCESS)
4925 goto end;
4927 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4928 &userdata, TRUE);
4929 if (rc != ERROR_SUCCESS)
4930 goto end;
4932 /* here the guids are base 85 encoded */
4933 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4935 ComponentList *cl;
4936 LPWSTR data = NULL;
4937 GUID clsid;
4938 INT size;
4939 BOOL absent = FALSE;
4940 MSIRECORD *uirow;
4942 if (feature->Level <= 0) continue;
4943 if (feature->Action == INSTALLSTATE_UNKNOWN &&
4944 feature->Installed != INSTALLSTATE_ABSENT) continue;
4946 if (feature->Action != INSTALLSTATE_LOCAL &&
4947 feature->Action != INSTALLSTATE_SOURCE &&
4948 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4950 size = 1;
4951 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4953 size += 21;
4955 if (feature->Feature_Parent)
4956 size += lstrlenW( feature->Feature_Parent )+2;
4958 data = msi_alloc(size * sizeof(WCHAR));
4960 data[0] = 0;
4961 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4963 MSICOMPONENT* component = cl->component;
4964 WCHAR buf[21];
4966 buf[0] = 0;
4967 if (component->ComponentId)
4969 TRACE("From %s\n",debugstr_w(component->ComponentId));
4970 CLSIDFromString(component->ComponentId, &clsid);
4971 encode_base85_guid(&clsid,buf);
4972 TRACE("to %s\n",debugstr_w(buf));
4973 lstrcatW(data,buf);
4977 if (feature->Feature_Parent)
4979 static const WCHAR sep[] = {'\2',0};
4980 lstrcatW(data,sep);
4981 lstrcatW(data,feature->Feature_Parent);
4984 msi_reg_set_val_str( userdata, feature->Feature, data );
4985 msi_free(data);
4987 size = 0;
4988 if (feature->Feature_Parent)
4989 size = lstrlenW(feature->Feature_Parent)*sizeof(WCHAR);
4990 if (!absent)
4992 size += sizeof(WCHAR);
4993 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4994 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4996 else
4998 size += 2*sizeof(WCHAR);
4999 data = msi_alloc(size);
5000 data[0] = 0x6;
5001 data[1] = 0;
5002 if (feature->Feature_Parent)
5003 lstrcpyW( &data[1], feature->Feature_Parent );
5004 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5005 (LPBYTE)data,size);
5006 msi_free(data);
5009 /* the UI chunk */
5010 uirow = MSI_CreateRecord( 1 );
5011 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5012 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5013 msiobj_release( &uirow->hdr );
5014 /* FIXME: call msi_ui_progress? */
5017 end:
5018 RegCloseKey(hkey);
5019 RegCloseKey(userdata);
5020 return rc;
5023 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
5025 UINT r;
5026 HKEY hkey;
5027 MSIRECORD *uirow;
5029 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
5031 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
5032 &hkey, FALSE);
5033 if (r == ERROR_SUCCESS)
5035 RegDeleteValueW(hkey, feature->Feature);
5036 RegCloseKey(hkey);
5039 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5040 &hkey, FALSE);
5041 if (r == ERROR_SUCCESS)
5043 RegDeleteValueW(hkey, feature->Feature);
5044 RegCloseKey(hkey);
5047 uirow = MSI_CreateRecord( 1 );
5048 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5049 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5050 msiobj_release( &uirow->hdr );
5052 return ERROR_SUCCESS;
5055 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5057 MSIFEATURE *feature;
5059 if (package->script == SCRIPT_NONE)
5060 return msi_schedule_action(package, SCRIPT_INSTALL, szUnpublishFeatures);
5062 if (!msi_check_unpublish(package))
5063 return ERROR_SUCCESS;
5065 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5067 msi_unpublish_feature(package, feature);
5070 return ERROR_SUCCESS;
5073 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5075 static const WCHAR date_fmt[] =
5076 {'%','i','%','0','2','i','%','0','2','i',0};
5077 static const WCHAR szEstimatedSize[] =
5078 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5079 static const WCHAR szDisplayVersion[] =
5080 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5081 static const WCHAR szInstallSource[] =
5082 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5083 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5084 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5085 static const WCHAR szAuthorizedCDFPrefix[] =
5086 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5087 static const WCHAR szARPCONTACT[] =
5088 {'A','R','P','C','O','N','T','A','C','T',0};
5089 static const WCHAR szContact[] =
5090 {'C','o','n','t','a','c','t',0};
5091 static const WCHAR szARPCOMMENTS[] =
5092 {'A','R','P','C','O','M','M','E','N','T','S',0};
5093 static const WCHAR szComments[] =
5094 {'C','o','m','m','e','n','t','s',0};
5095 static const WCHAR szProductName[] =
5096 {'P','r','o','d','u','c','t','N','a','m','e',0};
5097 static const WCHAR szDisplayName[] =
5098 {'D','i','s','p','l','a','y','N','a','m','e',0};
5099 static const WCHAR szARPHELPLINK[] =
5100 {'A','R','P','H','E','L','P','L','I','N','K',0};
5101 static const WCHAR szHelpLink[] =
5102 {'H','e','l','p','L','i','n','k',0};
5103 static const WCHAR szARPHELPTELEPHONE[] =
5104 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5105 static const WCHAR szHelpTelephone[] =
5106 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5107 static const WCHAR szARPINSTALLLOCATION[] =
5108 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5109 static const WCHAR szManufacturer[] =
5110 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5111 static const WCHAR szPublisher[] =
5112 {'P','u','b','l','i','s','h','e','r',0};
5113 static const WCHAR szARPREADME[] =
5114 {'A','R','P','R','E','A','D','M','E',0};
5115 static const WCHAR szReadme[] =
5116 {'R','e','a','d','M','e',0};
5117 static const WCHAR szARPSIZE[] =
5118 {'A','R','P','S','I','Z','E',0};
5119 static const WCHAR szSize[] =
5120 {'S','i','z','e',0};
5121 static const WCHAR szARPURLINFOABOUT[] =
5122 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5123 static const WCHAR szURLInfoAbout[] =
5124 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5125 static const WCHAR szARPURLUPDATEINFO[] =
5126 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5127 static const WCHAR szURLUpdateInfo[] =
5128 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5129 static const WCHAR szARPSYSTEMCOMPONENT[] =
5130 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5131 static const WCHAR szSystemComponent[] =
5132 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5133 static const WCHAR szARPNOMODIFY[] =
5134 {'A','R','P','N','O','M','O','D','I','F','Y',0};
5135 static const WCHAR szNoModify[] =
5136 {'N','o','M','o','d','i','f','y',0};
5137 static const WCHAR szARPNOREMOVE[] =
5138 {'A','R','P','N','O','R','E','M','O','V','E',0};
5139 static const WCHAR szNoRemove[] =
5140 {'N','o','R','e','m','o','v','e',0};
5141 static const WCHAR szARPNOREPAIR[] =
5142 {'A','R','P','N','O','R','E','P','A','I','R',0};
5143 static const WCHAR szNoRepair[] =
5144 {'N','o','R','e','p','a','i','r',0};
5146 static const WCHAR *propval[] = {
5147 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5148 szARPCONTACT, szContact,
5149 szARPCOMMENTS, szComments,
5150 szProductName, szDisplayName,
5151 szARPHELPLINK, szHelpLink,
5152 szARPHELPTELEPHONE, szHelpTelephone,
5153 szARPINSTALLLOCATION, szInstallLocation,
5154 szSourceDir, szInstallSource,
5155 szManufacturer, szPublisher,
5156 szARPREADME, szReadme,
5157 szARPSIZE, szSize,
5158 szARPURLINFOABOUT, szURLInfoAbout,
5159 szARPURLUPDATEINFO, szURLUpdateInfo,
5160 NULL
5162 const WCHAR **p = propval;
5163 SYSTEMTIME systime;
5164 DWORD size, langid;
5165 WCHAR date[9], *val, *buffer;
5166 const WCHAR *prop, *key;
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 );
5183 if (msi_get_property_int( package->db, szARPNOREMOVE, 0 ))
5184 msi_reg_set_val_dword( hkey, szNoRemove, 1 );
5185 else
5187 static const WCHAR fmt_install[] =
5188 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5189 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5190 static const WCHAR fmt_uninstall[] =
5191 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5192 '/','X','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5193 static const WCHAR szModifyPath[] =
5194 {'M','o','d','i','f','y','P','a','t','h',0};
5195 static const WCHAR szUninstallString[] =
5196 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5197 const WCHAR *fmt = fmt_install;
5199 if (msi_get_property_int( package->db, szARPNOREPAIR, 0 ))
5200 msi_reg_set_val_dword( hkey, szNoRepair, 1 );
5202 if (msi_get_property_int( package->db, szARPNOMODIFY, 0 ))
5204 msi_reg_set_val_dword( hkey, szNoModify, 1 );
5205 fmt = fmt_uninstall;
5208 size = deformat_string(package, fmt, &buffer) * sizeof(WCHAR);
5209 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5210 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5211 msi_free(buffer);
5214 /* FIXME: Write real Estimated Size when we have it */
5215 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5217 GetLocalTime(&systime);
5218 swprintf(date, ARRAY_SIZE(date), date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5219 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5221 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5222 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5224 buffer = msi_dup_property(package->db, szProductVersion);
5225 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5226 if (buffer)
5228 DWORD verdword = msi_version_str_to_dword(buffer);
5230 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5231 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5232 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5233 msi_free(buffer);
5236 return ERROR_SUCCESS;
5239 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5241 WCHAR *upgrade_code, squashed_pc[SQUASHED_GUID_SIZE];
5242 MSIRECORD *uirow;
5243 HKEY hkey, props, upgrade_key;
5244 UINT rc;
5246 if (package->script == SCRIPT_NONE)
5247 return msi_schedule_action(package, SCRIPT_INSTALL, szRegisterProduct);
5249 /* FIXME: also need to publish if the product is in advertise mode */
5250 if (!msi_get_property_int( package->db, szProductToBeRegistered, 0 )
5251 && !msi_check_publish(package))
5252 return ERROR_SUCCESS;
5254 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5255 if (rc != ERROR_SUCCESS)
5256 return rc;
5258 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5259 if (rc != ERROR_SUCCESS)
5260 goto done;
5262 rc = msi_publish_install_properties(package, hkey);
5263 if (rc != ERROR_SUCCESS)
5264 goto done;
5266 rc = msi_publish_install_properties(package, props);
5267 if (rc != ERROR_SUCCESS)
5268 goto done;
5270 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5271 if (upgrade_code)
5273 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5274 if (rc == ERROR_SUCCESS)
5276 squash_guid( package->ProductCode, squashed_pc );
5277 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5278 RegCloseKey( upgrade_key );
5280 msi_free( upgrade_code );
5282 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5283 package->delete_on_close = FALSE;
5285 done:
5286 uirow = MSI_CreateRecord( 1 );
5287 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5288 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5289 msiobj_release( &uirow->hdr );
5291 RegCloseKey(hkey);
5292 return ERROR_SUCCESS;
5295 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5297 return execute_script(package, SCRIPT_INSTALL);
5300 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5302 MSIPACKAGE *package = param;
5303 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5304 WCHAR *p, *icon_path;
5306 if (!icon) return ERROR_SUCCESS;
5307 if ((icon_path = msi_build_icon_path( package, icon )))
5309 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5310 msi_delete_file( package, icon_path );
5311 if ((p = wcsrchr( icon_path, '\\' )))
5313 *p = 0;
5314 msi_remove_directory( package, icon_path );
5316 msi_free( icon_path );
5318 return ERROR_SUCCESS;
5321 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5323 static const WCHAR query[]= {
5324 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5325 MSIQUERY *view;
5326 UINT r;
5328 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5329 if (r == ERROR_SUCCESS)
5331 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5332 msiobj_release( &view->hdr );
5333 if (r != ERROR_SUCCESS)
5334 return r;
5336 return ERROR_SUCCESS;
5339 static void remove_product_upgrade_code( MSIPACKAGE *package )
5341 WCHAR *code, product[SQUASHED_GUID_SIZE];
5342 HKEY hkey;
5343 LONG res;
5344 DWORD count;
5346 squash_guid( package->ProductCode, product );
5347 if (!(code = msi_dup_property( package->db, szUpgradeCode )))
5349 WARN( "upgrade code not found\n" );
5350 return;
5352 if (!MSIREG_OpenUpgradeCodesKey( code, &hkey, FALSE ))
5354 RegDeleteValueW( hkey, product );
5355 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5356 RegCloseKey( hkey );
5357 if (!res && !count) MSIREG_DeleteUpgradeCodesKey( code );
5359 if (!MSIREG_OpenUserUpgradeCodesKey( code, &hkey, FALSE ))
5361 RegDeleteValueW( hkey, product );
5362 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5363 RegCloseKey( hkey );
5364 if (!res && !count) MSIREG_DeleteUserUpgradeCodesKey( code );
5366 if (!MSIREG_OpenClassesUpgradeCodesKey( code, &hkey, FALSE ))
5368 RegDeleteValueW( hkey, product );
5369 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5370 RegCloseKey( hkey );
5371 if (!res && !count) MSIREG_DeleteClassesUpgradeCodesKey( code );
5374 msi_free( code );
5377 static UINT ACTION_UnpublishProduct(MSIPACKAGE *package)
5379 MSIPATCHINFO *patch;
5381 MSIREG_DeleteProductKey(package->ProductCode);
5382 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5383 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5385 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5386 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5387 MSIREG_DeleteUserProductKey(package->ProductCode);
5388 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5390 remove_product_upgrade_code( package );
5392 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5394 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5395 if (!wcscmp( package->ProductCode, patch->products ))
5397 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5398 patch->delete_on_close = TRUE;
5400 /* FIXME: remove local patch package if this is the last product */
5402 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5403 package->delete_on_close = TRUE;
5405 msi_unpublish_icons( package );
5406 return ERROR_SUCCESS;
5409 static BOOL is_full_uninstall( MSIPACKAGE *package )
5411 MSIFEATURE *feature;
5413 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5415 if (feature->Action != INSTALLSTATE_ABSENT &&
5416 (feature->Installed != INSTALLSTATE_ABSENT || feature->Action != INSTALLSTATE_UNKNOWN))
5417 return FALSE;
5420 return TRUE;
5423 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5425 UINT rc;
5427 /* first do the same as an InstallExecute */
5428 rc = execute_script(package, SCRIPT_INSTALL);
5429 if (rc != ERROR_SUCCESS)
5430 return rc;
5432 /* then handle commit actions */
5433 rc = execute_script(package, SCRIPT_COMMIT);
5434 if (rc != ERROR_SUCCESS)
5435 return rc;
5437 if (is_full_uninstall(package))
5438 rc = ACTION_UnpublishProduct(package);
5440 return rc;
5443 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5445 static const WCHAR RunOnce[] = {
5446 'S','o','f','t','w','a','r','e','\\',
5447 'M','i','c','r','o','s','o','f','t','\\',
5448 'W','i','n','d','o','w','s','\\',
5449 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5450 'R','u','n','O','n','c','e',0};
5451 static const WCHAR InstallRunOnce[] = {
5452 'S','o','f','t','w','a','r','e','\\',
5453 'M','i','c','r','o','s','o','f','t','\\',
5454 'W','i','n','d','o','w','s','\\',
5455 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5456 'I','n','s','t','a','l','l','e','r','\\',
5457 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5459 static const WCHAR msiexec_fmt[] = {
5460 '%','s',
5461 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5462 '\"','%','s','\"',0};
5463 static const WCHAR install_fmt[] = {
5464 '/','I',' ','\"','%','s','\"',' ',
5465 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5466 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5467 WCHAR buffer[256], sysdir[MAX_PATH], squashed_pc[SQUASHED_GUID_SIZE];
5468 HKEY hkey;
5470 squash_guid( package->ProductCode, squashed_pc );
5472 GetSystemDirectoryW(sysdir, ARRAY_SIZE(sysdir));
5473 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5474 swprintf(buffer, ARRAY_SIZE(buffer), msiexec_fmt, sysdir, squashed_pc);
5476 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5477 RegCloseKey(hkey);
5479 TRACE("Reboot command %s\n",debugstr_w(buffer));
5481 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5482 swprintf( buffer, ARRAY_SIZE(buffer), install_fmt, package->ProductCode, squashed_pc );
5484 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5485 RegCloseKey(hkey);
5487 return ERROR_INSTALL_SUSPEND;
5490 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5492 DWORD attrib;
5493 UINT rc;
5496 * We are currently doing what should be done here in the top level Install
5497 * however for Administrative and uninstalls this step will be needed
5499 if (!package->PackagePath)
5500 return ERROR_SUCCESS;
5502 msi_set_sourcedir_props(package, TRUE);
5504 attrib = GetFileAttributesW(package->db->path);
5505 if (attrib == INVALID_FILE_ATTRIBUTES)
5507 MSIRECORD *record;
5508 LPWSTR prompt;
5509 DWORD size = 0;
5511 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5512 package->Context, MSICODE_PRODUCT,
5513 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5514 if (rc == ERROR_MORE_DATA)
5516 prompt = msi_alloc(size * sizeof(WCHAR));
5517 MsiSourceListGetInfoW(package->ProductCode, NULL,
5518 package->Context, MSICODE_PRODUCT,
5519 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5521 else
5522 prompt = strdupW(package->db->path);
5524 record = MSI_CreateRecord(2);
5525 MSI_RecordSetInteger(record, 1, MSIERR_INSERTDISK);
5526 MSI_RecordSetStringW(record, 2, prompt);
5527 msi_free(prompt);
5528 while(attrib == INVALID_FILE_ATTRIBUTES)
5530 MSI_RecordSetStringW(record, 0, NULL);
5531 rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ERROR, record);
5532 if (rc == IDCANCEL)
5533 return ERROR_INSTALL_USEREXIT;
5534 attrib = GetFileAttributesW(package->db->path);
5536 rc = ERROR_SUCCESS;
5538 else
5539 return ERROR_SUCCESS;
5541 return rc;
5544 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5546 HKEY hkey = 0;
5547 LPWSTR buffer, productid = NULL;
5548 UINT i, rc = ERROR_SUCCESS;
5549 MSIRECORD *uirow;
5551 static const WCHAR szPropKeys[][80] =
5553 {'P','r','o','d','u','c','t','I','D',0},
5554 {'U','S','E','R','N','A','M','E',0},
5555 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5556 {0},
5559 static const WCHAR szRegKeys[][80] =
5561 {'P','r','o','d','u','c','t','I','D',0},
5562 {'R','e','g','O','w','n','e','r',0},
5563 {'R','e','g','C','o','m','p','a','n','y',0},
5564 {0},
5567 if (package->script == SCRIPT_NONE)
5568 return msi_schedule_action(package, SCRIPT_INSTALL, szRegisterUser);
5570 if (msi_check_unpublish(package))
5572 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5573 goto end;
5576 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5577 if (!productid)
5578 goto end;
5580 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5581 NULL, &hkey, TRUE);
5582 if (rc != ERROR_SUCCESS)
5583 goto end;
5585 for( i = 0; szPropKeys[i][0]; i++ )
5587 buffer = msi_dup_property( package->db, szPropKeys[i] );
5588 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5589 msi_free( buffer );
5592 end:
5593 uirow = MSI_CreateRecord( 1 );
5594 MSI_RecordSetStringW( uirow, 1, productid );
5595 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5596 msiobj_release( &uirow->hdr );
5598 msi_free(productid);
5599 RegCloseKey(hkey);
5600 return rc;
5603 static UINT iterate_properties(MSIRECORD *record, void *param)
5605 static const WCHAR prop_template[] =
5606 {'P','r','o','p','e','r','t','y','(','S',')',':',' ','[','1',']',' ','=',' ','[','2',']',0};
5607 MSIRECORD *uirow;
5609 uirow = MSI_CloneRecord(record);
5610 if (!uirow) return ERROR_OUTOFMEMORY;
5611 MSI_RecordSetStringW(uirow, 0, prop_template);
5612 MSI_ProcessMessage(param, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow);
5613 msiobj_release(&uirow->hdr);
5615 return ERROR_SUCCESS;
5619 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5621 static const WCHAR prop_query[] =
5622 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','_','P','r','o','p','e','r','t','y','`',0};
5623 WCHAR *productname;
5624 WCHAR *action;
5625 WCHAR *info_template;
5626 MSIQUERY *view;
5627 MSIRECORD *uirow, *uirow_info;
5628 UINT rc;
5630 /* Send COMMONDATA and INFO messages. */
5631 /* FIXME: when should these messages be sent? [see also MsiOpenPackage()] */
5632 uirow = MSI_CreateRecord(3);
5633 if (!uirow) return ERROR_OUTOFMEMORY;
5634 MSI_RecordSetStringW(uirow, 0, NULL);
5635 MSI_RecordSetInteger(uirow, 1, 0);
5636 MSI_RecordSetInteger(uirow, 2, package->num_langids ? package->langids[0] : 0);
5637 MSI_RecordSetInteger(uirow, 3, msi_get_string_table_codepage(package->db->strings));
5638 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5639 /* FIXME: send INSTALLMESSAGE_PROGRESS */
5640 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5642 if (!(needs_ui_sequence(package) && ui_sequence_exists(package)))
5644 uirow_info = MSI_CreateRecord(0);
5645 if (!uirow_info)
5647 msiobj_release(&uirow->hdr);
5648 return ERROR_OUTOFMEMORY;
5650 info_template = msi_get_error_message(package->db, MSIERR_INFO_LOGGINGSTART);
5651 MSI_RecordSetStringW(uirow_info, 0, info_template);
5652 msi_free(info_template);
5653 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow_info);
5654 msiobj_release(&uirow_info->hdr);
5657 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5659 productname = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
5660 MSI_RecordSetInteger(uirow, 1, 1);
5661 MSI_RecordSetStringW(uirow, 2, productname);
5662 MSI_RecordSetStringW(uirow, 3, NULL);
5663 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5664 msiobj_release(&uirow->hdr);
5666 package->LastActionResult = MSI_NULL_INTEGER;
5668 action = msi_dup_property(package->db, szEXECUTEACTION);
5669 if (!action) action = msi_strdupW(szINSTALL, lstrlenW(szINSTALL));
5671 /* Perform the action. Top-level actions trigger a sequence. */
5672 if (!wcscmp(action, szINSTALL))
5674 /* Send ACTIONSTART/INFO and INSTALLSTART. */
5675 ui_actionstart(package, szINSTALL, NULL, NULL);
5676 ui_actioninfo(package, szINSTALL, TRUE, 0);
5677 uirow = MSI_CreateRecord(2);
5678 if (!uirow)
5680 rc = ERROR_OUTOFMEMORY;
5681 goto end;
5683 MSI_RecordSetStringW(uirow, 0, NULL);
5684 MSI_RecordSetStringW(uirow, 1, productname);
5685 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5686 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLSTART, uirow);
5687 msiobj_release(&uirow->hdr);
5689 /* Perform the installation. Always use the ExecuteSequence. */
5690 package->InWhatSequence |= SEQUENCE_EXEC;
5691 rc = ACTION_ProcessExecSequence(package);
5693 /* Send return value and INSTALLEND. */
5694 ui_actioninfo(package, szINSTALL, FALSE, !rc);
5695 uirow = MSI_CreateRecord(3);
5696 if (!uirow)
5698 rc = ERROR_OUTOFMEMORY;
5699 goto end;
5701 MSI_RecordSetStringW(uirow, 0, NULL);
5702 MSI_RecordSetStringW(uirow, 1, productname);
5703 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5704 MSI_RecordSetInteger(uirow, 3, !rc);
5705 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLEND, uirow);
5706 msiobj_release(&uirow->hdr);
5708 else
5709 rc = ACTION_PerformAction(package, action);
5711 /* Send all set properties. */
5712 if (!MSI_OpenQuery(package->db, &view, prop_query))
5714 MSI_IterateRecords(view, NULL, iterate_properties, package);
5715 msiobj_release(&view->hdr);
5718 /* And finally, toggle the cancel off and on. */
5719 uirow = MSI_CreateRecord(2);
5720 if (!uirow)
5722 rc = ERROR_OUTOFMEMORY;
5723 goto end;
5725 MSI_RecordSetStringW(uirow, 0, NULL);
5726 MSI_RecordSetInteger(uirow, 1, 2);
5727 MSI_RecordSetInteger(uirow, 2, 0);
5728 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5729 MSI_RecordSetInteger(uirow, 2, 1);
5730 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5731 msiobj_release(&uirow->hdr);
5733 end:
5734 msi_free(productname);
5735 msi_free(action);
5736 return rc;
5739 static UINT ACTION_INSTALL(MSIPACKAGE *package)
5741 msi_set_property(package->db, szEXECUTEACTION, szINSTALL, -1);
5742 if (needs_ui_sequence(package) && ui_sequence_exists(package))
5744 package->InWhatSequence |= SEQUENCE_UI;
5745 return ACTION_ProcessUISequence(package);
5747 else
5748 return ACTION_ExecuteAction(package);
5751 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5753 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5754 WCHAR productid_85[21], component_85[21], *ret;
5755 GUID clsid;
5756 DWORD sz;
5758 /* > is used if there is a component GUID and < if not. */
5760 productid_85[0] = 0;
5761 component_85[0] = 0;
5762 CLSIDFromString( package->ProductCode, &clsid );
5764 encode_base85_guid( &clsid, productid_85 );
5765 if (component)
5767 CLSIDFromString( component->ComponentId, &clsid );
5768 encode_base85_guid( &clsid, component_85 );
5771 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5772 debugstr_w(component_85));
5774 sz = 20 + lstrlenW( feature ) + 20 + 3;
5775 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5776 if (ret) swprintf( ret, sz, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5777 return ret;
5780 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5782 MSIPACKAGE *package = param;
5783 LPCWSTR compgroupid, component, feature, qualifier, text;
5784 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5785 HKEY hkey = NULL;
5786 UINT rc;
5787 MSICOMPONENT *comp;
5788 MSIFEATURE *feat;
5789 DWORD sz;
5790 MSIRECORD *uirow;
5791 int len;
5793 feature = MSI_RecordGetString(rec, 5);
5794 feat = msi_get_loaded_feature(package, feature);
5795 if (!feat)
5796 return ERROR_SUCCESS;
5798 feat->Action = msi_get_feature_action( package, feat );
5799 if (feat->Action != INSTALLSTATE_LOCAL &&
5800 feat->Action != INSTALLSTATE_SOURCE &&
5801 feat->Action != INSTALLSTATE_ADVERTISED)
5803 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5804 return ERROR_SUCCESS;
5807 component = MSI_RecordGetString(rec, 3);
5808 comp = msi_get_loaded_component(package, component);
5809 if (!comp)
5810 return ERROR_SUCCESS;
5812 compgroupid = MSI_RecordGetString(rec,1);
5813 qualifier = MSI_RecordGetString(rec,2);
5815 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5816 if (rc != ERROR_SUCCESS)
5817 goto end;
5819 advertise = msi_create_component_advertise_string( package, comp, feature );
5820 text = MSI_RecordGetString( rec, 4 );
5821 if (text)
5823 p = msi_alloc( (lstrlenW( advertise ) + lstrlenW( text ) + 1) * sizeof(WCHAR) );
5824 lstrcpyW( p, advertise );
5825 lstrcatW( p, text );
5826 msi_free( advertise );
5827 advertise = p;
5829 existing = msi_reg_get_val_str( hkey, qualifier );
5831 sz = lstrlenW( advertise ) + 1;
5832 if (existing)
5834 for (p = existing; *p; p += len)
5836 len = lstrlenW( p ) + 1;
5837 if (wcscmp( advertise, p )) sz += len;
5840 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5842 rc = ERROR_OUTOFMEMORY;
5843 goto end;
5845 q = output;
5846 if (existing)
5848 for (p = existing; *p; p += len)
5850 len = lstrlenW( p ) + 1;
5851 if (wcscmp( advertise, p ))
5853 memcpy( q, p, len * sizeof(WCHAR) );
5854 q += len;
5858 lstrcpyW( q, advertise );
5859 q[lstrlenW( q ) + 1] = 0;
5861 msi_reg_set_val_multi_str( hkey, qualifier, output );
5863 end:
5864 RegCloseKey(hkey);
5865 msi_free( output );
5866 msi_free( advertise );
5867 msi_free( existing );
5869 /* the UI chunk */
5870 uirow = MSI_CreateRecord( 2 );
5871 MSI_RecordSetStringW( uirow, 1, compgroupid );
5872 MSI_RecordSetStringW( uirow, 2, qualifier);
5873 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5874 msiobj_release( &uirow->hdr );
5875 /* FIXME: call ui_progress? */
5877 return rc;
5881 * At present I am ignoring the advertised components part of this and only
5882 * focusing on the qualified component sets
5884 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5886 static const WCHAR query[] = {
5887 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5888 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5889 MSIQUERY *view;
5890 UINT rc;
5892 if (package->script == SCRIPT_NONE)
5893 return msi_schedule_action(package, SCRIPT_INSTALL, szPublishComponents);
5895 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5896 if (rc != ERROR_SUCCESS)
5897 return ERROR_SUCCESS;
5899 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5900 msiobj_release(&view->hdr);
5901 return rc;
5904 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5906 static const WCHAR szInstallerComponents[] = {
5907 'S','o','f','t','w','a','r','e','\\',
5908 'M','i','c','r','o','s','o','f','t','\\',
5909 'I','n','s','t','a','l','l','e','r','\\',
5910 'C','o','m','p','o','n','e','n','t','s','\\',0};
5912 MSIPACKAGE *package = param;
5913 LPCWSTR compgroupid, component, feature, qualifier;
5914 MSICOMPONENT *comp;
5915 MSIFEATURE *feat;
5916 MSIRECORD *uirow;
5917 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5918 LONG res;
5920 feature = MSI_RecordGetString( rec, 5 );
5921 feat = msi_get_loaded_feature( package, feature );
5922 if (!feat)
5923 return ERROR_SUCCESS;
5925 feat->Action = msi_get_feature_action( package, feat );
5926 if (feat->Action != INSTALLSTATE_ABSENT)
5928 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5929 return ERROR_SUCCESS;
5932 component = MSI_RecordGetString( rec, 3 );
5933 comp = msi_get_loaded_component( package, component );
5934 if (!comp)
5935 return ERROR_SUCCESS;
5937 compgroupid = MSI_RecordGetString( rec, 1 );
5938 qualifier = MSI_RecordGetString( rec, 2 );
5940 squash_guid( compgroupid, squashed );
5941 lstrcpyW( keypath, szInstallerComponents );
5942 lstrcatW( keypath, squashed );
5944 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5945 if (res != ERROR_SUCCESS)
5947 WARN("Unable to delete component key %d\n", res);
5950 uirow = MSI_CreateRecord( 2 );
5951 MSI_RecordSetStringW( uirow, 1, compgroupid );
5952 MSI_RecordSetStringW( uirow, 2, qualifier );
5953 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5954 msiobj_release( &uirow->hdr );
5956 return ERROR_SUCCESS;
5959 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5961 static const WCHAR query[] = {
5962 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5963 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5964 MSIQUERY *view;
5965 UINT rc;
5967 if (package->script == SCRIPT_NONE)
5968 return msi_schedule_action(package, SCRIPT_INSTALL, szUnpublishComponents);
5970 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5971 if (rc != ERROR_SUCCESS)
5972 return ERROR_SUCCESS;
5974 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5975 msiobj_release( &view->hdr );
5976 return rc;
5979 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5981 static const WCHAR query[] =
5982 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5983 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5984 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5985 MSIPACKAGE *package = param;
5986 MSICOMPONENT *component;
5987 MSIRECORD *row;
5988 MSIFILE *file;
5989 SC_HANDLE hscm = NULL, service = NULL;
5990 LPCWSTR comp, key;
5991 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5992 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5993 DWORD serv_type, start_type, err_control;
5994 BOOL is_vital;
5995 SERVICE_DESCRIPTIONW sd = {NULL};
5996 UINT ret = ERROR_SUCCESS;
5998 comp = MSI_RecordGetString( rec, 12 );
5999 component = msi_get_loaded_component( package, comp );
6000 if (!component)
6002 WARN("service component not found\n");
6003 goto done;
6005 component->Action = msi_get_component_action( package, component );
6006 if (component->Action != INSTALLSTATE_LOCAL)
6008 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
6009 goto done;
6011 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
6012 if (!hscm)
6014 ERR("Failed to open the SC Manager!\n");
6015 goto done;
6018 start_type = MSI_RecordGetInteger(rec, 5);
6019 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
6020 goto done;
6022 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
6023 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
6024 serv_type = MSI_RecordGetInteger(rec, 4);
6025 err_control = MSI_RecordGetInteger(rec, 6);
6026 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
6027 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
6028 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
6029 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
6030 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
6031 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
6033 /* Should the complete install fail if CreateService fails? */
6034 is_vital = (err_control & msidbServiceInstallErrorControlVital);
6036 /* Remove the msidbServiceInstallErrorControlVital-flag from err_control.
6037 CreateService (under Windows) would fail if not. */
6038 err_control &= ~msidbServiceInstallErrorControlVital;
6040 /* fetch the service path */
6041 row = MSI_QueryGetRecord(package->db, query, comp);
6042 if (!row)
6044 ERR("Query failed\n");
6045 goto done;
6047 if (!(key = MSI_RecordGetString(row, 6)))
6049 msiobj_release(&row->hdr);
6050 goto done;
6052 file = msi_get_loaded_file(package, key);
6053 msiobj_release(&row->hdr);
6054 if (!file)
6056 ERR("Failed to load the service file\n");
6057 goto done;
6060 if (!args || !args[0]) image_path = file->TargetPath;
6061 else
6063 int len = lstrlenW(file->TargetPath) + lstrlenW(args) + 2;
6064 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
6066 ret = ERROR_OUTOFMEMORY;
6067 goto done;
6070 lstrcpyW(image_path, file->TargetPath);
6071 lstrcatW(image_path, szSpace);
6072 lstrcatW(image_path, args);
6074 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
6075 start_type, err_control, image_path, load_order,
6076 NULL, depends, serv_name, pass);
6078 if (!service)
6080 if (GetLastError() != ERROR_SERVICE_EXISTS)
6082 WARN("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
6083 if (is_vital)
6084 ret = ERROR_INSTALL_FAILURE;
6088 else if (sd.lpDescription)
6090 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
6091 WARN("failed to set service description %u\n", GetLastError());
6094 if (image_path != file->TargetPath) msi_free(image_path);
6095 done:
6096 if (service) CloseServiceHandle(service);
6097 if (hscm) CloseServiceHandle(hscm);
6098 msi_free(name);
6099 msi_free(disp);
6100 msi_free(sd.lpDescription);
6101 msi_free(load_order);
6102 msi_free(serv_name);
6103 msi_free(pass);
6104 msi_free(depends);
6105 msi_free(args);
6107 return ret;
6110 static UINT ACTION_InstallServices( MSIPACKAGE *package )
6112 static const WCHAR query[] = {
6113 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6114 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
6115 MSIQUERY *view;
6116 UINT rc;
6118 if (package->script == SCRIPT_NONE)
6119 return msi_schedule_action(package, SCRIPT_INSTALL, szInstallServices);
6121 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6122 if (rc != ERROR_SUCCESS)
6123 return ERROR_SUCCESS;
6125 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
6126 msiobj_release(&view->hdr);
6127 return rc;
6130 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
6131 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
6133 LPCWSTR *vector, *temp_vector;
6134 LPWSTR p, q;
6135 DWORD sep_len;
6137 static const WCHAR separator[] = {'[','~',']',0};
6139 *numargs = 0;
6140 sep_len = ARRAY_SIZE(separator) - 1;
6142 if (!args)
6143 return NULL;
6145 vector = msi_alloc(sizeof(LPWSTR));
6146 if (!vector)
6147 return NULL;
6149 p = args;
6152 (*numargs)++;
6153 vector[*numargs - 1] = p;
6155 if ((q = wcsstr(p, separator)))
6157 *q = '\0';
6159 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
6160 if (!temp_vector)
6162 msi_free(vector);
6163 return NULL;
6165 vector = temp_vector;
6167 p = q + sep_len;
6169 } while (q);
6171 return vector;
6174 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
6176 MSIPACKAGE *package = param;
6177 MSICOMPONENT *comp;
6178 MSIRECORD *uirow;
6179 SC_HANDLE scm = NULL, service = NULL;
6180 LPCWSTR component, *vector = NULL;
6181 LPWSTR name, args, display_name = NULL;
6182 DWORD event, numargs, len, wait, dummy;
6183 UINT r = ERROR_FUNCTION_FAILED;
6184 SERVICE_STATUS_PROCESS status;
6185 ULONGLONG start_time;
6187 component = MSI_RecordGetString(rec, 6);
6188 comp = msi_get_loaded_component(package, component);
6189 if (!comp)
6190 return ERROR_SUCCESS;
6192 event = MSI_RecordGetInteger( rec, 3 );
6193 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6195 comp->Action = msi_get_component_action( package, comp );
6196 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
6197 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
6199 TRACE("not starting %s\n", debugstr_w(name));
6200 msi_free( name );
6201 return ERROR_SUCCESS;
6204 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6205 wait = MSI_RecordGetInteger(rec, 5);
6207 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
6208 if (!scm)
6210 ERR("Failed to open the service control manager\n");
6211 goto done;
6214 len = 0;
6215 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6216 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6218 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6219 GetServiceDisplayNameW( scm, name, display_name, &len );
6222 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6223 if (!service)
6225 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6226 goto done;
6229 vector = msi_service_args_to_vector(args, &numargs);
6231 if (!StartServiceW(service, numargs, vector) &&
6232 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6234 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6235 goto done;
6238 r = ERROR_SUCCESS;
6239 if (wait)
6241 /* wait for at most 30 seconds for the service to be up and running */
6242 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6243 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6245 TRACE("failed to query service status (%u)\n", GetLastError());
6246 goto done;
6248 start_time = GetTickCount64();
6249 while (status.dwCurrentState == SERVICE_START_PENDING)
6251 if (GetTickCount64() - start_time > 30000) break;
6252 Sleep(1000);
6253 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6254 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6256 TRACE("failed to query service status (%u)\n", GetLastError());
6257 goto done;
6260 if (status.dwCurrentState != SERVICE_RUNNING)
6262 WARN("service failed to start %u\n", status.dwCurrentState);
6263 r = ERROR_FUNCTION_FAILED;
6267 done:
6268 uirow = MSI_CreateRecord( 2 );
6269 MSI_RecordSetStringW( uirow, 1, display_name );
6270 MSI_RecordSetStringW( uirow, 2, name );
6271 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6272 msiobj_release( &uirow->hdr );
6274 if (service) CloseServiceHandle(service);
6275 if (scm) CloseServiceHandle(scm);
6277 msi_free(name);
6278 msi_free(args);
6279 msi_free(vector);
6280 msi_free(display_name);
6281 return r;
6284 static UINT ACTION_StartServices( MSIPACKAGE *package )
6286 static const WCHAR query[] = {
6287 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6288 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6289 MSIQUERY *view;
6290 UINT rc;
6292 if (package->script == SCRIPT_NONE)
6293 return msi_schedule_action(package, SCRIPT_INSTALL, szStartServices);
6295 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6296 if (rc != ERROR_SUCCESS)
6297 return ERROR_SUCCESS;
6299 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6300 msiobj_release(&view->hdr);
6301 return rc;
6304 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6306 DWORD i, needed, count;
6307 ENUM_SERVICE_STATUSW *dependencies;
6308 SERVICE_STATUS ss;
6309 SC_HANDLE depserv;
6310 BOOL stopped, ret = FALSE;
6312 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6313 0, &needed, &count))
6314 return TRUE;
6316 if (GetLastError() != ERROR_MORE_DATA)
6317 return FALSE;
6319 dependencies = msi_alloc(needed);
6320 if (!dependencies)
6321 return FALSE;
6323 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6324 needed, &needed, &count))
6325 goto done;
6327 for (i = 0; i < count; i++)
6329 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6330 SERVICE_STOP | SERVICE_QUERY_STATUS);
6331 if (!depserv)
6332 goto done;
6334 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6335 CloseServiceHandle(depserv);
6336 if (!stopped)
6337 goto done;
6340 ret = TRUE;
6342 done:
6343 msi_free(dependencies);
6344 return ret;
6347 static UINT stop_service( LPCWSTR name )
6349 SC_HANDLE scm = NULL, service = NULL;
6350 SERVICE_STATUS status;
6351 SERVICE_STATUS_PROCESS ssp;
6352 DWORD needed;
6354 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6355 if (!scm)
6357 WARN("Failed to open the SCM: %d\n", GetLastError());
6358 goto done;
6361 service = OpenServiceW(scm, name,
6362 SERVICE_STOP |
6363 SERVICE_QUERY_STATUS |
6364 SERVICE_ENUMERATE_DEPENDENTS);
6365 if (!service)
6367 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6368 goto done;
6371 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6372 sizeof(SERVICE_STATUS_PROCESS), &needed))
6374 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6375 goto done;
6378 if (ssp.dwCurrentState == SERVICE_STOPPED)
6379 goto done;
6381 stop_service_dependents(scm, service);
6383 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6384 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6386 done:
6387 if (service) CloseServiceHandle(service);
6388 if (scm) CloseServiceHandle(scm);
6390 return ERROR_SUCCESS;
6393 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6395 MSIPACKAGE *package = param;
6396 MSICOMPONENT *comp;
6397 MSIRECORD *uirow;
6398 LPCWSTR component;
6399 WCHAR *name, *display_name = NULL;
6400 DWORD event, len;
6401 SC_HANDLE scm;
6403 component = MSI_RecordGetString( rec, 6 );
6404 comp = msi_get_loaded_component( package, component );
6405 if (!comp)
6406 return ERROR_SUCCESS;
6408 event = MSI_RecordGetInteger( rec, 3 );
6409 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6411 comp->Action = msi_get_component_action( package, comp );
6412 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6413 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6415 TRACE("not stopping %s\n", debugstr_w(name));
6416 msi_free( name );
6417 return ERROR_SUCCESS;
6420 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6421 if (!scm)
6423 ERR("Failed to open the service control manager\n");
6424 goto done;
6427 len = 0;
6428 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6429 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6431 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6432 GetServiceDisplayNameW( scm, name, display_name, &len );
6434 CloseServiceHandle( scm );
6436 stop_service( name );
6438 done:
6439 uirow = MSI_CreateRecord( 2 );
6440 MSI_RecordSetStringW( uirow, 1, display_name );
6441 MSI_RecordSetStringW( uirow, 2, name );
6442 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6443 msiobj_release( &uirow->hdr );
6445 msi_free( name );
6446 msi_free( display_name );
6447 return ERROR_SUCCESS;
6450 static UINT ACTION_StopServices( MSIPACKAGE *package )
6452 static const WCHAR query[] = {
6453 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6454 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6455 MSIQUERY *view;
6456 UINT rc;
6458 if (package->script == SCRIPT_NONE)
6459 return msi_schedule_action(package, SCRIPT_INSTALL, szStopServices);
6461 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6462 if (rc != ERROR_SUCCESS)
6463 return ERROR_SUCCESS;
6465 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6466 msiobj_release(&view->hdr);
6467 return rc;
6470 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6472 MSIPACKAGE *package = param;
6473 MSICOMPONENT *comp;
6474 MSIRECORD *uirow;
6475 LPWSTR name = NULL, display_name = NULL;
6476 DWORD event, len;
6477 SC_HANDLE scm = NULL, service = NULL;
6479 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6480 if (!comp)
6481 return ERROR_SUCCESS;
6483 event = MSI_RecordGetInteger( rec, 3 );
6484 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6486 comp->Action = msi_get_component_action( package, comp );
6487 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6488 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6490 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6491 msi_free( name );
6492 return ERROR_SUCCESS;
6494 stop_service( name );
6496 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6497 if (!scm)
6499 WARN("Failed to open the SCM: %d\n", GetLastError());
6500 goto done;
6503 len = 0;
6504 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6505 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6507 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6508 GetServiceDisplayNameW( scm, name, display_name, &len );
6511 service = OpenServiceW( scm, name, DELETE );
6512 if (!service)
6514 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6515 goto done;
6518 if (!DeleteService( service ))
6519 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6521 done:
6522 uirow = MSI_CreateRecord( 2 );
6523 MSI_RecordSetStringW( uirow, 1, display_name );
6524 MSI_RecordSetStringW( uirow, 2, name );
6525 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6526 msiobj_release( &uirow->hdr );
6528 if (service) CloseServiceHandle( service );
6529 if (scm) CloseServiceHandle( scm );
6530 msi_free( name );
6531 msi_free( display_name );
6533 return ERROR_SUCCESS;
6536 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6538 static const WCHAR query[] = {
6539 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6540 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6541 MSIQUERY *view;
6542 UINT rc;
6544 if (package->script == SCRIPT_NONE)
6545 return msi_schedule_action(package, SCRIPT_INSTALL, szDeleteServices);
6547 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6548 if (rc != ERROR_SUCCESS)
6549 return ERROR_SUCCESS;
6551 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6552 msiobj_release( &view->hdr );
6553 return rc;
6556 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6558 MSIPACKAGE *package = param;
6559 LPWSTR driver, driver_path, ptr;
6560 WCHAR outpath[MAX_PATH];
6561 MSIFILE *driver_file = NULL, *setup_file = NULL;
6562 MSICOMPONENT *comp;
6563 MSIRECORD *uirow;
6564 LPCWSTR desc, file_key, component;
6565 DWORD len, usage;
6566 UINT r = ERROR_SUCCESS;
6568 static const WCHAR driver_fmt[] = {
6569 'D','r','i','v','e','r','=','%','s',0};
6570 static const WCHAR setup_fmt[] = {
6571 'S','e','t','u','p','=','%','s',0};
6572 static const WCHAR usage_fmt[] = {
6573 'F','i','l','e','U','s','a','g','e','=','1',0};
6575 component = MSI_RecordGetString( rec, 2 );
6576 comp = msi_get_loaded_component( package, component );
6577 if (!comp)
6578 return ERROR_SUCCESS;
6580 comp->Action = msi_get_component_action( package, comp );
6581 if (comp->Action != INSTALLSTATE_LOCAL)
6583 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6584 return ERROR_SUCCESS;
6586 desc = MSI_RecordGetString(rec, 3);
6588 file_key = MSI_RecordGetString( rec, 4 );
6589 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6591 file_key = MSI_RecordGetString( rec, 5 );
6592 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6594 if (!driver_file)
6596 ERR("ODBC Driver entry not found!\n");
6597 return ERROR_FUNCTION_FAILED;
6600 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6601 if (setup_file)
6602 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6603 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6605 driver = msi_alloc(len * sizeof(WCHAR));
6606 if (!driver)
6607 return ERROR_OUTOFMEMORY;
6609 ptr = driver;
6610 lstrcpyW(ptr, desc);
6611 ptr += lstrlenW(ptr) + 1;
6613 len = swprintf(ptr, len - (ptr - driver), driver_fmt, driver_file->FileName);
6614 ptr += len + 1;
6616 if (setup_file)
6618 len = swprintf(ptr, len - (ptr - driver), setup_fmt, setup_file->FileName);
6619 ptr += len + 1;
6622 lstrcpyW(ptr, usage_fmt);
6623 ptr += lstrlenW(ptr) + 1;
6624 *ptr = '\0';
6626 if (!driver_file->TargetPath)
6628 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6629 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6631 driver_path = strdupW(driver_file->TargetPath);
6632 ptr = wcsrchr(driver_path, '\\');
6633 if (ptr) *ptr = '\0';
6635 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6636 NULL, ODBC_INSTALL_COMPLETE, &usage))
6638 ERR("Failed to install SQL driver!\n");
6639 r = ERROR_FUNCTION_FAILED;
6642 uirow = MSI_CreateRecord( 5 );
6643 MSI_RecordSetStringW( uirow, 1, desc );
6644 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6645 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6646 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6647 msiobj_release( &uirow->hdr );
6649 msi_free(driver);
6650 msi_free(driver_path);
6652 return r;
6655 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6657 MSIPACKAGE *package = param;
6658 LPWSTR translator, translator_path, ptr;
6659 WCHAR outpath[MAX_PATH];
6660 MSIFILE *translator_file = NULL, *setup_file = NULL;
6661 MSICOMPONENT *comp;
6662 MSIRECORD *uirow;
6663 LPCWSTR desc, file_key, component;
6664 DWORD len, usage;
6665 UINT r = ERROR_SUCCESS;
6667 static const WCHAR translator_fmt[] = {
6668 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6669 static const WCHAR setup_fmt[] = {
6670 'S','e','t','u','p','=','%','s',0};
6672 component = MSI_RecordGetString( rec, 2 );
6673 comp = msi_get_loaded_component( package, component );
6674 if (!comp)
6675 return ERROR_SUCCESS;
6677 comp->Action = msi_get_component_action( package, comp );
6678 if (comp->Action != INSTALLSTATE_LOCAL)
6680 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6681 return ERROR_SUCCESS;
6683 desc = MSI_RecordGetString(rec, 3);
6685 file_key = MSI_RecordGetString( rec, 4 );
6686 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6688 file_key = MSI_RecordGetString( rec, 5 );
6689 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6691 if (!translator_file)
6693 ERR("ODBC Translator entry not found!\n");
6694 return ERROR_FUNCTION_FAILED;
6697 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6698 if (setup_file)
6699 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6701 translator = msi_alloc(len * sizeof(WCHAR));
6702 if (!translator)
6703 return ERROR_OUTOFMEMORY;
6705 ptr = translator;
6706 lstrcpyW(ptr, desc);
6707 ptr += lstrlenW(ptr) + 1;
6709 len = swprintf(ptr, len - (ptr - translator), translator_fmt, translator_file->FileName);
6710 ptr += len + 1;
6712 if (setup_file)
6714 len = swprintf(ptr, len - (ptr - translator), setup_fmt, setup_file->FileName);
6715 ptr += len + 1;
6717 *ptr = '\0';
6719 translator_path = strdupW(translator_file->TargetPath);
6720 ptr = wcsrchr(translator_path, '\\');
6721 if (ptr) *ptr = '\0';
6723 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6724 NULL, ODBC_INSTALL_COMPLETE, &usage))
6726 ERR("Failed to install SQL translator!\n");
6727 r = ERROR_FUNCTION_FAILED;
6730 uirow = MSI_CreateRecord( 5 );
6731 MSI_RecordSetStringW( uirow, 1, desc );
6732 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6733 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6734 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6735 msiobj_release( &uirow->hdr );
6737 msi_free(translator);
6738 msi_free(translator_path);
6740 return r;
6743 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6745 MSIPACKAGE *package = param;
6746 MSICOMPONENT *comp;
6747 LPWSTR attrs;
6748 LPCWSTR desc, driver, component;
6749 WORD request = ODBC_ADD_SYS_DSN;
6750 INT registration;
6751 DWORD len;
6752 UINT r = ERROR_SUCCESS;
6753 MSIRECORD *uirow;
6755 static const WCHAR attrs_fmt[] = {
6756 'D','S','N','=','%','s',0 };
6758 component = MSI_RecordGetString( rec, 2 );
6759 comp = msi_get_loaded_component( package, component );
6760 if (!comp)
6761 return ERROR_SUCCESS;
6763 comp->Action = msi_get_component_action( package, comp );
6764 if (comp->Action != INSTALLSTATE_LOCAL)
6766 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6767 return ERROR_SUCCESS;
6770 desc = MSI_RecordGetString(rec, 3);
6771 driver = MSI_RecordGetString(rec, 4);
6772 registration = MSI_RecordGetInteger(rec, 5);
6774 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6775 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6777 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6778 attrs = msi_alloc(len * sizeof(WCHAR));
6779 if (!attrs)
6780 return ERROR_OUTOFMEMORY;
6782 len = swprintf(attrs, len, attrs_fmt, desc);
6783 attrs[len + 1] = 0;
6785 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6786 WARN("Failed to install SQL data source!\n");
6788 uirow = MSI_CreateRecord( 5 );
6789 MSI_RecordSetStringW( uirow, 1, desc );
6790 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6791 MSI_RecordSetInteger( uirow, 3, request );
6792 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6793 msiobj_release( &uirow->hdr );
6795 msi_free(attrs);
6797 return r;
6800 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6802 static const WCHAR driver_query[] = {
6803 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6804 'O','D','B','C','D','r','i','v','e','r',0};
6805 static const WCHAR translator_query[] = {
6806 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6807 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6808 static const WCHAR source_query[] = {
6809 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6810 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6811 MSIQUERY *view;
6812 UINT rc;
6814 if (package->script == SCRIPT_NONE)
6815 return msi_schedule_action(package, SCRIPT_INSTALL, szInstallODBC);
6817 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6818 if (rc == ERROR_SUCCESS)
6820 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6821 msiobj_release(&view->hdr);
6822 if (rc != ERROR_SUCCESS)
6823 return rc;
6825 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6826 if (rc == ERROR_SUCCESS)
6828 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6829 msiobj_release(&view->hdr);
6830 if (rc != ERROR_SUCCESS)
6831 return rc;
6833 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6834 if (rc == ERROR_SUCCESS)
6836 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6837 msiobj_release(&view->hdr);
6838 if (rc != ERROR_SUCCESS)
6839 return rc;
6841 return ERROR_SUCCESS;
6844 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6846 MSIPACKAGE *package = param;
6847 MSICOMPONENT *comp;
6848 MSIRECORD *uirow;
6849 DWORD usage;
6850 LPCWSTR desc, component;
6852 component = MSI_RecordGetString( rec, 2 );
6853 comp = msi_get_loaded_component( package, component );
6854 if (!comp)
6855 return ERROR_SUCCESS;
6857 comp->Action = msi_get_component_action( package, comp );
6858 if (comp->Action != INSTALLSTATE_ABSENT)
6860 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6861 return ERROR_SUCCESS;
6864 desc = MSI_RecordGetString( rec, 3 );
6865 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6867 WARN("Failed to remove ODBC driver\n");
6869 else if (!usage)
6871 FIXME("Usage count reached 0\n");
6874 uirow = MSI_CreateRecord( 2 );
6875 MSI_RecordSetStringW( uirow, 1, desc );
6876 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6877 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6878 msiobj_release( &uirow->hdr );
6880 return ERROR_SUCCESS;
6883 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6885 MSIPACKAGE *package = param;
6886 MSICOMPONENT *comp;
6887 MSIRECORD *uirow;
6888 DWORD usage;
6889 LPCWSTR desc, component;
6891 component = MSI_RecordGetString( rec, 2 );
6892 comp = msi_get_loaded_component( package, component );
6893 if (!comp)
6894 return ERROR_SUCCESS;
6896 comp->Action = msi_get_component_action( package, comp );
6897 if (comp->Action != INSTALLSTATE_ABSENT)
6899 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6900 return ERROR_SUCCESS;
6903 desc = MSI_RecordGetString( rec, 3 );
6904 if (!SQLRemoveTranslatorW( desc, &usage ))
6906 WARN("Failed to remove ODBC translator\n");
6908 else if (!usage)
6910 FIXME("Usage count reached 0\n");
6913 uirow = MSI_CreateRecord( 2 );
6914 MSI_RecordSetStringW( uirow, 1, desc );
6915 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6916 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6917 msiobj_release( &uirow->hdr );
6919 return ERROR_SUCCESS;
6922 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6924 MSIPACKAGE *package = param;
6925 MSICOMPONENT *comp;
6926 MSIRECORD *uirow;
6927 LPWSTR attrs;
6928 LPCWSTR desc, driver, component;
6929 WORD request = ODBC_REMOVE_SYS_DSN;
6930 INT registration;
6931 DWORD len;
6933 static const WCHAR attrs_fmt[] = {
6934 'D','S','N','=','%','s',0 };
6936 component = MSI_RecordGetString( rec, 2 );
6937 comp = msi_get_loaded_component( package, component );
6938 if (!comp)
6939 return ERROR_SUCCESS;
6941 comp->Action = msi_get_component_action( package, comp );
6942 if (comp->Action != INSTALLSTATE_ABSENT)
6944 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6945 return ERROR_SUCCESS;
6948 desc = MSI_RecordGetString( rec, 3 );
6949 driver = MSI_RecordGetString( rec, 4 );
6950 registration = MSI_RecordGetInteger( rec, 5 );
6952 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6953 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6955 len = lstrlenW( attrs_fmt ) + lstrlenW( desc ) + 2; /* \0\0 */
6956 attrs = msi_alloc( len * sizeof(WCHAR) );
6957 if (!attrs)
6958 return ERROR_OUTOFMEMORY;
6960 FIXME("Use ODBCSourceAttribute table\n");
6962 len = swprintf( attrs, len, attrs_fmt, desc );
6963 attrs[len + 1] = 0;
6965 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6967 WARN("Failed to remove ODBC data source\n");
6969 msi_free( attrs );
6971 uirow = MSI_CreateRecord( 3 );
6972 MSI_RecordSetStringW( uirow, 1, desc );
6973 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6974 MSI_RecordSetInteger( uirow, 3, request );
6975 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6976 msiobj_release( &uirow->hdr );
6978 return ERROR_SUCCESS;
6981 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6983 static const WCHAR driver_query[] = {
6984 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6985 'O','D','B','C','D','r','i','v','e','r',0};
6986 static const WCHAR translator_query[] = {
6987 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6988 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6989 static const WCHAR source_query[] = {
6990 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6991 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6992 MSIQUERY *view;
6993 UINT rc;
6995 if (package->script == SCRIPT_NONE)
6996 return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveODBC);
6998 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6999 if (rc == ERROR_SUCCESS)
7001 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
7002 msiobj_release( &view->hdr );
7003 if (rc != ERROR_SUCCESS)
7004 return rc;
7006 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7007 if (rc == ERROR_SUCCESS)
7009 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
7010 msiobj_release( &view->hdr );
7011 if (rc != ERROR_SUCCESS)
7012 return rc;
7014 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
7015 if (rc == ERROR_SUCCESS)
7017 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
7018 msiobj_release( &view->hdr );
7019 if (rc != ERROR_SUCCESS)
7020 return rc;
7022 return ERROR_SUCCESS;
7025 #define ENV_ACT_SETALWAYS 0x1
7026 #define ENV_ACT_SETABSENT 0x2
7027 #define ENV_ACT_REMOVE 0x4
7028 #define ENV_ACT_REMOVEMATCH 0x8
7030 #define ENV_MOD_MACHINE 0x20000000
7031 #define ENV_MOD_APPEND 0x40000000
7032 #define ENV_MOD_PREFIX 0x80000000
7033 #define ENV_MOD_MASK 0xC0000000
7035 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
7037 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
7039 LPCWSTR cptr = *name;
7041 static const WCHAR prefix[] = {'[','~',']',0};
7042 static const int prefix_len = 3;
7044 *flags = 0;
7045 while (*cptr)
7047 if (*cptr == '=')
7048 *flags |= ENV_ACT_SETALWAYS;
7049 else if (*cptr == '+')
7050 *flags |= ENV_ACT_SETABSENT;
7051 else if (*cptr == '-')
7052 *flags |= ENV_ACT_REMOVE;
7053 else if (*cptr == '!')
7054 *flags |= ENV_ACT_REMOVEMATCH;
7055 else if (*cptr == '*')
7056 *flags |= ENV_MOD_MACHINE | ENV_ACT_REMOVE;
7057 else
7058 break;
7060 cptr++;
7061 (*name)++;
7064 if (!*cptr)
7066 ERR("Missing environment variable\n");
7067 return ERROR_FUNCTION_FAILED;
7070 if (*value)
7072 LPCWSTR ptr = *value;
7073 if (!wcsncmp(ptr, prefix, prefix_len))
7075 if (ptr[prefix_len] == szSemiColon[0])
7077 *flags |= ENV_MOD_APPEND;
7078 *value += lstrlenW(prefix);
7080 else
7082 *value = NULL;
7085 else if (lstrlenW(*value) >= prefix_len)
7087 ptr += lstrlenW(ptr) - prefix_len;
7088 if (!wcscmp( ptr, prefix ))
7090 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
7092 *flags |= ENV_MOD_PREFIX;
7093 /* the "[~]" will be removed by deformat_string */;
7095 else
7097 *value = NULL;
7103 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
7104 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
7105 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
7106 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
7108 ERR("Invalid flags: %08x\n", *flags);
7109 return ERROR_FUNCTION_FAILED;
7112 if (!*flags)
7113 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
7115 return ERROR_SUCCESS;
7118 static UINT open_env_key( DWORD flags, HKEY *key )
7120 static const WCHAR user_env[] =
7121 {'E','n','v','i','r','o','n','m','e','n','t',0};
7122 static const WCHAR machine_env[] =
7123 {'S','y','s','t','e','m','\\',
7124 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
7125 'C','o','n','t','r','o','l','\\',
7126 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
7127 'E','n','v','i','r','o','n','m','e','n','t',0};
7128 const WCHAR *env;
7129 HKEY root;
7130 LONG res;
7132 if (flags & ENV_MOD_MACHINE)
7134 env = machine_env;
7135 root = HKEY_LOCAL_MACHINE;
7137 else
7139 env = user_env;
7140 root = HKEY_CURRENT_USER;
7143 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
7144 if (res != ERROR_SUCCESS)
7146 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
7147 return ERROR_FUNCTION_FAILED;
7150 return ERROR_SUCCESS;
7153 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
7155 MSIPACKAGE *package = param;
7156 LPCWSTR name, value, component;
7157 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
7158 DWORD flags, type, size, len, len_value = 0;
7159 UINT res;
7160 HKEY env = NULL;
7161 MSICOMPONENT *comp;
7162 MSIRECORD *uirow;
7163 int action = 0, found = 0;
7165 component = MSI_RecordGetString(rec, 4);
7166 comp = msi_get_loaded_component(package, component);
7167 if (!comp)
7168 return ERROR_SUCCESS;
7170 comp->Action = msi_get_component_action( package, comp );
7171 if (comp->Action != INSTALLSTATE_LOCAL)
7173 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
7174 return ERROR_SUCCESS;
7176 name = MSI_RecordGetString(rec, 2);
7177 value = MSI_RecordGetString(rec, 3);
7179 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7181 res = env_parse_flags(&name, &value, &flags);
7182 if (res != ERROR_SUCCESS || !value)
7183 goto done;
7185 if (value && !deformat_string(package, value, &deformatted))
7187 res = ERROR_OUTOFMEMORY;
7188 goto done;
7191 if ((value = deformatted))
7193 if (flags & ENV_MOD_PREFIX)
7195 p = wcsrchr( value, ';' );
7196 len_value = p - value;
7198 else if (flags & ENV_MOD_APPEND)
7200 value = wcschr( value, ';' ) + 1;
7201 len_value = lstrlenW( value );
7203 else len_value = lstrlenW( value );
7206 res = open_env_key( flags, &env );
7207 if (res != ERROR_SUCCESS)
7208 goto done;
7210 if (flags & ENV_MOD_MACHINE)
7211 action |= 0x20000000;
7213 size = 0;
7214 type = REG_SZ;
7215 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
7216 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7217 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7218 goto done;
7220 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
7222 action = 0x2;
7224 /* Nothing to do. */
7225 if (!value)
7227 res = ERROR_SUCCESS;
7228 goto done;
7230 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
7231 newval = strdupW(value);
7232 if (!newval)
7234 res = ERROR_OUTOFMEMORY;
7235 goto done;
7238 else
7240 action = 0x1;
7242 /* Contrary to MSDN, +-variable to [~];path works */
7243 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7245 res = ERROR_SUCCESS;
7246 goto done;
7249 if (!(p = q = data = msi_alloc( size )))
7251 msi_free(deformatted);
7252 RegCloseKey(env);
7253 return ERROR_OUTOFMEMORY;
7256 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
7257 if (res != ERROR_SUCCESS)
7258 goto done;
7260 if (flags & ENV_ACT_REMOVEMATCH && (!value || !wcscmp( data, value )))
7262 action = 0x4;
7263 res = RegDeleteValueW(env, name);
7264 if (res != ERROR_SUCCESS)
7265 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7266 goto done;
7269 for (;;)
7271 while (*q && *q != ';') q++;
7272 len = q - p;
7273 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
7274 (!p[len] || p[len] == ';'))
7276 found = 1;
7277 break;
7279 if (!*q) break;
7280 p = ++q;
7283 if (found)
7285 TRACE("string already set\n");
7286 goto done;
7289 size = (len_value + 1 + lstrlenW( data ) + 1) * sizeof(WCHAR);
7290 if (!(p = newval = msi_alloc( size )))
7292 res = ERROR_OUTOFMEMORY;
7293 goto done;
7296 if (flags & ENV_MOD_PREFIX)
7298 memcpy( newval, value, len_value * sizeof(WCHAR) );
7299 newval[len_value] = ';';
7300 p = newval + len_value + 1;
7301 action |= 0x80000000;
7304 lstrcpyW( p, data );
7306 if (flags & ENV_MOD_APPEND)
7308 p += lstrlenW( data );
7309 *p++ = ';';
7310 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
7311 action |= 0x40000000;
7314 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7315 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
7316 if (res)
7318 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7321 done:
7322 uirow = MSI_CreateRecord( 3 );
7323 MSI_RecordSetStringW( uirow, 1, name );
7324 MSI_RecordSetStringW( uirow, 2, newval );
7325 MSI_RecordSetInteger( uirow, 3, action );
7326 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7327 msiobj_release( &uirow->hdr );
7329 if (env) RegCloseKey(env);
7330 msi_free(deformatted);
7331 msi_free(data);
7332 msi_free(newval);
7333 return res;
7336 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7338 static const WCHAR query[] = {
7339 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7340 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7341 MSIQUERY *view;
7342 UINT rc;
7344 if (package->script == SCRIPT_NONE)
7345 return msi_schedule_action(package, SCRIPT_INSTALL, szWriteEnvironmentStrings);
7347 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7348 if (rc != ERROR_SUCCESS)
7349 return ERROR_SUCCESS;
7351 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7352 msiobj_release(&view->hdr);
7353 return rc;
7356 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7358 MSIPACKAGE *package = param;
7359 LPCWSTR name, value, component;
7360 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7361 DWORD flags, type, size, len, len_value = 0, len_new_value;
7362 HKEY env;
7363 MSICOMPONENT *comp;
7364 MSIRECORD *uirow;
7365 int action = 0;
7366 LONG res;
7367 UINT r;
7369 component = MSI_RecordGetString( rec, 4 );
7370 comp = msi_get_loaded_component( package, component );
7371 if (!comp)
7372 return ERROR_SUCCESS;
7374 comp->Action = msi_get_component_action( package, comp );
7375 if (comp->Action != INSTALLSTATE_ABSENT)
7377 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7378 return ERROR_SUCCESS;
7380 name = MSI_RecordGetString( rec, 2 );
7381 value = MSI_RecordGetString( rec, 3 );
7383 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7385 r = env_parse_flags( &name, &value, &flags );
7386 if (r != ERROR_SUCCESS)
7387 return r;
7389 if (!(flags & ENV_ACT_REMOVE))
7391 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7392 return ERROR_SUCCESS;
7395 if (value && !deformat_string( package, value, &deformatted ))
7396 return ERROR_OUTOFMEMORY;
7398 if ((value = deformatted))
7400 if (flags & ENV_MOD_PREFIX)
7402 p = wcschr( value, ';' );
7403 len_value = p - value;
7405 else if (flags & ENV_MOD_APPEND)
7407 value = wcschr( value, ';' ) + 1;
7408 len_value = lstrlenW( value );
7410 else len_value = lstrlenW( value );
7413 r = open_env_key( flags, &env );
7414 if (r != ERROR_SUCCESS)
7416 r = ERROR_SUCCESS;
7417 goto done;
7420 if (flags & ENV_MOD_MACHINE)
7421 action |= 0x20000000;
7423 size = 0;
7424 type = REG_SZ;
7425 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7426 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7427 goto done;
7429 if (!(new_value = msi_alloc( size ))) goto done;
7431 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7432 if (res != ERROR_SUCCESS)
7433 goto done;
7435 len_new_value = size / sizeof(WCHAR) - 1;
7436 p = q = new_value;
7437 for (;;)
7439 while (*q && *q != ';') q++;
7440 len = q - p;
7441 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7443 if (*q == ';') q++;
7444 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7445 break;
7447 if (!*q) break;
7448 p = ++q;
7451 if (!new_value[0] || !value)
7453 TRACE("removing %s\n", debugstr_w(name));
7454 res = RegDeleteValueW( env, name );
7455 if (res != ERROR_SUCCESS)
7456 WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
7458 else
7460 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7461 size = (lstrlenW( new_value ) + 1) * sizeof(WCHAR);
7462 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7463 if (res != ERROR_SUCCESS)
7464 WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7467 done:
7468 uirow = MSI_CreateRecord( 3 );
7469 MSI_RecordSetStringW( uirow, 1, name );
7470 MSI_RecordSetStringW( uirow, 2, value );
7471 MSI_RecordSetInteger( uirow, 3, action );
7472 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7473 msiobj_release( &uirow->hdr );
7475 if (env) RegCloseKey( env );
7476 msi_free( deformatted );
7477 msi_free( new_value );
7478 return r;
7481 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7483 static const WCHAR query[] = {
7484 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7485 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7486 MSIQUERY *view;
7487 UINT rc;
7489 if (package->script == SCRIPT_NONE)
7490 return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveEnvironmentStrings);
7492 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7493 if (rc != ERROR_SUCCESS)
7494 return ERROR_SUCCESS;
7496 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7497 msiobj_release( &view->hdr );
7498 return rc;
7501 UINT msi_validate_product_id( MSIPACKAGE *package )
7503 LPWSTR key, template, id;
7504 UINT r = ERROR_SUCCESS;
7506 id = msi_dup_property( package->db, szProductID );
7507 if (id)
7509 msi_free( id );
7510 return ERROR_SUCCESS;
7512 template = msi_dup_property( package->db, szPIDTemplate );
7513 key = msi_dup_property( package->db, szPIDKEY );
7514 if (key && template)
7516 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7517 r = msi_set_property( package->db, szProductID, key, -1 );
7519 msi_free( template );
7520 msi_free( key );
7521 return r;
7524 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7526 return msi_validate_product_id( package );
7529 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7531 TRACE("\n");
7532 package->need_reboot_at_end = 1;
7533 return ERROR_SUCCESS;
7536 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7538 static const WCHAR szAvailableFreeReg[] =
7539 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7540 MSIRECORD *uirow;
7541 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7543 TRACE("%p %d kilobytes\n", package, space);
7545 uirow = MSI_CreateRecord( 1 );
7546 MSI_RecordSetInteger( uirow, 1, space );
7547 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7548 msiobj_release( &uirow->hdr );
7550 return ERROR_SUCCESS;
7553 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7555 TRACE("%p\n", package);
7557 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7558 return ERROR_SUCCESS;
7561 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7563 FIXME("%p\n", package);
7564 return ERROR_SUCCESS;
7567 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7569 static const WCHAR driver_query[] = {
7570 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7571 'O','D','B','C','D','r','i','v','e','r',0};
7572 static const WCHAR translator_query[] = {
7573 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7574 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7575 MSIQUERY *view;
7576 UINT r, count;
7578 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7579 if (r == ERROR_SUCCESS)
7581 count = 0;
7582 r = MSI_IterateRecords( view, &count, NULL, package );
7583 msiobj_release( &view->hdr );
7584 if (r != ERROR_SUCCESS)
7585 return r;
7586 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7588 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7589 if (r == ERROR_SUCCESS)
7591 count = 0;
7592 r = MSI_IterateRecords( view, &count, NULL, package );
7593 msiobj_release( &view->hdr );
7594 if (r != ERROR_SUCCESS)
7595 return r;
7596 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7598 return ERROR_SUCCESS;
7601 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7603 static const WCHAR fmtW[] =
7604 {'m','s','i','e','x','e','c',' ','/','q','n',' ','/','i',' ','%','s',' ',
7605 'R','E','M','O','V','E','=','%','s',0};
7606 MSIPACKAGE *package = param;
7607 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7608 int attrs = MSI_RecordGetInteger( rec, 5 );
7609 UINT len = ARRAY_SIZE( fmtW );
7610 WCHAR *product, *features, *cmd;
7611 STARTUPINFOW si;
7612 PROCESS_INFORMATION info;
7613 BOOL ret;
7615 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7616 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7618 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7620 len += lstrlenW( product );
7621 if (features)
7622 len += lstrlenW( features );
7623 else
7624 len += ARRAY_SIZE( szAll );
7626 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7628 msi_free( product );
7629 msi_free( features );
7630 return ERROR_OUTOFMEMORY;
7632 swprintf( cmd, len, fmtW, product, features ? features : szAll );
7633 msi_free( product );
7634 msi_free( features );
7636 memset( &si, 0, sizeof(STARTUPINFOW) );
7637 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7638 msi_free( cmd );
7639 if (!ret) return GetLastError();
7640 CloseHandle( info.hThread );
7642 WaitForSingleObject( info.hProcess, INFINITE );
7643 CloseHandle( info.hProcess );
7644 return ERROR_SUCCESS;
7647 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7649 static const WCHAR query[] = {
7650 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7651 MSIQUERY *view;
7652 UINT r;
7654 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7655 if (r == ERROR_SUCCESS)
7657 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7658 msiobj_release( &view->hdr );
7659 if (r != ERROR_SUCCESS)
7660 return r;
7662 return ERROR_SUCCESS;
7665 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7667 MSIPACKAGE *package = param;
7668 int attributes = MSI_RecordGetInteger( rec, 5 );
7670 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7672 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7673 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7674 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7675 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7676 HKEY hkey;
7677 UINT r;
7679 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7681 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7682 if (r != ERROR_SUCCESS)
7683 return ERROR_SUCCESS;
7685 else
7687 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7688 if (r != ERROR_SUCCESS)
7689 return ERROR_SUCCESS;
7691 RegCloseKey( hkey );
7693 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7694 debugstr_w(upgrade_code), debugstr_w(version_min),
7695 debugstr_w(version_max), debugstr_w(language));
7697 return ERROR_SUCCESS;
7700 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7702 static const WCHAR query[] = {
7703 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7704 'U','p','g','r','a','d','e',0};
7705 MSIQUERY *view;
7706 UINT r;
7708 if (msi_get_property_int( package->db, szInstalled, 0 ))
7710 TRACE("product is installed, skipping action\n");
7711 return ERROR_SUCCESS;
7713 if (msi_get_property_int( package->db, szPreselected, 0 ))
7715 TRACE("Preselected property is set, not migrating feature states\n");
7716 return ERROR_SUCCESS;
7718 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7719 if (r == ERROR_SUCCESS)
7721 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7722 msiobj_release( &view->hdr );
7723 if (r != ERROR_SUCCESS)
7724 return r;
7726 return ERROR_SUCCESS;
7729 static BOOL msi_bind_image( MSIPACKAGE *package, const char *filename, const char *path )
7731 BOOL ret;
7732 msi_disable_fs_redirection( package );
7733 ret = BindImage( filename, path, NULL );
7734 msi_revert_fs_redirection( package );
7735 return ret;
7738 static void bind_image( MSIPACKAGE *package, const char *filename, const char *path )
7740 if (!msi_bind_image( package, filename, path ))
7742 WARN("failed to bind image %u\n", GetLastError());
7746 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7748 UINT i;
7749 MSIFILE *file;
7750 MSIPACKAGE *package = param;
7751 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7752 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7753 char *filenameA, *pathA;
7754 WCHAR *pathW, **path_list;
7756 if (!(file = msi_get_loaded_file( package, key )))
7758 WARN("file %s not found\n", debugstr_w(key));
7759 return ERROR_SUCCESS;
7761 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7763 path_list = msi_split_string( paths, ';' );
7764 if (!path_list) bind_image( package, filenameA, NULL );
7765 else
7767 for (i = 0; path_list[i] && path_list[i][0]; i++)
7769 deformat_string( package, path_list[i], &pathW );
7770 if ((pathA = strdupWtoA( pathW )))
7772 bind_image( package, filenameA, pathA );
7773 msi_free( pathA );
7775 msi_free( pathW );
7778 msi_free( path_list );
7779 msi_free( filenameA );
7781 return ERROR_SUCCESS;
7784 static UINT ACTION_BindImage( MSIPACKAGE *package )
7786 static const WCHAR query[] = {
7787 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7788 'B','i','n','d','I','m','a','g','e',0};
7789 MSIQUERY *view;
7790 UINT r;
7792 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7793 if (r == ERROR_SUCCESS)
7795 MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7796 msiobj_release( &view->hdr );
7798 return ERROR_SUCCESS;
7801 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7803 static const WCHAR query[] = {
7804 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7805 MSIQUERY *view;
7806 DWORD count = 0;
7807 UINT r;
7809 r = MSI_OpenQuery( package->db, &view, query, table );
7810 if (r == ERROR_SUCCESS)
7812 r = MSI_IterateRecords(view, &count, NULL, package);
7813 msiobj_release(&view->hdr);
7814 if (r != ERROR_SUCCESS)
7815 return r;
7817 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7818 return ERROR_SUCCESS;
7821 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7823 static const WCHAR table[] = {
7824 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7825 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7828 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7830 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7831 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7834 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7836 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7837 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7840 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7842 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7843 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7846 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7848 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7849 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7852 static const struct
7854 const WCHAR *action;
7855 const UINT description;
7856 const UINT template;
7857 UINT (*handler)(MSIPACKAGE *);
7858 const WCHAR *action_rollback;
7860 StandardActions[] =
7862 { szAllocateRegistrySpace, IDS_DESC_ALLOCATEREGISTRYSPACE, IDS_TEMP_ALLOCATEREGISTRYSPACE, ACTION_AllocateRegistrySpace, NULL },
7863 { szAppSearch, IDS_DESC_APPSEARCH, IDS_TEMP_APPSEARCH, ACTION_AppSearch, NULL },
7864 { szBindImage, IDS_DESC_BINDIMAGE, IDS_TEMP_BINDIMAGE, ACTION_BindImage, NULL },
7865 { szCCPSearch, IDS_DESC_CCPSEARCH, 0, ACTION_CCPSearch, NULL },
7866 { szCostFinalize, IDS_DESC_COSTFINALIZE, 0, ACTION_CostFinalize, NULL },
7867 { szCostInitialize, IDS_DESC_COSTINITIALIZE, 0, ACTION_CostInitialize, NULL },
7868 { szCreateFolders, IDS_DESC_CREATEFOLDERS, IDS_TEMP_CREATEFOLDERS, ACTION_CreateFolders, szRemoveFolders },
7869 { szCreateShortcuts, IDS_DESC_CREATESHORTCUTS, IDS_TEMP_CREATESHORTCUTS, ACTION_CreateShortcuts, szRemoveShortcuts },
7870 { szDeleteServices, IDS_DESC_DELETESERVICES, IDS_TEMP_DELETESERVICES, ACTION_DeleteServices, szInstallServices },
7871 { szDisableRollback, 0, 0, ACTION_DisableRollback, NULL },
7872 { szDuplicateFiles, IDS_DESC_DUPLICATEFILES, IDS_TEMP_DUPLICATEFILES, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7873 { szExecuteAction, 0, 0, ACTION_ExecuteAction, NULL },
7874 { szFileCost, IDS_DESC_FILECOST, 0, ACTION_FileCost, NULL },
7875 { szFindRelatedProducts, IDS_DESC_FINDRELATEDPRODUCTS, IDS_TEMP_FINDRELATEDPRODUCTS, ACTION_FindRelatedProducts, NULL },
7876 { szForceReboot, 0, 0, ACTION_ForceReboot, NULL },
7877 { szInstallAdminPackage, IDS_DESC_INSTALLADMINPACKAGE, IDS_TEMP_INSTALLADMINPACKAGE, ACTION_InstallAdminPackage, NULL },
7878 { szInstallExecute, 0, 0, ACTION_InstallExecute, NULL },
7879 { szInstallExecuteAgain, 0, 0, ACTION_InstallExecute, NULL },
7880 { szInstallFiles, IDS_DESC_INSTALLFILES, IDS_TEMP_INSTALLFILES, ACTION_InstallFiles, szRemoveFiles },
7881 { szInstallFinalize, 0, 0, ACTION_InstallFinalize, NULL },
7882 { szInstallInitialize, 0, 0, ACTION_InstallInitialize, NULL },
7883 { szInstallODBC, IDS_DESC_INSTALLODBC, 0, ACTION_InstallODBC, szRemoveODBC },
7884 { szInstallServices, IDS_DESC_INSTALLSERVICES, IDS_TEMP_INSTALLSERVICES, ACTION_InstallServices, szDeleteServices },
7885 { szInstallSFPCatalogFile, IDS_DESC_INSTALLSFPCATALOGFILE, IDS_TEMP_INSTALLSFPCATALOGFILE, ACTION_InstallSFPCatalogFile, NULL },
7886 { szInstallValidate, IDS_DESC_INSTALLVALIDATE, 0, ACTION_InstallValidate, NULL },
7887 { szIsolateComponents, 0, 0, ACTION_IsolateComponents, NULL },
7888 { szLaunchConditions, IDS_DESC_LAUNCHCONDITIONS, 0, ACTION_LaunchConditions, NULL },
7889 { szMigrateFeatureStates, IDS_DESC_MIGRATEFEATURESTATES, IDS_TEMP_MIGRATEFEATURESTATES, ACTION_MigrateFeatureStates, NULL },
7890 { szMoveFiles, IDS_DESC_MOVEFILES, IDS_TEMP_MOVEFILES, ACTION_MoveFiles, NULL },
7891 { szMsiPublishAssemblies, IDS_DESC_MSIPUBLISHASSEMBLIES, IDS_TEMP_MSIPUBLISHASSEMBLIES, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7892 { szMsiUnpublishAssemblies, IDS_DESC_MSIUNPUBLISHASSEMBLIES, IDS_TEMP_MSIUNPUBLISHASSEMBLIES, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7893 { szPatchFiles, IDS_DESC_PATCHFILES, IDS_TEMP_PATCHFILES, ACTION_PatchFiles, NULL },
7894 { szProcessComponents, IDS_DESC_PROCESSCOMPONENTS, 0, ACTION_ProcessComponents, szProcessComponents },
7895 { szPublishComponents, IDS_DESC_PUBLISHCOMPONENTS, IDS_TEMP_PUBLISHCOMPONENTS, ACTION_PublishComponents, szUnpublishComponents },
7896 { szPublishFeatures, IDS_DESC_PUBLISHFEATURES, IDS_TEMP_PUBLISHFEATURES, ACTION_PublishFeatures, szUnpublishFeatures },
7897 { szPublishProduct, IDS_DESC_PUBLISHPRODUCT, 0, ACTION_PublishProduct, szUnpublishProduct },
7898 { szRegisterClassInfo, IDS_DESC_REGISTERCLASSINFO, IDS_TEMP_REGISTERCLASSINFO, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7899 { szRegisterComPlus, IDS_DESC_REGISTERCOMPLUS, IDS_TEMP_REGISTERCOMPLUS, ACTION_RegisterComPlus, szUnregisterComPlus },
7900 { szRegisterExtensionInfo, IDS_DESC_REGISTEREXTENSIONINFO, 0, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7901 { szRegisterFonts, IDS_DESC_REGISTERFONTS, IDS_TEMP_REGISTERFONTS, ACTION_RegisterFonts, szUnregisterFonts },
7902 { szRegisterMIMEInfo, IDS_DESC_REGISTERMIMEINFO, IDS_TEMP_REGISTERMIMEINFO, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7903 { szRegisterProduct, IDS_DESC_REGISTERPRODUCT, 0, ACTION_RegisterProduct, NULL },
7904 { szRegisterProgIdInfo, IDS_DESC_REGISTERPROGIDINFO, IDS_TEMP_REGISTERPROGIDINFO, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7905 { szRegisterTypeLibraries, IDS_DESC_REGISTERTYPELIBRARIES, IDS_TEMP_REGISTERTYPELIBRARIES, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7906 { szRegisterUser, IDS_DESC_REGISTERUSER, 0, ACTION_RegisterUser, NULL },
7907 { szRemoveDuplicateFiles, IDS_DESC_REMOVEDUPLICATEFILES, IDS_TEMP_REMOVEDUPLICATEFILES, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7908 { szRemoveEnvironmentStrings, IDS_DESC_REMOVEENVIRONMENTSTRINGS, IDS_TEMP_REMOVEENVIRONMENTSTRINGS, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7909 { szRemoveExistingProducts, IDS_DESC_REMOVEEXISTINGPRODUCTS, IDS_TEMP_REMOVEEXISTINGPRODUCTS, ACTION_RemoveExistingProducts, NULL },
7910 { szRemoveFiles, IDS_DESC_REMOVEFILES, IDS_TEMP_REMOVEFILES, ACTION_RemoveFiles, szInstallFiles },
7911 { szRemoveFolders, IDS_DESC_REMOVEFOLDERS, IDS_TEMP_REMOVEFOLDERS, ACTION_RemoveFolders, szCreateFolders },
7912 { szRemoveIniValues, IDS_DESC_REMOVEINIVALUES, IDS_TEMP_REMOVEINIVALUES, ACTION_RemoveIniValues, szWriteIniValues },
7913 { szRemoveODBC, IDS_DESC_REMOVEODBC, 0, ACTION_RemoveODBC, szInstallODBC },
7914 { szRemoveRegistryValues, IDS_DESC_REMOVEREGISTRYVALUES, IDS_TEMP_REMOVEREGISTRYVALUES, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7915 { szRemoveShortcuts, IDS_DESC_REMOVESHORTCUTS, IDS_TEMP_REMOVESHORTCUTS, ACTION_RemoveShortcuts, szCreateShortcuts },
7916 { szResolveSource, 0, 0, ACTION_ResolveSource, NULL },
7917 { szRMCCPSearch, IDS_DESC_RMCCPSEARCH, 0, ACTION_RMCCPSearch, NULL },
7918 { szScheduleReboot, 0, 0, ACTION_ScheduleReboot, NULL },
7919 { szSelfRegModules, IDS_DESC_SELFREGMODULES, IDS_TEMP_SELFREGMODULES, ACTION_SelfRegModules, szSelfUnregModules },
7920 { szSelfUnregModules, IDS_DESC_SELFUNREGMODULES, IDS_TEMP_SELFUNREGMODULES, ACTION_SelfUnregModules, szSelfRegModules },
7921 { szSetODBCFolders, IDS_DESC_SETODBCFOLDERS, 0, ACTION_SetODBCFolders, NULL },
7922 { szStartServices, IDS_DESC_STARTSERVICES, IDS_TEMP_STARTSERVICES, ACTION_StartServices, szStopServices },
7923 { szStopServices, IDS_DESC_STOPSERVICES, IDS_TEMP_STOPSERVICES, ACTION_StopServices, szStartServices },
7924 { szUnpublishComponents, IDS_DESC_UNPUBLISHCOMPONENTS, IDS_TEMP_UNPUBLISHCOMPONENTS, ACTION_UnpublishComponents, szPublishComponents },
7925 { szUnpublishFeatures, IDS_DESC_UNPUBLISHFEATURES, IDS_TEMP_UNPUBLISHFEATURES, ACTION_UnpublishFeatures, szPublishFeatures },
7926 { szUnpublishProduct, IDS_DESC_UNPUBLISHPRODUCT, 0, ACTION_UnpublishProduct, NULL }, /* for rollback only */
7927 { szUnregisterClassInfo, IDS_DESC_UNREGISTERCLASSINFO, IDS_TEMP_UNREGISTERCLASSINFO, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7928 { szUnregisterComPlus, IDS_DESC_UNREGISTERCOMPLUS, IDS_TEMP_UNREGISTERCOMPLUS, ACTION_UnregisterComPlus, szRegisterComPlus },
7929 { szUnregisterExtensionInfo, IDS_DESC_UNREGISTEREXTENSIONINFO, IDS_TEMP_UNREGISTEREXTENSIONINFO, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7930 { szUnregisterFonts, IDS_DESC_UNREGISTERFONTS, IDS_TEMP_UNREGISTERFONTS, ACTION_UnregisterFonts, szRegisterFonts },
7931 { szUnregisterMIMEInfo, IDS_DESC_UNREGISTERMIMEINFO, IDS_TEMP_UNREGISTERMIMEINFO, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7932 { szUnregisterProgIdInfo, IDS_DESC_UNREGISTERPROGIDINFO, IDS_TEMP_UNREGISTERPROGIDINFO, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7933 { szUnregisterTypeLibraries, IDS_DESC_UNREGISTERTYPELIBRARIES, IDS_TEMP_UNREGISTERTYPELIBRARIES, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7934 { szValidateProductID, 0, 0, ACTION_ValidateProductID, NULL },
7935 { szWriteEnvironmentStrings, IDS_DESC_WRITEENVIRONMENTSTRINGS, IDS_TEMP_WRITEENVIRONMENTSTRINGS, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7936 { szWriteIniValues, IDS_DESC_WRITEINIVALUES, IDS_TEMP_WRITEINIVALUES, ACTION_WriteIniValues, szRemoveIniValues },
7937 { szWriteRegistryValues, IDS_DESC_WRITEREGISTRYVALUES, IDS_TEMP_WRITEREGISTRYVALUES, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7938 { szINSTALL, 0, 0, ACTION_INSTALL, NULL },
7939 { 0 }
7942 static UINT ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action)
7944 UINT rc = ERROR_FUNCTION_NOT_CALLED;
7945 UINT i;
7947 i = 0;
7948 while (StandardActions[i].action != NULL)
7950 if (!wcscmp( StandardActions[i].action, action ))
7952 WCHAR description[100] = {0}, template[100] = {0};
7954 if (StandardActions[i].description != 0)
7955 LoadStringW(msi_hInstance, StandardActions[i].description, (LPWSTR)&description, 100);
7956 if (StandardActions[i].template != 0)
7957 LoadStringW(msi_hInstance, StandardActions[i].template, (LPWSTR)&template, 100);
7959 ui_actionstart(package, action, description, template);
7960 if (StandardActions[i].handler)
7962 ui_actioninfo( package, action, TRUE, 0 );
7963 rc = StandardActions[i].handler( package );
7964 ui_actioninfo( package, action, FALSE, !rc );
7966 if (StandardActions[i].action_rollback && !package->need_rollback)
7968 TRACE("scheduling rollback action\n");
7969 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7972 else
7974 FIXME("unhandled standard action %s\n", debugstr_w(action));
7975 rc = ERROR_SUCCESS;
7977 break;
7979 i++;
7982 return rc;
7985 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action)
7987 UINT rc;
7989 TRACE("Performing action (%s)\n", debugstr_w(action));
7991 package->action_progress_increment = 0;
7992 rc = ACTION_HandleStandardAction(package, action);
7994 if (rc == ERROR_FUNCTION_NOT_CALLED)
7995 rc = ACTION_HandleCustomAction(package, action);
7997 if (rc == ERROR_FUNCTION_NOT_CALLED)
7998 WARN("unhandled msi action %s\n", debugstr_w(action));
8000 return rc;
8003 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
8005 UINT rc = ERROR_SUCCESS;
8006 MSIRECORD *row;
8008 static const WCHAR query[] =
8009 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
8010 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
8011 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
8012 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
8013 static const WCHAR ui_query[] =
8014 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
8015 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
8016 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
8017 ' ', '=',' ','%','i',0};
8019 if (needs_ui_sequence(package))
8020 row = MSI_QueryGetRecord(package->db, ui_query, seq);
8021 else
8022 row = MSI_QueryGetRecord(package->db, query, seq);
8024 if (row)
8026 LPCWSTR action, cond;
8028 TRACE("Running the actions\n");
8030 /* check conditions */
8031 cond = MSI_RecordGetString(row, 2);
8033 /* this is a hack to skip errors in the condition code */
8034 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
8036 msiobj_release(&row->hdr);
8037 return ERROR_SUCCESS;
8040 action = MSI_RecordGetString(row, 1);
8041 if (!action)
8043 ERR("failed to fetch action\n");
8044 msiobj_release(&row->hdr);
8045 return ERROR_FUNCTION_FAILED;
8048 rc = ACTION_PerformAction(package, action);
8050 msiobj_release(&row->hdr);
8053 return rc;
8056 /****************************************************
8057 * TOP level entry points
8058 *****************************************************/
8060 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
8061 LPCWSTR szCommandLine )
8063 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
8064 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
8065 WCHAR *reinstall = NULL, *productcode, *action;
8066 UINT rc;
8067 DWORD len = 0;
8069 if (szPackagePath)
8071 LPWSTR p, dir;
8072 LPCWSTR file;
8074 dir = strdupW(szPackagePath);
8075 p = wcsrchr(dir, '\\');
8076 if (p)
8078 *(++p) = 0;
8079 file = szPackagePath + (p - dir);
8081 else
8083 msi_free(dir);
8084 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
8085 GetCurrentDirectoryW(MAX_PATH, dir);
8086 lstrcatW(dir, szBackSlash);
8087 file = szPackagePath;
8090 msi_free( package->PackagePath );
8091 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
8092 if (!package->PackagePath)
8094 msi_free(dir);
8095 return ERROR_OUTOFMEMORY;
8098 lstrcpyW(package->PackagePath, dir);
8099 lstrcatW(package->PackagePath, file);
8100 msi_free(dir);
8102 msi_set_sourcedir_props(package, FALSE);
8105 rc = msi_parse_command_line( package, szCommandLine, FALSE );
8106 if (rc != ERROR_SUCCESS)
8107 return rc;
8109 msi_apply_transforms( package );
8110 msi_apply_patches( package );
8112 if (msi_get_property( package->db, szAction, NULL, &len ))
8113 msi_set_property( package->db, szAction, szINSTALL, -1 );
8114 action = msi_dup_property( package->db, szAction );
8115 CharUpperW(action);
8117 msi_set_original_database_property( package->db, szPackagePath );
8118 msi_parse_command_line( package, szCommandLine, FALSE );
8119 msi_adjust_privilege_properties( package );
8120 msi_set_context( package );
8122 productcode = msi_dup_property( package->db, szProductCode );
8123 if (wcsicmp( productcode, package->ProductCode ))
8125 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
8126 msi_free( package->ProductCode );
8127 package->ProductCode = productcode;
8129 else msi_free( productcode );
8131 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
8133 TRACE("disabling rollback\n");
8134 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
8137 rc = ACTION_PerformAction(package, action);
8139 /* process the ending type action */
8140 if (rc == ERROR_SUCCESS)
8141 ACTION_PerformActionSequence(package, -1);
8142 else if (rc == ERROR_INSTALL_USEREXIT)
8143 ACTION_PerformActionSequence(package, -2);
8144 else if (rc == ERROR_INSTALL_SUSPEND)
8145 ACTION_PerformActionSequence(package, -4);
8146 else /* failed */
8148 ACTION_PerformActionSequence(package, -3);
8149 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
8151 package->need_rollback = TRUE;
8155 /* finish up running custom actions */
8156 ACTION_FinishCustomActions(package);
8158 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
8160 WARN("installation failed, running rollback script\n");
8161 execute_script( package, SCRIPT_ROLLBACK );
8163 msi_free( reinstall );
8164 msi_free( action );
8166 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
8167 return ERROR_SUCCESS_REBOOT_REQUIRED;
8169 return rc;