msi: Fix installations with only INSTALLSTATE_SOURCE features.
[wine.git] / dlls / msi / action.c
blobda7af0609e3bd93a92bb793c97048ccfddd0512d
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 "wine/unicode.h"
40 #include "winver.h"
42 #include "msipriv.h"
43 #include "resource.h"
45 #define REG_PROGRESS_VALUE 13200
46 #define COMPONENT_PROGRESS_VALUE 24000
48 WINE_DEFAULT_DEBUG_CHANNEL(msi);
50 static const WCHAR szCreateFolders[] =
51 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
52 static const WCHAR szCostFinalize[] =
53 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
54 static const WCHAR szWriteRegistryValues[] =
55 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
56 static const WCHAR szFileCost[] =
57 {'F','i','l','e','C','o','s','t',0};
58 static const WCHAR szInstallInitialize[] =
59 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
60 static const WCHAR szInstallValidate[] =
61 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
62 static const WCHAR szLaunchConditions[] =
63 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
64 static const WCHAR szProcessComponents[] =
65 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
66 static const WCHAR szRegisterTypeLibraries[] =
67 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
68 static const WCHAR szCreateShortcuts[] =
69 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
70 static const WCHAR szPublishProduct[] =
71 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
72 static const WCHAR szWriteIniValues[] =
73 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
74 static const WCHAR szSelfRegModules[] =
75 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
76 static const WCHAR szPublishFeatures[] =
77 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
78 static const WCHAR szRegisterProduct[] =
79 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
80 static const WCHAR szInstallExecute[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
82 static const WCHAR szInstallExecuteAgain[] =
83 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
84 static const WCHAR szInstallFinalize[] =
85 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
86 static const WCHAR szForceReboot[] =
87 {'F','o','r','c','e','R','e','b','o','o','t',0};
88 static const WCHAR szResolveSource[] =
89 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
90 static const WCHAR szAllocateRegistrySpace[] =
91 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
92 static const WCHAR szBindImage[] =
93 {'B','i','n','d','I','m','a','g','e',0};
94 static const WCHAR szDeleteServices[] =
95 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
96 static const WCHAR szDisableRollback[] =
97 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
98 static const WCHAR szExecuteAction[] =
99 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
100 static const WCHAR szInstallAdminPackage[] =
101 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
102 static const WCHAR szInstallSFPCatalogFile[] =
103 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
104 static const WCHAR szIsolateComponents[] =
105 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
106 static const WCHAR szMigrateFeatureStates[] =
107 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
108 static const WCHAR szMsiUnpublishAssemblies[] =
109 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
110 static const WCHAR szInstallODBC[] =
111 {'I','n','s','t','a','l','l','O','D','B','C',0};
112 static const WCHAR szInstallServices[] =
113 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
114 static const WCHAR szPublishComponents[] =
115 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
116 static const WCHAR szRegisterComPlus[] =
117 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
118 static const WCHAR szRegisterUser[] =
119 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
120 static const WCHAR szRemoveEnvironmentStrings[] =
121 {'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};
122 static const WCHAR szRemoveExistingProducts[] =
123 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
124 static const WCHAR szRemoveFolders[] =
125 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
126 static const WCHAR szRemoveIniValues[] =
127 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
128 static const WCHAR szRemoveODBC[] =
129 {'R','e','m','o','v','e','O','D','B','C',0};
130 static const WCHAR szRemoveRegistryValues[] =
131 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
132 static const WCHAR szRemoveShortcuts[] =
133 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
134 static const WCHAR szRMCCPSearch[] =
135 {'R','M','C','C','P','S','e','a','r','c','h',0};
136 static const WCHAR szScheduleReboot[] =
137 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
138 static const WCHAR szSelfUnregModules[] =
139 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
140 static const WCHAR szSetODBCFolders[] =
141 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
142 static const WCHAR szStartServices[] =
143 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szStopServices[] =
145 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
146 static const WCHAR szUnpublishComponents[] =
147 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
148 static const WCHAR szUnpublishFeatures[] =
149 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
150 static const WCHAR szUnpublishProduct[] =
151 {'U','n','p','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
152 static const WCHAR szUnregisterComPlus[] =
153 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
154 static const WCHAR szUnregisterTypeLibraries[] =
155 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
156 static const WCHAR szValidateProductID[] =
157 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
158 static const WCHAR szWriteEnvironmentStrings[] =
159 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
160 static const WCHAR szINSTALL[] =
161 {'I','N','S','T','A','L','L',0};
163 static INT ui_actionstart(MSIPACKAGE *package, LPCWSTR action, LPCWSTR description, LPCWSTR template)
165 WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
166 '`','A','c','t','i','o','n','T','e','x','t','`',' ','W','H','E','R','E',' ',
167 '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
168 MSIRECORD *row, *textrow;
169 INT rc;
171 textrow = MSI_QueryGetRecord(package->db, query, action);
172 if (textrow)
174 description = MSI_RecordGetString(textrow, 2);
175 template = MSI_RecordGetString(textrow, 3);
178 row = MSI_CreateRecord(3);
179 if (!row) return -1;
180 MSI_RecordSetStringW(row, 1, action);
181 MSI_RecordSetStringW(row, 2, description);
182 MSI_RecordSetStringW(row, 3, template);
183 rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
184 if (textrow) msiobj_release(&textrow->hdr);
185 msiobj_release(&row->hdr);
186 return rc;
189 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
190 INT rc)
192 MSIRECORD *row;
193 WCHAR *template;
195 template = msi_get_error_message(package->db, start ? MSIERR_INFO_ACTIONSTART : MSIERR_INFO_ACTIONENDED);
197 row = MSI_CreateRecord(2);
198 if (!row) return;
199 MSI_RecordSetStringW(row, 0, template);
200 MSI_RecordSetStringW(row, 1, action);
201 MSI_RecordSetInteger(row, 2, start ? package->LastActionResult : rc);
202 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
203 msiobj_release(&row->hdr);
204 msi_free(template);
205 if (!start) package->LastActionResult = rc;
208 enum parse_state
210 state_whitespace,
211 state_token,
212 state_quote
215 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
217 enum parse_state state = state_quote;
218 const WCHAR *p;
219 WCHAR *out = value;
220 BOOL ignore, in_quotes = FALSE;
221 int count = 0, len = 0;
223 for (p = str; *p; p++)
225 ignore = FALSE;
226 switch (state)
228 case state_whitespace:
229 switch (*p)
231 case ' ':
232 in_quotes = TRUE;
233 ignore = TRUE;
234 len++;
235 break;
236 case '"':
237 state = state_quote;
238 if (in_quotes && p[1] != '\"') count--;
239 else count++;
240 break;
241 default:
242 state = state_token;
243 in_quotes = TRUE;
244 len++;
245 break;
247 break;
249 case state_token:
250 switch (*p)
252 case '"':
253 state = state_quote;
254 if (in_quotes) count--;
255 else count++;
256 break;
257 case ' ':
258 state = state_whitespace;
259 if (!count) goto done;
260 in_quotes = TRUE;
261 len++;
262 break;
263 default:
264 if (count) in_quotes = TRUE;
265 len++;
266 break;
268 break;
270 case state_quote:
271 switch (*p)
273 case '"':
274 if (in_quotes && p[1] != '\"') count--;
275 else count++;
276 break;
277 case ' ':
278 state = state_whitespace;
279 if (!count || (count > 1 && !len)) goto done;
280 in_quotes = TRUE;
281 len++;
282 break;
283 default:
284 state = state_token;
285 if (count) in_quotes = TRUE;
286 len++;
287 break;
289 break;
291 default: break;
293 if (!ignore) *out++ = *p;
294 if (!count) in_quotes = FALSE;
297 done:
298 if (!len) *value = 0;
299 else *out = 0;
301 *quotes = count;
302 return p - str;
305 static void remove_quotes( WCHAR *str )
307 WCHAR *p = str;
308 int len = strlenW( str );
310 while ((p = strchrW( p, '"' )))
312 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
313 p++;
317 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
318 BOOL preserve_case )
320 LPCWSTR ptr, ptr2;
321 int num_quotes;
322 DWORD len;
323 WCHAR *prop, *val;
324 UINT r;
326 if (!szCommandLine)
327 return ERROR_SUCCESS;
329 ptr = szCommandLine;
330 while (*ptr)
332 while (*ptr == ' ') ptr++;
333 if (!*ptr) break;
335 ptr2 = strchrW( ptr, '=' );
336 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
338 len = ptr2 - ptr;
339 if (!len) return ERROR_INVALID_COMMAND_LINE;
341 while (ptr[len - 1] == ' ') len--;
343 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
344 memcpy( prop, ptr, len * sizeof(WCHAR) );
345 prop[len] = 0;
346 if (!preserve_case) struprW( prop );
348 ptr2++;
349 while (*ptr2 == ' ') ptr2++;
351 num_quotes = 0;
352 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
353 len = parse_prop( ptr2, val, &num_quotes );
354 if (num_quotes % 2)
356 WARN("unbalanced quotes\n");
357 msi_free( val );
358 msi_free( prop );
359 return ERROR_INVALID_COMMAND_LINE;
361 remove_quotes( val );
362 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
364 r = msi_set_property( package->db, prop, val, -1 );
365 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
366 msi_reset_folders( package, TRUE );
368 msi_free( val );
369 msi_free( prop );
371 ptr = ptr2 + len;
374 return ERROR_SUCCESS;
377 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
379 LPCWSTR pc;
380 LPWSTR p, *ret = NULL;
381 UINT count = 0;
383 if (!str)
384 return ret;
386 /* count the number of substrings */
387 for ( pc = str, count = 0; pc; count++ )
389 pc = strchrW( pc, sep );
390 if (pc)
391 pc++;
394 /* allocate space for an array of substring pointers and the substrings */
395 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
396 (lstrlenW(str)+1) * sizeof(WCHAR) );
397 if (!ret)
398 return ret;
400 /* copy the string and set the pointers */
401 p = (LPWSTR) &ret[count+1];
402 lstrcpyW( p, str );
403 for( count = 0; (ret[count] = p); count++ )
405 p = strchrW( p, sep );
406 if (p)
407 *p++ = 0;
410 return ret;
413 static BOOL ui_sequence_exists( MSIPACKAGE *package )
415 static const WCHAR query [] = {
416 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
417 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
418 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
419 MSIQUERY *view;
420 DWORD count = 0;
422 if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
424 MSI_IterateRecords( view, &count, NULL, package );
425 msiobj_release( &view->hdr );
427 return count != 0;
430 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
432 WCHAR *source, *check, *p, *db;
433 DWORD len;
435 if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
436 return ERROR_OUTOFMEMORY;
438 if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' )))
440 msi_free(db);
441 return ERROR_SUCCESS;
443 len = p - db + 2;
444 source = msi_alloc( len * sizeof(WCHAR) );
445 lstrcpynW( source, db, len );
446 msi_free( db );
448 check = msi_dup_property( package->db, szSourceDir );
449 if (!check || replace)
451 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
452 if (r == ERROR_SUCCESS)
453 msi_reset_folders( package, TRUE );
455 msi_free( check );
457 check = msi_dup_property( package->db, szSOURCEDIR );
458 if (!check || replace)
459 msi_set_property( package->db, szSOURCEDIR, source, -1 );
461 msi_free( check );
462 msi_free( source );
464 return ERROR_SUCCESS;
467 static BOOL needs_ui_sequence(MSIPACKAGE *package)
469 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
472 UINT msi_set_context(MSIPACKAGE *package)
474 UINT r = msi_locate_product( package->ProductCode, &package->Context );
475 if (r != ERROR_SUCCESS)
477 int num = msi_get_property_int( package->db, szAllUsers, 0 );
478 if (num == 1 || num == 2)
479 package->Context = MSIINSTALLCONTEXT_MACHINE;
480 else
481 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
483 return ERROR_SUCCESS;
486 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
488 UINT rc;
489 LPCWSTR cond, action;
490 MSIPACKAGE *package = param;
492 action = MSI_RecordGetString(row,1);
493 if (!action)
495 ERR("Error is retrieving action name\n");
496 return ERROR_FUNCTION_FAILED;
499 /* check conditions */
500 cond = MSI_RecordGetString(row,2);
502 /* this is a hack to skip errors in the condition code */
503 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
505 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
506 return ERROR_SUCCESS;
509 rc = ACTION_PerformAction(package, action);
511 msi_dialog_check_messages( NULL );
513 if (rc == ERROR_FUNCTION_NOT_CALLED)
514 rc = ERROR_SUCCESS;
516 if (rc != ERROR_SUCCESS)
517 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
519 if (package->need_reboot_now)
521 TRACE("action %s asked for immediate reboot, suspending installation\n",
522 debugstr_w(action));
523 rc = ACTION_ForceReboot( package );
525 return rc;
528 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
530 static const WCHAR query[] = {
531 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
532 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
533 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
534 '`','S','e','q','u','e','n','c','e','`',0};
535 MSIQUERY *view;
536 UINT r;
538 TRACE("%p %s\n", package, debugstr_w(table));
540 r = MSI_OpenQuery( package->db, &view, query, table );
541 if (r == ERROR_SUCCESS)
543 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
544 msiobj_release(&view->hdr);
546 return r;
549 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package)
551 static const WCHAR query[] = {
552 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
553 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
554 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
555 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','0',' ',
556 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
557 MSIQUERY *view;
558 UINT rc;
560 if (package->ExecuteSequenceRun)
562 TRACE("Execute Sequence already Run\n");
563 return ERROR_SUCCESS;
566 package->ExecuteSequenceRun = TRUE;
568 rc = MSI_OpenQuery(package->db, &view, query);
569 if (rc == ERROR_SUCCESS)
571 TRACE("Running the actions\n");
573 msi_set_property( package->db, szSourceDir, NULL, -1 );
574 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
575 msiobj_release(&view->hdr);
577 return rc;
580 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
582 static const WCHAR query[] = {
583 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
584 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
585 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
586 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
587 MSIQUERY *view;
588 UINT rc;
590 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
591 if (rc == ERROR_SUCCESS)
593 TRACE("Running the actions\n");
594 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
595 msiobj_release(&view->hdr);
597 return rc;
600 /********************************************************
601 * ACTION helper functions and functions that perform the actions
602 *******************************************************/
603 static UINT ACTION_HandleCustomAction(MSIPACKAGE *package, LPCWSTR action)
605 UINT arc;
606 INT uirc;
608 uirc = ui_actionstart(package, action, NULL, NULL);
609 if (uirc == IDCANCEL)
610 return ERROR_INSTALL_USEREXIT;
611 ui_actioninfo(package, action, TRUE, 0);
612 arc = ACTION_CustomAction(package, action);
613 uirc = !arc;
615 if (arc == ERROR_FUNCTION_NOT_CALLED && needs_ui_sequence(package))
617 uirc = ACTION_ShowDialog(package, action);
618 switch (uirc)
620 case -1:
621 return ERROR_SUCCESS; /* stop immediately */
622 case 0: arc = ERROR_FUNCTION_NOT_CALLED; break;
623 case 1: arc = ERROR_SUCCESS; break;
624 case 2: arc = ERROR_INSTALL_USEREXIT; break;
625 case 3: arc = ERROR_INSTALL_FAILURE; break;
626 case 4: arc = ERROR_INSTALL_SUSPEND; break;
627 case 5: arc = ERROR_MORE_DATA; break;
628 case 6: arc = ERROR_INVALID_HANDLE_STATE; break;
629 case 7: arc = ERROR_INVALID_DATA; break;
630 case 8: arc = ERROR_INSTALL_ALREADY_RUNNING; break;
631 case 9: arc = ERROR_INSTALL_PACKAGE_REJECTED; break;
632 default: arc = ERROR_FUNCTION_FAILED; break;
636 ui_actioninfo(package, action, FALSE, uirc);
638 return arc;
641 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
643 MSICOMPONENT *comp;
645 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
647 if (!strcmpW( Component, comp->Component )) return comp;
649 return NULL;
652 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
654 MSIFEATURE *feature;
656 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
658 if (!strcmpW( Feature, feature->Feature )) return feature;
660 return NULL;
663 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
665 MSIFILE *file;
667 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
669 if (!strcmpW( key, file->File )) return file;
671 return NULL;
674 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
676 MSIFOLDER *folder;
678 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
680 if (!strcmpW( dir, folder->Directory )) return folder;
682 return NULL;
686 * Recursively create all directories in the path.
687 * shamelessly stolen from setupapi/queue.c
689 BOOL msi_create_full_path( const WCHAR *path )
691 BOOL ret = TRUE;
692 WCHAR *new_path;
693 int len;
695 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
696 strcpyW( new_path, path );
698 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
699 new_path[len - 1] = 0;
701 while (!CreateDirectoryW( new_path, NULL ))
703 WCHAR *slash;
704 DWORD last_error = GetLastError();
705 if (last_error == ERROR_ALREADY_EXISTS) break;
706 if (last_error != ERROR_PATH_NOT_FOUND)
708 ret = FALSE;
709 break;
711 if (!(slash = strrchrW( new_path, '\\' )))
713 ret = FALSE;
714 break;
716 len = slash - new_path;
717 new_path[len] = 0;
718 if (!msi_create_full_path( new_path ))
720 ret = FALSE;
721 break;
723 new_path[len] = '\\';
725 msi_free( new_path );
726 return ret;
729 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
731 MSIRECORD *row;
733 row = MSI_CreateRecord( 4 );
734 MSI_RecordSetInteger( row, 1, a );
735 MSI_RecordSetInteger( row, 2, b );
736 MSI_RecordSetInteger( row, 3, c );
737 MSI_RecordSetInteger( row, 4, d );
738 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
739 msiobj_release( &row->hdr );
741 msi_dialog_check_messages( NULL );
744 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
746 if (!comp->Enabled)
748 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
749 return INSTALLSTATE_UNKNOWN;
751 if (package->need_rollback) return comp->Installed;
752 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
754 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
755 return INSTALLSTATE_UNKNOWN;
757 return comp->ActionRequest;
760 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
762 if (package->need_rollback) return feature->Installed;
763 return feature->ActionRequest;
766 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
768 MSIPACKAGE *package = param;
769 LPCWSTR dir, component, full_path;
770 MSIRECORD *uirow;
771 MSIFOLDER *folder;
772 MSICOMPONENT *comp;
774 component = MSI_RecordGetString(row, 2);
775 if (!component)
776 return ERROR_SUCCESS;
778 comp = msi_get_loaded_component(package, component);
779 if (!comp)
780 return ERROR_SUCCESS;
782 comp->Action = msi_get_component_action( package, comp );
783 if (comp->Action != INSTALLSTATE_LOCAL)
785 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
786 return ERROR_SUCCESS;
789 dir = MSI_RecordGetString(row,1);
790 if (!dir)
792 ERR("Unable to get folder id\n");
793 return ERROR_SUCCESS;
796 uirow = MSI_CreateRecord(1);
797 MSI_RecordSetStringW(uirow, 1, dir);
798 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
799 msiobj_release(&uirow->hdr);
801 full_path = msi_get_target_folder( package, dir );
802 if (!full_path)
804 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
805 return ERROR_SUCCESS;
807 TRACE("folder is %s\n", debugstr_w(full_path));
809 folder = msi_get_loaded_folder( package, dir );
810 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
811 folder->State = FOLDER_STATE_CREATED;
812 return ERROR_SUCCESS;
815 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
817 static const WCHAR query[] = {
818 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
819 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
820 MSIQUERY *view;
821 UINT rc;
823 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
824 if (rc != ERROR_SUCCESS)
825 return ERROR_SUCCESS;
827 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
828 msiobj_release(&view->hdr);
829 return rc;
832 static void remove_persistent_folder( MSIFOLDER *folder )
834 FolderList *fl;
836 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
838 remove_persistent_folder( fl->folder );
840 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
842 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
846 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
848 MSIPACKAGE *package = param;
849 LPCWSTR dir, component, full_path;
850 MSIRECORD *uirow;
851 MSIFOLDER *folder;
852 MSICOMPONENT *comp;
854 component = MSI_RecordGetString(row, 2);
855 if (!component)
856 return ERROR_SUCCESS;
858 comp = msi_get_loaded_component(package, component);
859 if (!comp)
860 return ERROR_SUCCESS;
862 comp->Action = msi_get_component_action( package, comp );
863 if (comp->Action != INSTALLSTATE_ABSENT)
865 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
866 return ERROR_SUCCESS;
869 dir = MSI_RecordGetString( row, 1 );
870 if (!dir)
872 ERR("Unable to get folder id\n");
873 return ERROR_SUCCESS;
876 full_path = msi_get_target_folder( package, dir );
877 if (!full_path)
879 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
880 return ERROR_SUCCESS;
882 TRACE("folder is %s\n", debugstr_w(full_path));
884 uirow = MSI_CreateRecord( 1 );
885 MSI_RecordSetStringW( uirow, 1, dir );
886 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
887 msiobj_release( &uirow->hdr );
889 folder = msi_get_loaded_folder( package, dir );
890 remove_persistent_folder( folder );
891 return ERROR_SUCCESS;
894 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
896 static const WCHAR query[] = {
897 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
898 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
899 MSIQUERY *view;
900 UINT rc;
902 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
903 if (rc != ERROR_SUCCESS)
904 return ERROR_SUCCESS;
906 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
907 msiobj_release( &view->hdr );
908 return rc;
911 static UINT load_component( MSIRECORD *row, LPVOID param )
913 MSIPACKAGE *package = param;
914 MSICOMPONENT *comp;
916 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
917 if (!comp)
918 return ERROR_FUNCTION_FAILED;
920 list_add_tail( &package->components, &comp->entry );
922 /* fill in the data */
923 comp->Component = msi_dup_record_field( row, 1 );
925 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
927 comp->ComponentId = msi_dup_record_field( row, 2 );
928 comp->Directory = msi_dup_record_field( row, 3 );
929 comp->Attributes = MSI_RecordGetInteger(row,4);
930 comp->Condition = msi_dup_record_field( row, 5 );
931 comp->KeyPath = msi_dup_record_field( row, 6 );
933 comp->Installed = INSTALLSTATE_UNKNOWN;
934 comp->Action = INSTALLSTATE_UNKNOWN;
935 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
937 comp->assembly = msi_load_assembly( package, comp );
938 return ERROR_SUCCESS;
941 UINT msi_load_all_components( MSIPACKAGE *package )
943 static const WCHAR query[] = {
944 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
945 '`','C','o','m','p','o','n','e','n','t','`',0};
946 MSIQUERY *view;
947 UINT r;
949 if (!list_empty(&package->components))
950 return ERROR_SUCCESS;
952 r = MSI_DatabaseOpenViewW( package->db, query, &view );
953 if (r != ERROR_SUCCESS)
954 return r;
956 if (!msi_init_assembly_caches( package ))
958 ERR("can't initialize assembly caches\n");
959 msiobj_release( &view->hdr );
960 return ERROR_FUNCTION_FAILED;
963 r = MSI_IterateRecords(view, NULL, load_component, package);
964 msiobj_release(&view->hdr);
965 return r;
968 typedef struct {
969 MSIPACKAGE *package;
970 MSIFEATURE *feature;
971 } _ilfs;
973 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
975 ComponentList *cl;
977 cl = msi_alloc( sizeof (*cl) );
978 if ( !cl )
979 return ERROR_NOT_ENOUGH_MEMORY;
980 cl->component = comp;
981 list_add_tail( &feature->Components, &cl->entry );
983 return ERROR_SUCCESS;
986 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
988 FeatureList *fl;
990 fl = msi_alloc( sizeof(*fl) );
991 if ( !fl )
992 return ERROR_NOT_ENOUGH_MEMORY;
993 fl->feature = child;
994 list_add_tail( &parent->Children, &fl->entry );
996 return ERROR_SUCCESS;
999 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1001 _ilfs* ilfs = param;
1002 LPCWSTR component;
1003 MSICOMPONENT *comp;
1005 component = MSI_RecordGetString(row,1);
1007 /* check to see if the component is already loaded */
1008 comp = msi_get_loaded_component( ilfs->package, component );
1009 if (!comp)
1011 WARN("ignoring unknown component %s\n", debugstr_w(component));
1012 return ERROR_SUCCESS;
1014 add_feature_component( ilfs->feature, comp );
1015 comp->Enabled = TRUE;
1017 return ERROR_SUCCESS;
1020 static UINT load_feature(MSIRECORD * row, LPVOID param)
1022 static const WCHAR query[] = {
1023 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1024 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1025 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1026 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1027 MSIPACKAGE *package = param;
1028 MSIFEATURE *feature;
1029 MSIQUERY *view;
1030 _ilfs ilfs;
1031 UINT rc;
1033 /* fill in the data */
1035 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1036 if (!feature)
1037 return ERROR_NOT_ENOUGH_MEMORY;
1039 list_init( &feature->Children );
1040 list_init( &feature->Components );
1042 feature->Feature = msi_dup_record_field( row, 1 );
1044 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1046 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1047 feature->Title = msi_dup_record_field( row, 3 );
1048 feature->Description = msi_dup_record_field( row, 4 );
1050 if (!MSI_RecordIsNull(row,5))
1051 feature->Display = MSI_RecordGetInteger(row,5);
1053 feature->Level= MSI_RecordGetInteger(row,6);
1054 feature->Directory = msi_dup_record_field( row, 7 );
1055 feature->Attributes = MSI_RecordGetInteger(row,8);
1057 feature->Installed = INSTALLSTATE_UNKNOWN;
1058 feature->Action = INSTALLSTATE_UNKNOWN;
1059 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1061 list_add_tail( &package->features, &feature->entry );
1063 /* load feature components */
1065 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1066 if (rc != ERROR_SUCCESS)
1067 return ERROR_SUCCESS;
1069 ilfs.package = package;
1070 ilfs.feature = feature;
1072 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1073 msiobj_release(&view->hdr);
1074 return rc;
1077 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1079 MSIPACKAGE *package = param;
1080 MSIFEATURE *parent, *child;
1082 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1083 if (!child)
1084 return ERROR_FUNCTION_FAILED;
1086 if (!child->Feature_Parent)
1087 return ERROR_SUCCESS;
1089 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1090 if (!parent)
1091 return ERROR_FUNCTION_FAILED;
1093 add_feature_child( parent, child );
1094 return ERROR_SUCCESS;
1097 UINT msi_load_all_features( MSIPACKAGE *package )
1099 static const WCHAR query[] = {
1100 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1101 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1102 '`','D','i','s','p','l','a','y','`',0};
1103 MSIQUERY *view;
1104 UINT r;
1106 if (!list_empty(&package->features))
1107 return ERROR_SUCCESS;
1109 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1110 if (r != ERROR_SUCCESS)
1111 return r;
1113 r = MSI_IterateRecords( view, NULL, load_feature, package );
1114 if (r != ERROR_SUCCESS)
1116 msiobj_release( &view->hdr );
1117 return r;
1119 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1120 msiobj_release( &view->hdr );
1121 return r;
1124 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1126 if (!p)
1127 return p;
1128 p = strchrW(p, ch);
1129 if (!p)
1130 return p;
1131 *p = 0;
1132 return p+1;
1135 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1137 static const WCHAR query[] = {
1138 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1139 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1140 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1141 MSIQUERY *view = NULL;
1142 MSIRECORD *row = NULL;
1143 UINT r;
1145 TRACE("%s\n", debugstr_w(file->File));
1147 r = MSI_OpenQuery(package->db, &view, query, file->File);
1148 if (r != ERROR_SUCCESS)
1149 goto done;
1151 r = MSI_ViewExecute(view, NULL);
1152 if (r != ERROR_SUCCESS)
1153 goto done;
1155 r = MSI_ViewFetch(view, &row);
1156 if (r != ERROR_SUCCESS)
1157 goto done;
1159 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1160 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1161 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1162 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1163 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1165 done:
1166 if (view) msiobj_release(&view->hdr);
1167 if (row) msiobj_release(&row->hdr);
1168 return r;
1171 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1173 MSIRECORD *row;
1174 static const WCHAR query[] = {
1175 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1176 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1177 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1179 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1180 if (!row)
1182 WARN("query failed\n");
1183 return ERROR_FUNCTION_FAILED;
1186 file->disk_id = MSI_RecordGetInteger( row, 1 );
1187 msiobj_release( &row->hdr );
1188 return ERROR_SUCCESS;
1191 static UINT load_file(MSIRECORD *row, LPVOID param)
1193 MSIPACKAGE* package = param;
1194 LPCWSTR component;
1195 MSIFILE *file;
1197 /* fill in the data */
1199 file = msi_alloc_zero( sizeof (MSIFILE) );
1200 if (!file)
1201 return ERROR_NOT_ENOUGH_MEMORY;
1203 file->File = msi_dup_record_field( row, 1 );
1205 component = MSI_RecordGetString( row, 2 );
1206 file->Component = msi_get_loaded_component( package, component );
1208 if (!file->Component)
1210 WARN("Component not found: %s\n", debugstr_w(component));
1211 msi_free(file->File);
1212 msi_free(file);
1213 return ERROR_SUCCESS;
1216 file->FileName = msi_dup_record_field( row, 3 );
1217 msi_reduce_to_long_filename( file->FileName );
1219 file->ShortName = msi_dup_record_field( row, 3 );
1220 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1222 file->FileSize = MSI_RecordGetInteger( row, 4 );
1223 file->Version = msi_dup_record_field( row, 5 );
1224 file->Language = msi_dup_record_field( row, 6 );
1225 file->Attributes = MSI_RecordGetInteger( row, 7 );
1226 file->Sequence = MSI_RecordGetInteger( row, 8 );
1228 file->state = msifs_invalid;
1230 /* if the compressed bits are not set in the file attributes,
1231 * then read the information from the package word count property
1233 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1235 file->IsCompressed = FALSE;
1237 else if (file->Attributes &
1238 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1240 file->IsCompressed = TRUE;
1242 else if (file->Attributes & msidbFileAttributesNoncompressed)
1244 file->IsCompressed = FALSE;
1246 else
1248 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1251 load_file_hash(package, file);
1252 load_file_disk_id(package, file);
1254 TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1256 list_add_tail( &package->files, &file->entry );
1258 return ERROR_SUCCESS;
1261 static UINT load_all_files(MSIPACKAGE *package)
1263 static const WCHAR query[] = {
1264 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1265 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1266 '`','S','e','q','u','e','n','c','e','`', 0};
1267 MSIQUERY *view;
1268 UINT rc;
1270 if (!list_empty(&package->files))
1271 return ERROR_SUCCESS;
1273 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1274 if (rc != ERROR_SUCCESS)
1275 return ERROR_SUCCESS;
1277 rc = MSI_IterateRecords(view, NULL, load_file, package);
1278 msiobj_release(&view->hdr);
1279 return rc;
1282 static UINT load_media( MSIRECORD *row, LPVOID param )
1284 MSIPACKAGE *package = param;
1285 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1286 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1288 /* FIXME: load external cabinets and directory sources too */
1289 if (!cabinet || cabinet[0] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
1290 return ERROR_SUCCESS;
1292 return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1295 static UINT load_all_media( MSIPACKAGE *package )
1297 static const WCHAR query[] = {
1298 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1299 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1300 '`','D','i','s','k','I','d','`',0};
1301 MSIQUERY *view;
1302 UINT r;
1304 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1305 if (r != ERROR_SUCCESS)
1306 return ERROR_SUCCESS;
1308 r = MSI_IterateRecords( view, NULL, load_media, package );
1309 msiobj_release( &view->hdr );
1310 return r;
1313 static UINT load_patch_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
1315 static const WCHAR query[] =
1316 {'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1317 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1318 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','u',0};
1319 MSIRECORD *rec;
1321 if (!(rec = MSI_QueryGetRecord( package->db, query, patch->Sequence )))
1323 WARN("query failed\n");
1324 return ERROR_FUNCTION_FAILED;
1327 patch->disk_id = MSI_RecordGetInteger( rec, 1 );
1328 msiobj_release( &rec->hdr );
1329 return ERROR_SUCCESS;
1332 static UINT load_patch(MSIRECORD *row, LPVOID param)
1334 MSIPACKAGE *package = param;
1335 MSIFILEPATCH *patch;
1336 const WCHAR *file_key;
1338 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1339 if (!patch)
1340 return ERROR_NOT_ENOUGH_MEMORY;
1342 file_key = MSI_RecordGetString( row, 1 );
1343 patch->File = msi_get_loaded_file( package, file_key );
1344 if (!patch->File)
1346 ERR("Failed to find target for patch in File table\n");
1347 msi_free(patch);
1348 return ERROR_FUNCTION_FAILED;
1351 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1352 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1353 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1355 /* FIXME:
1356 * Header field - for patch validation.
1357 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1360 load_patch_disk_id( package, patch );
1362 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1364 list_add_tail( &package->filepatches, &patch->entry );
1366 return ERROR_SUCCESS;
1369 static UINT load_all_patches(MSIPACKAGE *package)
1371 static const WCHAR query[] = {
1372 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1373 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1374 '`','S','e','q','u','e','n','c','e','`',0};
1375 MSIQUERY *view;
1376 UINT rc;
1378 if (!list_empty(&package->filepatches))
1379 return ERROR_SUCCESS;
1381 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1382 if (rc != ERROR_SUCCESS)
1383 return ERROR_SUCCESS;
1385 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1386 msiobj_release(&view->hdr);
1387 return rc;
1390 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1392 static const WCHAR query[] = {
1393 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1394 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1395 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1396 MSIQUERY *view;
1398 folder->persistent = FALSE;
1399 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1401 if (!MSI_ViewExecute( view, NULL ))
1403 MSIRECORD *rec;
1404 if (!MSI_ViewFetch( view, &rec ))
1406 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1407 folder->persistent = TRUE;
1408 msiobj_release( &rec->hdr );
1411 msiobj_release( &view->hdr );
1413 return ERROR_SUCCESS;
1416 static UINT load_folder( MSIRECORD *row, LPVOID param )
1418 MSIPACKAGE *package = param;
1419 static WCHAR szEmpty[] = { 0 };
1420 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1421 MSIFOLDER *folder;
1423 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1424 list_init( &folder->children );
1425 folder->Directory = msi_dup_record_field( row, 1 );
1426 folder->Parent = msi_dup_record_field( row, 2 );
1427 p = msi_dup_record_field(row, 3);
1429 TRACE("%s\n", debugstr_w(folder->Directory));
1431 /* split src and target dir */
1432 tgt_short = p;
1433 src_short = folder_split_path( p, ':' );
1435 /* split the long and short paths */
1436 tgt_long = folder_split_path( tgt_short, '|' );
1437 src_long = folder_split_path( src_short, '|' );
1439 /* check for no-op dirs */
1440 if (tgt_short && !strcmpW( szDot, tgt_short ))
1441 tgt_short = szEmpty;
1442 if (src_short && !strcmpW( szDot, src_short ))
1443 src_short = szEmpty;
1445 if (!tgt_long)
1446 tgt_long = tgt_short;
1448 if (!src_short) {
1449 src_short = tgt_short;
1450 src_long = tgt_long;
1453 if (!src_long)
1454 src_long = src_short;
1456 /* FIXME: use the target short path too */
1457 folder->TargetDefault = strdupW(tgt_long);
1458 folder->SourceShortPath = strdupW(src_short);
1459 folder->SourceLongPath = strdupW(src_long);
1460 msi_free(p);
1462 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1463 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1464 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1466 load_folder_persistence( package, folder );
1468 list_add_tail( &package->folders, &folder->entry );
1469 return ERROR_SUCCESS;
1472 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1474 FolderList *fl;
1476 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1477 fl->folder = child;
1478 list_add_tail( &parent->children, &fl->entry );
1479 return ERROR_SUCCESS;
1482 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1484 MSIPACKAGE *package = param;
1485 MSIFOLDER *parent, *child;
1487 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1488 return ERROR_FUNCTION_FAILED;
1490 if (!child->Parent) return ERROR_SUCCESS;
1492 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1493 return ERROR_FUNCTION_FAILED;
1495 return add_folder_child( parent, child );
1498 static UINT load_all_folders( MSIPACKAGE *package )
1500 static const WCHAR query[] = {
1501 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1502 '`','D','i','r','e','c','t','o','r','y','`',0};
1503 MSIQUERY *view;
1504 UINT r;
1506 if (!list_empty(&package->folders))
1507 return ERROR_SUCCESS;
1509 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1510 if (r != ERROR_SUCCESS)
1511 return r;
1513 r = MSI_IterateRecords( view, NULL, load_folder, package );
1514 if (r != ERROR_SUCCESS)
1516 msiobj_release( &view->hdr );
1517 return r;
1519 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1520 msiobj_release( &view->hdr );
1521 return r;
1524 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1526 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1527 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1529 load_all_folders( package );
1530 msi_load_all_components( package );
1531 msi_load_all_features( package );
1532 load_all_files( package );
1533 load_all_patches( package );
1534 load_all_media( package );
1536 return ERROR_SUCCESS;
1539 static UINT execute_script( MSIPACKAGE *package, UINT script )
1541 UINT i, rc = ERROR_SUCCESS;
1543 TRACE("executing script %u\n", script);
1545 package->script = script;
1547 if (script == SCRIPT_ROLLBACK)
1549 for (i = package->script_actions_count[script]; i > 0; i--)
1551 rc = ACTION_PerformAction(package, package->script_actions[script][i-1]);
1552 if (rc != ERROR_SUCCESS)
1554 ERR("Execution of script %i halted; action %s returned %u\n",
1555 script, debugstr_w(package->script_actions[script][i-1]), rc);
1556 break;
1560 else
1562 for (i = 0; i < package->script_actions_count[script]; i++)
1564 rc = ACTION_PerformAction(package, package->script_actions[script][i]);
1565 if (rc != ERROR_SUCCESS)
1567 ERR("Execution of script %i halted; action %s returned %u\n",
1568 script, debugstr_w(package->script_actions[script][i]), rc);
1569 break;
1574 package->script = SCRIPT_NONE;
1576 msi_free_action_script(package, script);
1577 return rc;
1580 static UINT ACTION_FileCost(MSIPACKAGE *package)
1582 return ERROR_SUCCESS;
1585 static void get_client_counts( MSIPACKAGE *package )
1587 MSICOMPONENT *comp;
1588 HKEY hkey;
1590 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1592 if (!comp->ComponentId) continue;
1594 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1595 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1597 comp->num_clients = 0;
1598 continue;
1600 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1601 NULL, NULL, NULL, NULL );
1602 RegCloseKey( hkey );
1606 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1608 MSICOMPONENT *comp;
1609 UINT r;
1611 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1613 if (!comp->ComponentId) continue;
1615 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1616 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1617 &comp->Installed );
1618 if (r == ERROR_SUCCESS) continue;
1620 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1621 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1622 &comp->Installed );
1623 if (r == ERROR_SUCCESS) continue;
1625 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1626 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1627 &comp->Installed );
1628 if (r == ERROR_SUCCESS) continue;
1630 comp->Installed = INSTALLSTATE_ABSENT;
1634 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1636 MSIFEATURE *feature;
1638 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1640 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1642 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1643 feature->Installed = INSTALLSTATE_ABSENT;
1644 else
1645 feature->Installed = state;
1649 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1651 return (feature->Level > 0 && feature->Level <= level);
1654 static BOOL process_state_property(MSIPACKAGE* package, int level,
1655 LPCWSTR property, INSTALLSTATE state)
1657 LPWSTR override;
1658 MSIFEATURE *feature;
1659 BOOL remove = !strcmpW(property, szRemove);
1660 BOOL reinstall = !strcmpW(property, szReinstall);
1662 override = msi_dup_property( package->db, property );
1663 if (!override)
1664 return FALSE;
1666 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1668 if (feature->Level <= 0)
1669 continue;
1671 if (reinstall)
1672 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1673 else if (remove)
1674 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1676 if (!strcmpiW( override, szAll ))
1678 feature->Action = state;
1679 feature->ActionRequest = state;
1681 else
1683 LPWSTR ptr = override;
1684 LPWSTR ptr2 = strchrW(override,',');
1686 while (ptr)
1688 int len = ptr2 - ptr;
1690 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1691 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1693 feature->Action = state;
1694 feature->ActionRequest = state;
1695 break;
1697 if (ptr2)
1699 ptr=ptr2+1;
1700 ptr2 = strchrW(ptr,',');
1702 else
1703 break;
1707 msi_free(override);
1708 return TRUE;
1711 static BOOL process_overrides( MSIPACKAGE *package, int level )
1713 static const WCHAR szAddLocal[] =
1714 {'A','D','D','L','O','C','A','L',0};
1715 static const WCHAR szAddSource[] =
1716 {'A','D','D','S','O','U','R','C','E',0};
1717 static const WCHAR szAdvertise[] =
1718 {'A','D','V','E','R','T','I','S','E',0};
1719 BOOL ret = FALSE;
1721 /* all these activation/deactivation things happen in order and things
1722 * later on the list override things earlier on the list.
1724 * 0 INSTALLLEVEL processing
1725 * 1 ADDLOCAL
1726 * 2 REMOVE
1727 * 3 ADDSOURCE
1728 * 4 ADDDEFAULT
1729 * 5 REINSTALL
1730 * 6 ADVERTISE
1731 * 7 COMPADDLOCAL
1732 * 8 COMPADDSOURCE
1733 * 9 FILEADDLOCAL
1734 * 10 FILEADDSOURCE
1735 * 11 FILEADDDEFAULT
1737 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1738 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1739 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1740 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1741 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1743 if (ret && !package->full_reinstall)
1744 msi_set_property( package->db, szPreselected, szOne, -1 );
1746 return ret;
1749 static void disable_children( MSIFEATURE *feature, int level )
1751 FeatureList *fl;
1753 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1755 if (!is_feature_selected( feature, level ))
1757 TRACE("child %s (level %d request %d) follows disabled parent %s (level %d request %d)\n",
1758 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1759 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1761 fl->feature->Level = feature->Level;
1762 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1763 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1765 disable_children( fl->feature, level );
1769 static void follow_parent( MSIFEATURE *feature )
1771 FeatureList *fl;
1773 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1775 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1777 TRACE("child %s (level %d request %d) follows parent %s (level %d request %d)\n",
1778 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1779 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1781 fl->feature->Action = feature->Action;
1782 fl->feature->ActionRequest = feature->ActionRequest;
1784 follow_parent( fl->feature );
1788 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1790 int level;
1791 MSICOMPONENT* component;
1792 MSIFEATURE *feature;
1794 TRACE("Checking Install Level\n");
1796 level = msi_get_property_int(package->db, szInstallLevel, 1);
1798 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1800 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1802 if (!is_feature_selected( feature, level )) continue;
1804 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1806 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1808 feature->Action = INSTALLSTATE_SOURCE;
1809 feature->ActionRequest = INSTALLSTATE_SOURCE;
1811 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1813 feature->Action = INSTALLSTATE_ADVERTISED;
1814 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1816 else
1818 feature->Action = INSTALLSTATE_LOCAL;
1819 feature->ActionRequest = INSTALLSTATE_LOCAL;
1823 /* disable child features of unselected parent or follow parent */
1824 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1826 if (feature->Feature_Parent) continue;
1827 disable_children( feature, level );
1828 follow_parent( feature );
1831 else /* preselected */
1833 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1835 if (!is_feature_selected( feature, level )) continue;
1837 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1839 if (feature->Installed == INSTALLSTATE_ABSENT)
1841 feature->Action = INSTALLSTATE_UNKNOWN;
1842 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1844 else
1846 feature->Action = feature->Installed;
1847 feature->ActionRequest = feature->Installed;
1851 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1853 if (feature->Feature_Parent) continue;
1854 disable_children( feature, level );
1855 follow_parent( feature );
1859 /* now we want to set component state based based on feature state */
1860 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1862 ComponentList *cl;
1864 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1865 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1866 feature->ActionRequest, feature->Action);
1868 /* features with components that have compressed files are made local */
1869 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1871 if (cl->component->ForceLocalState &&
1872 feature->ActionRequest == INSTALLSTATE_SOURCE)
1874 feature->Action = INSTALLSTATE_LOCAL;
1875 feature->ActionRequest = INSTALLSTATE_LOCAL;
1876 break;
1880 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1882 component = cl->component;
1884 switch (feature->ActionRequest)
1886 case INSTALLSTATE_ABSENT:
1887 component->anyAbsent = 1;
1888 break;
1889 case INSTALLSTATE_ADVERTISED:
1890 component->hasAdvertisedFeature = 1;
1891 break;
1892 case INSTALLSTATE_SOURCE:
1893 component->hasSourceFeature = 1;
1894 break;
1895 case INSTALLSTATE_LOCAL:
1896 component->hasLocalFeature = 1;
1897 break;
1898 case INSTALLSTATE_DEFAULT:
1899 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1900 component->hasAdvertisedFeature = 1;
1901 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1902 component->hasSourceFeature = 1;
1903 else
1904 component->hasLocalFeature = 1;
1905 break;
1906 default:
1907 break;
1912 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1914 /* check if it's local or source */
1915 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1916 (component->hasLocalFeature || component->hasSourceFeature))
1918 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1919 !component->ForceLocalState)
1921 component->Action = INSTALLSTATE_SOURCE;
1922 component->ActionRequest = INSTALLSTATE_SOURCE;
1924 else
1926 component->Action = INSTALLSTATE_LOCAL;
1927 component->ActionRequest = INSTALLSTATE_LOCAL;
1929 continue;
1932 /* if any feature is local, the component must be local too */
1933 if (component->hasLocalFeature)
1935 component->Action = INSTALLSTATE_LOCAL;
1936 component->ActionRequest = INSTALLSTATE_LOCAL;
1937 continue;
1939 if (component->hasSourceFeature)
1941 component->Action = INSTALLSTATE_SOURCE;
1942 component->ActionRequest = INSTALLSTATE_SOURCE;
1943 continue;
1945 if (component->hasAdvertisedFeature)
1947 component->Action = INSTALLSTATE_ADVERTISED;
1948 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1949 continue;
1951 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1952 if (component->anyAbsent && component->ComponentId)
1954 component->Action = INSTALLSTATE_ABSENT;
1955 component->ActionRequest = INSTALLSTATE_ABSENT;
1959 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1961 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1963 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1964 component->Action = INSTALLSTATE_LOCAL;
1965 component->ActionRequest = INSTALLSTATE_LOCAL;
1968 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1969 component->Installed == INSTALLSTATE_SOURCE &&
1970 component->hasSourceFeature)
1972 component->Action = INSTALLSTATE_UNKNOWN;
1973 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1976 TRACE("component %s (installed %d request %d action %d)\n",
1977 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1979 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1980 component->num_clients++;
1981 else if (component->Action == INSTALLSTATE_ABSENT)
1982 component->num_clients--;
1985 return ERROR_SUCCESS;
1988 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1990 MSIPACKAGE *package = param;
1991 LPCWSTR name;
1992 MSIFEATURE *feature;
1994 name = MSI_RecordGetString( row, 1 );
1996 feature = msi_get_loaded_feature( package, name );
1997 if (!feature)
1998 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1999 else
2001 LPCWSTR Condition;
2002 Condition = MSI_RecordGetString(row,3);
2004 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2006 int level = MSI_RecordGetInteger(row,2);
2007 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2008 feature->Level = level;
2011 return ERROR_SUCCESS;
2014 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2016 static const WCHAR name[] = {'\\',0};
2017 VS_FIXEDFILEINFO *ptr, *ret;
2018 LPVOID version;
2019 DWORD versize, handle;
2020 UINT sz;
2022 versize = GetFileVersionInfoSizeW( filename, &handle );
2023 if (!versize)
2024 return NULL;
2026 version = msi_alloc( versize );
2027 if (!version)
2028 return NULL;
2030 GetFileVersionInfoW( filename, 0, versize, version );
2032 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2034 msi_free( version );
2035 return NULL;
2038 ret = msi_alloc( sz );
2039 memcpy( ret, ptr, sz );
2041 msi_free( version );
2042 return ret;
2045 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2047 DWORD ms, ls;
2049 msi_parse_version_string( version, &ms, &ls );
2051 if (fi->dwFileVersionMS > ms) return 1;
2052 else if (fi->dwFileVersionMS < ms) return -1;
2053 else if (fi->dwFileVersionLS > ls) return 1;
2054 else if (fi->dwFileVersionLS < ls) return -1;
2055 return 0;
2058 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2060 DWORD ms1, ms2;
2062 msi_parse_version_string( ver1, &ms1, NULL );
2063 msi_parse_version_string( ver2, &ms2, NULL );
2065 if (ms1 > ms2) return 1;
2066 else if (ms1 < ms2) return -1;
2067 return 0;
2070 DWORD msi_get_disk_file_size( LPCWSTR filename )
2072 HANDLE file;
2073 DWORD size;
2075 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2076 if (file == INVALID_HANDLE_VALUE)
2077 return INVALID_FILE_SIZE;
2079 size = GetFileSize( file, NULL );
2080 CloseHandle( file );
2081 return size;
2084 BOOL msi_file_hash_matches( MSIFILE *file )
2086 UINT r;
2087 MSIFILEHASHINFO hash;
2089 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2090 r = msi_get_filehash( file->TargetPath, &hash );
2091 if (r != ERROR_SUCCESS)
2092 return FALSE;
2094 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2097 static WCHAR *create_temp_dir( MSIDATABASE *db )
2099 static UINT id;
2100 WCHAR *ret;
2102 if (!db->tempfolder)
2104 WCHAR tmp[MAX_PATH];
2105 UINT len = sizeof(tmp)/sizeof(tmp[0]);
2107 if (msi_get_property( db, szTempFolder, tmp, &len ) ||
2108 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
2110 GetTempPathW( MAX_PATH, tmp );
2112 if (!(db->tempfolder = strdupW( tmp ))) return NULL;
2115 if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
2117 for (;;)
2119 if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret ))
2121 msi_free( ret );
2122 return NULL;
2124 if (CreateDirectoryW( ret, NULL )) break;
2128 return ret;
2132 * msi_build_directory_name()
2134 * This function is to save messing round with directory names
2135 * It handles adding backslashes between path segments,
2136 * and can add \ at the end of the directory name if told to.
2138 * It takes a variable number of arguments.
2139 * It always allocates a new string for the result, so make sure
2140 * to free the return value when finished with it.
2142 * The first arg is the number of path segments that follow.
2143 * The arguments following count are a list of path segments.
2144 * A path segment may be NULL.
2146 * Path segments will be added with a \ separating them.
2147 * A \ will not be added after the last segment, however if the
2148 * last segment is NULL, then the last character will be a \
2150 WCHAR *msi_build_directory_name( DWORD count, ... )
2152 DWORD sz = 1, i;
2153 WCHAR *dir;
2154 va_list va;
2156 va_start( va, count );
2157 for (i = 0; i < count; i++)
2159 const WCHAR *str = va_arg( va, const WCHAR * );
2160 if (str) sz += strlenW( str ) + 1;
2162 va_end( va );
2164 dir = msi_alloc( sz * sizeof(WCHAR) );
2165 dir[0] = 0;
2167 va_start( va, count );
2168 for (i = 0; i < count; i++)
2170 const WCHAR *str = va_arg( va, const WCHAR * );
2171 if (!str) continue;
2172 strcatW( dir, str );
2173 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2175 va_end( va );
2176 return dir;
2179 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2181 return comp->assembly && !comp->assembly->application;
2184 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2186 msi_free( file->TargetPath );
2187 if (msi_is_global_assembly( file->Component ))
2189 MSIASSEMBLY *assembly = file->Component->assembly;
2191 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2192 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2194 else
2196 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2197 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2200 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2203 static UINT calculate_file_cost( MSIPACKAGE *package )
2205 VS_FIXEDFILEINFO *file_version;
2206 WCHAR *font_version;
2207 MSIFILE *file;
2209 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2211 MSICOMPONENT *comp = file->Component;
2212 DWORD file_size;
2214 if (!comp->Enabled) continue;
2216 if (file->IsCompressed)
2217 comp->ForceLocalState = TRUE;
2219 set_target_path( package, file );
2221 if ((comp->assembly && !comp->assembly->installed) ||
2222 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2224 comp->Cost += file->FileSize;
2225 continue;
2227 file_size = msi_get_disk_file_size( file->TargetPath );
2228 TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2230 if (file->Version)
2232 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2234 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2236 comp->Cost += file->FileSize - file_size;
2238 msi_free( file_version );
2239 continue;
2241 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2243 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2245 comp->Cost += file->FileSize - file_size;
2247 msi_free( font_version );
2248 continue;
2251 if (file_size != file->FileSize)
2253 comp->Cost += file->FileSize - file_size;
2256 return ERROR_SUCCESS;
2259 WCHAR *msi_normalize_path( const WCHAR *in )
2261 const WCHAR *p = in;
2262 WCHAR *q, *ret;
2263 int n, len = strlenW( in ) + 2;
2265 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2267 len = 0;
2268 while (1)
2270 /* copy until the end of the string or a space */
2271 while (*p != ' ' && (*q = *p))
2273 p++, len++;
2274 /* reduce many backslashes to one */
2275 if (*p != '\\' || *q != '\\')
2276 q++;
2279 /* quit at the end of the string */
2280 if (!*p)
2281 break;
2283 /* count the number of spaces */
2284 n = 0;
2285 while (p[n] == ' ')
2286 n++;
2288 /* if it's leading or trailing space, skip it */
2289 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2290 p += n;
2291 else /* copy n spaces */
2292 while (n && (*q++ = *p++)) n--;
2294 while (q - ret > 0 && q[-1] == ' ') q--;
2295 if (q - ret > 0 && q[-1] != '\\')
2297 q[0] = '\\';
2298 q[1] = 0;
2300 return ret;
2303 static WCHAR *get_install_location( MSIPACKAGE *package )
2305 HKEY hkey;
2306 WCHAR *path;
2308 if (!package->ProductCode) return NULL;
2309 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2310 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2312 msi_free( path );
2313 path = NULL;
2315 RegCloseKey( hkey );
2316 return path;
2319 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2321 FolderList *fl;
2322 MSIFOLDER *folder, *parent, *child;
2323 WCHAR *path, *normalized_path;
2325 TRACE("resolving %s\n", debugstr_w(name));
2327 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2329 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2331 if (!(path = get_install_location( package )) &&
2332 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2334 path = msi_dup_property( package->db, szRootDrive );
2337 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2339 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2341 parent = msi_get_loaded_folder( package, folder->Parent );
2342 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2344 else
2345 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2347 normalized_path = msi_normalize_path( path );
2348 msi_free( path );
2349 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2351 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2352 msi_free( normalized_path );
2353 return;
2355 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2356 msi_free( folder->ResolvedTarget );
2357 folder->ResolvedTarget = normalized_path;
2359 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2361 child = fl->folder;
2362 msi_resolve_target_folder( package, child->Directory, load_prop );
2364 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2367 static ULONGLONG get_volume_space_required( MSIPACKAGE *package )
2369 MSICOMPONENT *comp;
2370 ULONGLONG ret = 0;
2372 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2374 if (comp->Action == INSTALLSTATE_LOCAL) ret += comp->Cost;
2376 return ret;
2379 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2381 static const WCHAR query[] =
2382 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2383 '`','C','o','n','d','i','t','i','o','n','`',0};
2384 static const WCHAR szOutOfDiskSpace[] =
2385 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2386 static const WCHAR szPrimaryFolder[] =
2387 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2388 static const WCHAR szPrimaryVolumePath[] =
2389 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2390 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2391 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2392 'A','v','a','i','l','a','b','l','e',0};
2393 static const WCHAR szPrimaryVolumeSpaceRequired[] =
2394 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2395 'R','e','q','u','i','r','e','d',0};
2396 static const WCHAR szPrimaryVolumeSpaceRemaining[] =
2397 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2398 'R','e','m','a','i','n','i','n','g',0};
2399 static const WCHAR szOutOfNoRbDiskSpace[] =
2400 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2401 MSICOMPONENT *comp;
2402 MSIQUERY *view;
2403 WCHAR *level, *primary_key, *primary_folder;
2404 UINT rc;
2406 TRACE("Building directory properties\n");
2407 msi_resolve_target_folder( package, szTargetDir, TRUE );
2409 TRACE("Evaluating component conditions\n");
2410 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2412 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2414 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2415 comp->Enabled = FALSE;
2417 else
2418 comp->Enabled = TRUE;
2420 get_client_counts( package );
2422 /* read components states from the registry */
2423 ACTION_GetComponentInstallStates(package);
2424 ACTION_GetFeatureInstallStates(package);
2426 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2428 TRACE("Evaluating feature conditions\n");
2430 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2431 if (rc == ERROR_SUCCESS)
2433 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2434 msiobj_release( &view->hdr );
2435 if (rc != ERROR_SUCCESS)
2436 return rc;
2440 TRACE("Calculating file cost\n");
2441 calculate_file_cost( package );
2443 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2444 /* set default run level if not set */
2445 level = msi_dup_property( package->db, szInstallLevel );
2446 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2447 msi_free(level);
2449 if ((rc = MSI_SetFeatureStates( package ))) return rc;
2451 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2453 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2455 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2456 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2458 static const WCHAR fmtW[] = {'%','l','u',0};
2459 ULARGE_INTEGER free;
2460 ULONGLONG required;
2461 WCHAR buf[21];
2463 primary_folder[2] = 0;
2464 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2466 sprintfW( buf, fmtW, free.QuadPart / 512 );
2467 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2469 required = get_volume_space_required( package );
2470 sprintfW( buf, fmtW, required / 512 );
2471 msi_set_property( package->db, szPrimaryVolumeSpaceRequired, buf, -1 );
2473 sprintfW( buf, fmtW, (free.QuadPart - required) / 512 );
2474 msi_set_property( package->db, szPrimaryVolumeSpaceRemaining, buf, -1 );
2475 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2477 msi_free( primary_folder );
2479 msi_free( primary_key );
2482 /* FIXME: check volume disk space */
2483 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2484 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2486 return ERROR_SUCCESS;
2489 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2491 BYTE *data;
2493 if (!value)
2495 *size = sizeof(WCHAR);
2496 *type = REG_SZ;
2497 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2498 return data;
2500 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2502 if (value[1]=='x')
2504 LPWSTR ptr;
2505 CHAR byte[5];
2506 LPWSTR deformated = NULL;
2507 int count;
2509 deformat_string(package, &value[2], &deformated);
2511 /* binary value type */
2512 ptr = deformated;
2513 *type = REG_BINARY;
2514 if (strlenW(ptr)%2)
2515 *size = (strlenW(ptr)/2)+1;
2516 else
2517 *size = strlenW(ptr)/2;
2519 data = msi_alloc(*size);
2521 byte[0] = '0';
2522 byte[1] = 'x';
2523 byte[4] = 0;
2524 count = 0;
2525 /* if uneven pad with a zero in front */
2526 if (strlenW(ptr)%2)
2528 byte[2]= '0';
2529 byte[3]= *ptr;
2530 ptr++;
2531 data[count] = (BYTE)strtol(byte,NULL,0);
2532 count ++;
2533 TRACE("Uneven byte count\n");
2535 while (*ptr)
2537 byte[2]= *ptr;
2538 ptr++;
2539 byte[3]= *ptr;
2540 ptr++;
2541 data[count] = (BYTE)strtol(byte,NULL,0);
2542 count ++;
2544 msi_free(deformated);
2546 TRACE("Data %i bytes(%i)\n",*size,count);
2548 else
2550 LPWSTR deformated;
2551 LPWSTR p;
2552 DWORD d = 0;
2553 deformat_string(package, &value[1], &deformated);
2555 *type=REG_DWORD;
2556 *size = sizeof(DWORD);
2557 data = msi_alloc(*size);
2558 p = deformated;
2559 if (*p == '-')
2560 p++;
2561 while (*p)
2563 if ( (*p < '0') || (*p > '9') )
2564 break;
2565 d *= 10;
2566 d += (*p - '0');
2567 p++;
2569 if (deformated[0] == '-')
2570 d = -d;
2571 *(LPDWORD)data = d;
2572 TRACE("DWORD %i\n",*(LPDWORD)data);
2574 msi_free(deformated);
2577 else
2579 const WCHAR *ptr = value;
2581 *type = REG_SZ;
2582 if (value[0] == '#')
2584 ptr++; len--;
2585 if (value[1] == '%')
2587 ptr++; len--;
2588 *type = REG_EXPAND_SZ;
2591 data = (BYTE *)msi_strdupW( ptr, len );
2592 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2593 *size = (len + 1) * sizeof(WCHAR);
2595 return data;
2598 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2600 const WCHAR *ret;
2602 switch (root)
2604 case -1:
2605 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2607 *root_key = HKEY_LOCAL_MACHINE;
2608 ret = szHLM;
2610 else
2612 *root_key = HKEY_CURRENT_USER;
2613 ret = szHCU;
2615 break;
2616 case 0:
2617 *root_key = HKEY_CLASSES_ROOT;
2618 ret = szHCR;
2619 break;
2620 case 1:
2621 *root_key = HKEY_CURRENT_USER;
2622 ret = szHCU;
2623 break;
2624 case 2:
2625 *root_key = HKEY_LOCAL_MACHINE;
2626 ret = szHLM;
2627 break;
2628 case 3:
2629 *root_key = HKEY_USERS;
2630 ret = szHU;
2631 break;
2632 default:
2633 ERR("Unknown root %i\n", root);
2634 return NULL;
2637 return ret;
2640 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2642 REGSAM view = 0;
2643 if (is_wow64 || is_64bit)
2644 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2645 return view;
2648 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access )
2650 WCHAR *subkey, *p, *q;
2651 HKEY hkey, ret = NULL;
2652 LONG res;
2654 access |= get_registry_view( comp );
2656 if (!(subkey = strdupW( path ))) return NULL;
2657 p = subkey;
2658 if ((q = strchrW( p, '\\' ))) *q = 0;
2659 if (create)
2660 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2661 else
2662 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2663 if (res)
2665 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2666 msi_free( subkey );
2667 return NULL;
2669 if (q && q[1])
2671 ret = open_key( comp, hkey, q + 1, create, access );
2672 RegCloseKey( hkey );
2674 else ret = hkey;
2675 msi_free( subkey );
2676 return ret;
2679 static BOOL is_special_entry( const WCHAR *name )
2681 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2684 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2686 const WCHAR *p = str;
2687 WCHAR **ret;
2688 int i = 0;
2690 *count = 0;
2691 if (!str) return NULL;
2692 while ((p - str) < len)
2694 p += strlenW( p ) + 1;
2695 (*count)++;
2697 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2698 p = str;
2699 while ((p - str) < len)
2701 if (!(ret[i] = strdupW( p )))
2703 for (; i >= 0; i--) msi_free( ret[i] );
2704 msi_free( ret );
2705 return NULL;
2707 p += strlenW( p ) + 1;
2708 i++;
2710 return ret;
2713 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2714 WCHAR **right, DWORD right_count, DWORD *size )
2716 WCHAR *ret, *p;
2717 unsigned int i;
2719 *size = sizeof(WCHAR);
2720 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2721 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2723 if (!(ret = p = msi_alloc( *size ))) return NULL;
2725 for (i = 0; i < left_count; i++)
2727 strcpyW( p, left[i] );
2728 p += strlenW( p ) + 1;
2730 for (i = 0; i < right_count; i++)
2732 strcpyW( p, right[i] );
2733 p += strlenW( p ) + 1;
2735 *p = 0;
2736 return ret;
2739 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2740 WCHAR **new, DWORD new_count )
2742 DWORD ret = old_count;
2743 unsigned int i, j, k;
2745 for (i = 0; i < new_count; i++)
2747 for (j = 0; j < old_count; j++)
2749 if (old[j] && !strcmpW( new[i], old[j] ))
2751 msi_free( old[j] );
2752 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2753 old[k] = NULL;
2754 ret--;
2758 return ret;
2761 enum join_op
2763 JOIN_OP_APPEND,
2764 JOIN_OP_PREPEND,
2765 JOIN_OP_REPLACE
2768 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2769 WCHAR **new, DWORD new_count, DWORD *size )
2771 switch (op)
2773 case JOIN_OP_APPEND:
2774 old_count = remove_duplicate_values( old, old_count, new, new_count );
2775 return flatten_multi_string_values( old, old_count, new, new_count, size );
2777 case JOIN_OP_PREPEND:
2778 old_count = remove_duplicate_values( old, old_count, new, new_count );
2779 return flatten_multi_string_values( new, new_count, old, old_count, size );
2781 case JOIN_OP_REPLACE:
2782 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2784 default:
2785 ERR("unhandled join op %u\n", op);
2786 return NULL;
2790 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2791 BYTE *new_value, DWORD new_size, DWORD *size )
2793 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2794 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2795 enum join_op op = JOIN_OP_REPLACE;
2796 WCHAR **old = NULL, **new = NULL;
2797 BYTE *ret;
2799 if (new_size / sizeof(WCHAR) - 1 > 1)
2801 new_ptr = (const WCHAR *)new_value;
2802 new_len = new_size / sizeof(WCHAR) - 1;
2804 if (!new_ptr[0] && new_ptr[new_len - 1])
2806 op = JOIN_OP_APPEND;
2807 new_len--;
2808 new_ptr++;
2810 else if (new_ptr[0] && !new_ptr[new_len - 1])
2812 op = JOIN_OP_PREPEND;
2813 new_len--;
2815 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2817 op = JOIN_OP_REPLACE;
2818 new_len -= 2;
2819 new_ptr++;
2821 new = split_multi_string_values( new_ptr, new_len, &new_count );
2823 if (old_size / sizeof(WCHAR) - 1 > 1)
2825 old_ptr = (const WCHAR *)old_value;
2826 old_len = old_size / sizeof(WCHAR) - 1;
2827 old = split_multi_string_values( old_ptr, old_len, &old_count );
2829 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2830 for (i = 0; i < old_count; i++) msi_free( old[i] );
2831 for (i = 0; i < new_count; i++) msi_free( new[i] );
2832 msi_free( old );
2833 msi_free( new );
2834 return ret;
2837 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2839 BYTE *ret;
2840 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2841 if (!(ret = msi_alloc( *size ))) return NULL;
2842 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2843 return ret;
2846 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2848 MSIPACKAGE *package = param;
2849 BYTE *new_value, *old_value = NULL;
2850 HKEY root_key, hkey;
2851 DWORD type, old_type, new_size, old_size = 0;
2852 LPWSTR deformated, uikey;
2853 const WCHAR *szRoot, *component, *name, *key, *str;
2854 MSICOMPONENT *comp;
2855 MSIRECORD * uirow;
2856 INT root;
2857 BOOL check_first = FALSE;
2858 int len;
2860 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2862 component = MSI_RecordGetString(row, 6);
2863 comp = msi_get_loaded_component(package,component);
2864 if (!comp)
2865 return ERROR_SUCCESS;
2867 comp->Action = msi_get_component_action( package, comp );
2868 if (comp->Action != INSTALLSTATE_LOCAL)
2870 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2871 return ERROR_SUCCESS;
2874 name = MSI_RecordGetString(row, 4);
2875 if( MSI_RecordIsNull(row,5) && name )
2877 /* null values can have special meanings */
2878 if (name[0]=='-' && name[1] == 0)
2879 return ERROR_SUCCESS;
2880 if ((name[0] == '+' || name[0] == '*') && !name[1])
2881 check_first = TRUE;
2884 root = MSI_RecordGetInteger(row,2);
2885 key = MSI_RecordGetString(row, 3);
2887 szRoot = get_root_key( package, root, &root_key );
2888 if (!szRoot)
2889 return ERROR_SUCCESS;
2891 deformat_string(package, key , &deformated);
2892 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2893 strcpyW(uikey,szRoot);
2894 strcatW(uikey,deformated);
2896 if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE )))
2898 ERR("Could not create key %s\n", debugstr_w(deformated));
2899 msi_free(uikey);
2900 msi_free(deformated);
2901 return ERROR_FUNCTION_FAILED;
2903 msi_free( deformated );
2904 str = msi_record_get_string( row, 5, NULL );
2905 len = deformat_string( package, str, &deformated );
2906 new_value = parse_value( package, deformated, len, &type, &new_size );
2908 msi_free( deformated );
2909 deformat_string(package, name, &deformated);
2911 if (!is_special_entry( name ))
2913 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2914 if (type == REG_MULTI_SZ)
2916 BYTE *new;
2917 if (old_value && old_type != REG_MULTI_SZ)
2919 msi_free( old_value );
2920 old_value = NULL;
2921 old_size = 0;
2923 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2924 msi_free( new_value );
2925 new_value = new;
2927 if (!check_first)
2929 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2930 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2932 else if (!old_value)
2934 if (deformated || new_size)
2936 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2937 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2940 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2942 RegCloseKey(hkey);
2944 uirow = MSI_CreateRecord(3);
2945 MSI_RecordSetStringW(uirow,2,deformated);
2946 MSI_RecordSetStringW(uirow,1,uikey);
2947 if (type == REG_SZ || type == REG_EXPAND_SZ)
2948 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2949 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
2950 msiobj_release( &uirow->hdr );
2952 msi_free(new_value);
2953 msi_free(old_value);
2954 msi_free(deformated);
2955 msi_free(uikey);
2957 return ERROR_SUCCESS;
2960 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2962 static const WCHAR query[] = {
2963 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2964 '`','R','e','g','i','s','t','r','y','`',0};
2965 MSIQUERY *view;
2966 UINT rc;
2968 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2969 if (rc != ERROR_SUCCESS)
2970 return ERROR_SUCCESS;
2972 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2973 msiobj_release(&view->hdr);
2974 return rc;
2977 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2979 REGSAM access = 0;
2980 WCHAR *subkey, *p;
2981 HKEY hkey;
2982 LONG res;
2984 access |= get_registry_view( comp );
2986 if (!(subkey = strdupW( path ))) return;
2989 if ((p = strrchrW( subkey, '\\' )))
2991 *p = 0;
2992 if (!p[1]) continue; /* trailing backslash */
2993 hkey = open_key( comp, root, subkey, FALSE, access | READ_CONTROL );
2994 if (!hkey) break;
2995 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2996 RegCloseKey( hkey );
2998 else
2999 res = RegDeleteKeyExW( root, subkey, access, 0 );
3000 if (res)
3002 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
3003 break;
3005 } while (p);
3006 msi_free( subkey );
3009 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
3011 LONG res;
3012 HKEY hkey;
3013 DWORD num_subkeys, num_values;
3015 if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE )))
3017 if ((res = RegDeleteValueW( hkey, value )))
3018 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3020 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3021 NULL, NULL, NULL, NULL );
3022 RegCloseKey( hkey );
3023 if (!res && !num_subkeys && !num_values)
3025 TRACE("removing empty key %s\n", debugstr_w(path));
3026 delete_key( comp, root, path );
3031 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3033 LONG res;
3034 HKEY hkey;
3036 if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return;
3037 res = RegDeleteTreeW( hkey, NULL );
3038 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3039 delete_key( comp, root, path );
3040 RegCloseKey( hkey );
3043 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3045 MSIPACKAGE *package = param;
3046 LPCWSTR component, name, key_str, root_key_str;
3047 LPWSTR deformated_key, deformated_name, ui_key_str;
3048 MSICOMPONENT *comp;
3049 MSIRECORD *uirow;
3050 BOOL delete_key = FALSE;
3051 HKEY hkey_root;
3052 UINT size;
3053 INT root;
3055 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3057 component = MSI_RecordGetString( row, 6 );
3058 comp = msi_get_loaded_component( package, component );
3059 if (!comp)
3060 return ERROR_SUCCESS;
3062 comp->Action = msi_get_component_action( package, comp );
3063 if (comp->Action != INSTALLSTATE_ABSENT)
3065 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3066 return ERROR_SUCCESS;
3069 name = MSI_RecordGetString( row, 4 );
3070 if (MSI_RecordIsNull( row, 5 ) && name )
3072 if (name[0] == '+' && !name[1])
3073 return ERROR_SUCCESS;
3074 if ((name[0] == '-' || name[0] == '*') && !name[1])
3076 delete_key = TRUE;
3077 name = NULL;
3081 root = MSI_RecordGetInteger( row, 2 );
3082 key_str = MSI_RecordGetString( row, 3 );
3084 root_key_str = get_root_key( package, root, &hkey_root );
3085 if (!root_key_str)
3086 return ERROR_SUCCESS;
3088 deformat_string( package, key_str, &deformated_key );
3089 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3090 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3091 strcpyW( ui_key_str, root_key_str );
3092 strcatW( ui_key_str, deformated_key );
3094 deformat_string( package, name, &deformated_name );
3096 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3097 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3098 msi_free( deformated_key );
3100 uirow = MSI_CreateRecord( 2 );
3101 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3102 MSI_RecordSetStringW( uirow, 2, deformated_name );
3103 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3104 msiobj_release( &uirow->hdr );
3106 msi_free( ui_key_str );
3107 msi_free( deformated_name );
3108 return ERROR_SUCCESS;
3111 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3113 MSIPACKAGE *package = param;
3114 LPCWSTR component, name, key_str, root_key_str;
3115 LPWSTR deformated_key, deformated_name, ui_key_str;
3116 MSICOMPONENT *comp;
3117 MSIRECORD *uirow;
3118 BOOL delete_key = FALSE;
3119 HKEY hkey_root;
3120 UINT size;
3121 INT root;
3123 component = MSI_RecordGetString( row, 5 );
3124 comp = msi_get_loaded_component( package, component );
3125 if (!comp)
3126 return ERROR_SUCCESS;
3128 comp->Action = msi_get_component_action( package, comp );
3129 if (comp->Action != INSTALLSTATE_LOCAL)
3131 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3132 return ERROR_SUCCESS;
3135 if ((name = MSI_RecordGetString( row, 4 )))
3137 if (name[0] == '-' && !name[1])
3139 delete_key = TRUE;
3140 name = NULL;
3144 root = MSI_RecordGetInteger( row, 2 );
3145 key_str = MSI_RecordGetString( row, 3 );
3147 root_key_str = get_root_key( package, root, &hkey_root );
3148 if (!root_key_str)
3149 return ERROR_SUCCESS;
3151 deformat_string( package, key_str, &deformated_key );
3152 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3153 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3154 strcpyW( ui_key_str, root_key_str );
3155 strcatW( ui_key_str, deformated_key );
3157 deformat_string( package, name, &deformated_name );
3159 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3160 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3161 msi_free( deformated_key );
3163 uirow = MSI_CreateRecord( 2 );
3164 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3165 MSI_RecordSetStringW( uirow, 2, deformated_name );
3166 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3167 msiobj_release( &uirow->hdr );
3169 msi_free( ui_key_str );
3170 msi_free( deformated_name );
3171 return ERROR_SUCCESS;
3174 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3176 static const WCHAR registry_query[] = {
3177 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3178 '`','R','e','g','i','s','t','r','y','`',0};
3179 static const WCHAR remove_registry_query[] = {
3180 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3181 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3182 MSIQUERY *view;
3183 UINT rc;
3185 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3186 if (rc == ERROR_SUCCESS)
3188 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3189 msiobj_release( &view->hdr );
3190 if (rc != ERROR_SUCCESS)
3191 return rc;
3193 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3194 if (rc == ERROR_SUCCESS)
3196 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3197 msiobj_release( &view->hdr );
3198 if (rc != ERROR_SUCCESS)
3199 return rc;
3201 return ERROR_SUCCESS;
3204 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3206 return ERROR_SUCCESS;
3210 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3212 static const WCHAR query[]= {
3213 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3214 '`','R','e','g','i','s','t','r','y','`',0};
3215 MSICOMPONENT *comp;
3216 DWORD total = 0, count = 0;
3217 MSIQUERY *view;
3218 MSIFEATURE *feature;
3219 MSIFILE *file;
3220 UINT rc;
3222 TRACE("InstallValidate\n");
3224 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3225 if (rc == ERROR_SUCCESS)
3227 rc = MSI_IterateRecords( view, &count, NULL, package );
3228 msiobj_release( &view->hdr );
3229 if (rc != ERROR_SUCCESS)
3230 return rc;
3231 total += count * REG_PROGRESS_VALUE;
3233 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3234 total += COMPONENT_PROGRESS_VALUE;
3236 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3237 total += file->FileSize;
3239 msi_ui_progress( package, 0, total, 0, 0 );
3241 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3243 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3244 debugstr_w(feature->Feature), feature->Installed,
3245 feature->ActionRequest, feature->Action);
3247 return ERROR_SUCCESS;
3250 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3252 MSIPACKAGE* package = param;
3253 LPCWSTR cond = NULL;
3254 LPCWSTR message = NULL;
3255 UINT r;
3257 static const WCHAR title[]=
3258 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3260 cond = MSI_RecordGetString(row,1);
3262 r = MSI_EvaluateConditionW(package,cond);
3263 if (r == MSICONDITION_FALSE)
3265 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3267 LPWSTR deformated;
3268 message = MSI_RecordGetString(row,2);
3269 deformat_string(package,message,&deformated);
3270 MessageBoxW(NULL,deformated,title,MB_OK);
3271 msi_free(deformated);
3274 return ERROR_INSTALL_FAILURE;
3277 return ERROR_SUCCESS;
3280 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3282 static const WCHAR query[] = {
3283 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3284 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3285 MSIQUERY *view;
3286 UINT rc;
3288 TRACE("Checking launch conditions\n");
3290 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3291 if (rc != ERROR_SUCCESS)
3292 return ERROR_SUCCESS;
3294 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3295 msiobj_release(&view->hdr);
3296 return rc;
3299 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3302 if (!cmp->KeyPath)
3303 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3305 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3307 static const WCHAR query[] = {
3308 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3309 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3310 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3311 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3312 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3313 MSIRECORD *row;
3314 UINT root, len;
3315 LPWSTR deformated, buffer, deformated_name;
3316 LPCWSTR key, name;
3318 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3319 if (!row)
3320 return NULL;
3322 root = MSI_RecordGetInteger(row,2);
3323 key = MSI_RecordGetString(row, 3);
3324 name = MSI_RecordGetString(row, 4);
3325 deformat_string(package, key , &deformated);
3326 deformat_string(package, name, &deformated_name);
3328 len = strlenW(deformated) + 6;
3329 if (deformated_name)
3330 len+=strlenW(deformated_name);
3332 buffer = msi_alloc( len *sizeof(WCHAR));
3334 if (deformated_name)
3335 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3336 else
3337 sprintfW(buffer,fmt,root,deformated);
3339 msi_free(deformated);
3340 msi_free(deformated_name);
3341 msiobj_release(&row->hdr);
3343 return buffer;
3345 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3347 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3348 return NULL;
3350 else
3352 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3354 if (file)
3355 return strdupW( file->TargetPath );
3357 return NULL;
3360 static HKEY openSharedDLLsKey(void)
3362 HKEY hkey=0;
3363 static const WCHAR path[] =
3364 {'S','o','f','t','w','a','r','e','\\',
3365 'M','i','c','r','o','s','o','f','t','\\',
3366 'W','i','n','d','o','w','s','\\',
3367 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3368 'S','h','a','r','e','d','D','L','L','s',0};
3370 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3371 return hkey;
3374 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3376 HKEY hkey;
3377 DWORD count=0;
3378 DWORD type;
3379 DWORD sz = sizeof(count);
3380 DWORD rc;
3382 hkey = openSharedDLLsKey();
3383 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3384 if (rc != ERROR_SUCCESS)
3385 count = 0;
3386 RegCloseKey(hkey);
3387 return count;
3390 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3392 HKEY hkey;
3394 hkey = openSharedDLLsKey();
3395 if (count > 0)
3396 msi_reg_set_val_dword( hkey, path, count );
3397 else
3398 RegDeleteValueW(hkey,path);
3399 RegCloseKey(hkey);
3400 return count;
3403 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3405 MSIFEATURE *feature;
3406 INT count = 0;
3407 BOOL write = FALSE;
3409 /* only refcount DLLs */
3410 if (comp->KeyPath == NULL ||
3411 comp->assembly ||
3412 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3413 comp->Attributes & msidbComponentAttributesODBCDataSource)
3414 write = FALSE;
3415 else
3417 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3418 write = (count > 0);
3420 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3421 write = TRUE;
3424 /* increment counts */
3425 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3427 ComponentList *cl;
3429 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3430 continue;
3432 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3434 if ( cl->component == comp )
3435 count++;
3439 /* decrement counts */
3440 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3442 ComponentList *cl;
3444 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3445 continue;
3447 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3449 if ( cl->component == comp )
3450 count--;
3454 /* ref count all the files in the component */
3455 if (write)
3457 MSIFILE *file;
3459 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3461 if (file->Component == comp)
3462 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3466 /* add a count for permanent */
3467 if (comp->Attributes & msidbComponentAttributesPermanent)
3468 count ++;
3470 comp->RefCount = count;
3472 if (write)
3473 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3476 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3478 if (comp->assembly)
3480 const WCHAR prefixW[] = {'<','\\',0};
3481 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3482 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3484 if (keypath)
3486 strcpyW( keypath, prefixW );
3487 strcatW( keypath, comp->assembly->display_name );
3489 return keypath;
3491 return resolve_keypath( package, comp );
3494 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3496 WCHAR squashed_pc[SQUASHED_GUID_SIZE], squashed_cc[SQUASHED_GUID_SIZE];
3497 UINT rc;
3498 MSICOMPONENT *comp;
3499 HKEY hkey;
3501 TRACE("\n");
3503 squash_guid( package->ProductCode, squashed_pc );
3504 msi_set_sourcedir_props(package, FALSE);
3506 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3508 MSIRECORD *uirow;
3509 INSTALLSTATE action;
3511 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3512 if (!comp->ComponentId)
3513 continue;
3515 squash_guid( comp->ComponentId, squashed_cc );
3516 msi_free( comp->FullKeypath );
3517 comp->FullKeypath = build_full_keypath( package, comp );
3519 ACTION_RefCountComponent( package, comp );
3521 if (package->need_rollback) action = comp->Installed;
3522 else action = comp->ActionRequest;
3524 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3525 debugstr_w(comp->Component), debugstr_w(squashed_cc),
3526 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3528 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3530 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3531 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3532 else
3533 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3535 if (rc != ERROR_SUCCESS)
3536 continue;
3538 if (comp->Attributes & msidbComponentAttributesPermanent)
3540 static const WCHAR szPermKey[] =
3541 { '0','0','0','0','0','0','0','0','0','0','0','0',
3542 '0','0','0','0','0','0','0','0','0','0','0','0',
3543 '0','0','0','0','0','0','0','0',0 };
3545 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3547 if (action == INSTALLSTATE_LOCAL)
3548 msi_reg_set_val_str( hkey, squashed_pc, comp->FullKeypath );
3549 else
3551 MSIFILE *file;
3552 MSIRECORD *row;
3553 LPWSTR ptr, ptr2;
3554 WCHAR source[MAX_PATH];
3555 WCHAR base[MAX_PATH];
3556 LPWSTR sourcepath;
3558 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3559 static const WCHAR query[] = {
3560 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3561 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3562 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3563 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3564 '`','D','i','s','k','I','d','`',0};
3566 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3567 continue;
3569 if (!(row = MSI_QueryGetRecord(package->db, query, file->Sequence)))
3570 return ERROR_FUNCTION_FAILED;
3572 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3573 ptr2 = strrchrW(source, '\\') + 1;
3574 msiobj_release(&row->hdr);
3576 lstrcpyW(base, package->PackagePath);
3577 ptr = strrchrW(base, '\\');
3578 *(ptr + 1) = '\0';
3580 sourcepath = msi_resolve_file_source(package, file);
3581 ptr = sourcepath + lstrlenW(base);
3582 lstrcpyW(ptr2, ptr);
3583 msi_free(sourcepath);
3585 msi_reg_set_val_str( hkey, squashed_pc, source );
3587 RegCloseKey(hkey);
3589 else if (action == INSTALLSTATE_ABSENT)
3591 if (comp->num_clients <= 0)
3593 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3594 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3595 else
3596 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3598 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3600 else
3602 LONG res;
3604 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3605 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3606 else
3607 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3609 if (rc != ERROR_SUCCESS)
3611 WARN( "failed to open component key %u\n", rc );
3612 continue;
3614 res = RegDeleteValueW( hkey, squashed_pc );
3615 RegCloseKey(hkey);
3616 if (res) WARN( "failed to delete component value %d\n", res );
3620 /* UI stuff */
3621 uirow = MSI_CreateRecord(3);
3622 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3623 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3624 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3625 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3626 msiobj_release( &uirow->hdr );
3628 return ERROR_SUCCESS;
3631 typedef struct {
3632 CLSID clsid;
3633 LPWSTR source;
3635 LPWSTR path;
3636 ITypeLib *ptLib;
3637 } typelib_struct;
3639 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3640 LPWSTR lpszName, LONG_PTR lParam)
3642 TLIBATTR *attr;
3643 typelib_struct *tl_struct = (typelib_struct*) lParam;
3644 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3645 int sz;
3646 HRESULT res;
3648 if (!IS_INTRESOURCE(lpszName))
3650 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3651 return TRUE;
3654 sz = strlenW(tl_struct->source)+4;
3655 sz *= sizeof(WCHAR);
3657 if ((INT_PTR)lpszName == 1)
3658 tl_struct->path = strdupW(tl_struct->source);
3659 else
3661 tl_struct->path = msi_alloc(sz);
3662 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3665 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3666 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3667 if (FAILED(res))
3669 msi_free(tl_struct->path);
3670 tl_struct->path = NULL;
3672 return TRUE;
3675 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3676 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3678 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3679 return FALSE;
3682 msi_free(tl_struct->path);
3683 tl_struct->path = NULL;
3685 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3686 ITypeLib_Release(tl_struct->ptLib);
3688 return TRUE;
3691 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3693 MSIPACKAGE* package = param;
3694 LPCWSTR component;
3695 MSICOMPONENT *comp;
3696 MSIFILE *file;
3697 typelib_struct tl_struct;
3698 ITypeLib *tlib;
3699 HMODULE module;
3700 HRESULT hr;
3702 component = MSI_RecordGetString(row,3);
3703 comp = msi_get_loaded_component(package,component);
3704 if (!comp)
3705 return ERROR_SUCCESS;
3707 comp->Action = msi_get_component_action( package, comp );
3708 if (comp->Action != INSTALLSTATE_LOCAL)
3710 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3711 return ERROR_SUCCESS;
3714 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3716 TRACE("component has no key path\n");
3717 return ERROR_SUCCESS;
3719 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3721 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3722 if (module)
3724 LPCWSTR guid;
3725 guid = MSI_RecordGetString(row,1);
3726 CLSIDFromString( guid, &tl_struct.clsid);
3727 tl_struct.source = strdupW( file->TargetPath );
3728 tl_struct.path = NULL;
3730 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3731 (LONG_PTR)&tl_struct);
3733 if (tl_struct.path)
3735 LPCWSTR helpid, help_path = NULL;
3736 HRESULT res;
3738 helpid = MSI_RecordGetString(row,6);
3740 if (helpid) help_path = msi_get_target_folder( package, helpid );
3741 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3743 if (FAILED(res))
3744 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3745 else
3746 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3748 ITypeLib_Release(tl_struct.ptLib);
3749 msi_free(tl_struct.path);
3751 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3753 FreeLibrary(module);
3754 msi_free(tl_struct.source);
3756 else
3758 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3759 if (FAILED(hr))
3761 ERR("Failed to load type library: %08x\n", hr);
3762 return ERROR_INSTALL_FAILURE;
3765 ITypeLib_Release(tlib);
3768 return ERROR_SUCCESS;
3771 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3773 static const WCHAR query[] = {
3774 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3775 '`','T','y','p','e','L','i','b','`',0};
3776 MSIQUERY *view;
3777 UINT rc;
3779 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3780 if (rc != ERROR_SUCCESS)
3781 return ERROR_SUCCESS;
3783 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3784 msiobj_release(&view->hdr);
3785 return rc;
3788 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3790 MSIPACKAGE *package = param;
3791 LPCWSTR component, guid;
3792 MSICOMPONENT *comp;
3793 GUID libid;
3794 UINT version;
3795 LCID language;
3796 SYSKIND syskind;
3797 HRESULT hr;
3799 component = MSI_RecordGetString( row, 3 );
3800 comp = msi_get_loaded_component( package, component );
3801 if (!comp)
3802 return ERROR_SUCCESS;
3804 comp->Action = msi_get_component_action( package, comp );
3805 if (comp->Action != INSTALLSTATE_ABSENT)
3807 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3808 return ERROR_SUCCESS;
3810 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3812 guid = MSI_RecordGetString( row, 1 );
3813 CLSIDFromString( guid, &libid );
3814 version = MSI_RecordGetInteger( row, 4 );
3815 language = MSI_RecordGetInteger( row, 2 );
3817 #ifdef _WIN64
3818 syskind = SYS_WIN64;
3819 #else
3820 syskind = SYS_WIN32;
3821 #endif
3823 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3824 if (FAILED(hr))
3826 WARN("Failed to unregister typelib: %08x\n", hr);
3829 return ERROR_SUCCESS;
3832 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3834 static const WCHAR query[] = {
3835 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3836 '`','T','y','p','e','L','i','b','`',0};
3837 MSIQUERY *view;
3838 UINT rc;
3840 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3841 if (rc != ERROR_SUCCESS)
3842 return ERROR_SUCCESS;
3844 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3845 msiobj_release( &view->hdr );
3846 return rc;
3849 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3851 static const WCHAR szlnk[] = {'.','l','n','k',0};
3852 LPCWSTR directory, extension, link_folder;
3853 LPWSTR link_file, filename;
3855 directory = MSI_RecordGetString( row, 2 );
3856 link_folder = msi_get_target_folder( package, directory );
3857 if (!link_folder)
3859 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3860 return NULL;
3862 /* may be needed because of a bug somewhere else */
3863 msi_create_full_path( link_folder );
3865 filename = msi_dup_record_field( row, 3 );
3866 msi_reduce_to_long_filename( filename );
3868 extension = strrchrW( filename, '.' );
3869 if (!extension || strcmpiW( extension, szlnk ))
3871 int len = strlenW( filename );
3872 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3873 memcpy( filename + len, szlnk, sizeof(szlnk) );
3875 link_file = msi_build_directory_name( 2, link_folder, filename );
3876 msi_free( filename );
3878 return link_file;
3881 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3883 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3884 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3885 WCHAR *folder, *dest, *path;
3887 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3888 folder = msi_dup_property( package->db, szWindowsFolder );
3889 else
3891 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3892 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3893 msi_free( appdata );
3895 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3896 msi_create_full_path( dest );
3897 path = msi_build_directory_name( 2, dest, icon_name );
3898 msi_free( folder );
3899 msi_free( dest );
3900 return path;
3903 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3905 MSIPACKAGE *package = param;
3906 LPWSTR link_file, deformated, path;
3907 LPCWSTR component, target;
3908 MSICOMPONENT *comp;
3909 IShellLinkW *sl = NULL;
3910 IPersistFile *pf = NULL;
3911 HRESULT res;
3913 component = MSI_RecordGetString(row, 4);
3914 comp = msi_get_loaded_component(package, component);
3915 if (!comp)
3916 return ERROR_SUCCESS;
3918 comp->Action = msi_get_component_action( package, comp );
3919 if (comp->Action != INSTALLSTATE_LOCAL)
3921 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3922 return ERROR_SUCCESS;
3924 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3926 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3927 &IID_IShellLinkW, (LPVOID *) &sl );
3929 if (FAILED( res ))
3931 ERR("CLSID_ShellLink not available\n");
3932 goto err;
3935 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3936 if (FAILED( res ))
3938 ERR("QueryInterface(IID_IPersistFile) failed\n");
3939 goto err;
3942 target = MSI_RecordGetString(row, 5);
3943 if (strchrW(target, '['))
3945 deformat_string( package, target, &path );
3946 TRACE("target path is %s\n", debugstr_w(path));
3947 IShellLinkW_SetPath( sl, path );
3948 msi_free( path );
3950 else
3952 FIXME("poorly handled shortcut format, advertised shortcut\n");
3953 path = resolve_keypath( package, comp );
3954 IShellLinkW_SetPath( sl, path );
3955 msi_free( path );
3958 if (!MSI_RecordIsNull(row,6))
3960 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3961 deformat_string(package, arguments, &deformated);
3962 IShellLinkW_SetArguments(sl,deformated);
3963 msi_free(deformated);
3966 if (!MSI_RecordIsNull(row,7))
3968 LPCWSTR description = MSI_RecordGetString(row, 7);
3969 IShellLinkW_SetDescription(sl, description);
3972 if (!MSI_RecordIsNull(row,8))
3973 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3975 if (!MSI_RecordIsNull(row,9))
3977 INT index;
3978 LPCWSTR icon = MSI_RecordGetString(row, 9);
3980 path = msi_build_icon_path(package, icon);
3981 index = MSI_RecordGetInteger(row,10);
3983 /* no value means 0 */
3984 if (index == MSI_NULL_INTEGER)
3985 index = 0;
3987 IShellLinkW_SetIconLocation(sl, path, index);
3988 msi_free(path);
3991 if (!MSI_RecordIsNull(row,11))
3992 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3994 if (!MSI_RecordIsNull(row,12))
3996 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3997 full_path = msi_get_target_folder( package, wkdir );
3998 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
4000 link_file = get_link_file(package, row);
4002 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
4003 IPersistFile_Save(pf, link_file, FALSE);
4004 msi_free(link_file);
4006 err:
4007 if (pf)
4008 IPersistFile_Release( pf );
4009 if (sl)
4010 IShellLinkW_Release( sl );
4012 return ERROR_SUCCESS;
4015 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4017 static const WCHAR query[] = {
4018 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4019 '`','S','h','o','r','t','c','u','t','`',0};
4020 MSIQUERY *view;
4021 HRESULT res;
4022 UINT rc;
4024 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4025 if (rc != ERROR_SUCCESS)
4026 return ERROR_SUCCESS;
4028 res = CoInitialize( NULL );
4030 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4031 msiobj_release(&view->hdr);
4033 if (SUCCEEDED(res)) CoUninitialize();
4034 return rc;
4037 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4039 MSIPACKAGE *package = param;
4040 LPWSTR link_file;
4041 LPCWSTR component;
4042 MSICOMPONENT *comp;
4044 component = MSI_RecordGetString( row, 4 );
4045 comp = msi_get_loaded_component( package, component );
4046 if (!comp)
4047 return ERROR_SUCCESS;
4049 comp->Action = msi_get_component_action( package, comp );
4050 if (comp->Action != INSTALLSTATE_ABSENT)
4052 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4053 return ERROR_SUCCESS;
4055 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
4057 link_file = get_link_file( package, row );
4059 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4060 if (!DeleteFileW( link_file ))
4062 WARN("Failed to remove shortcut file %u\n", GetLastError());
4064 msi_free( link_file );
4066 return ERROR_SUCCESS;
4069 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4071 static const WCHAR query[] = {
4072 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4073 '`','S','h','o','r','t','c','u','t','`',0};
4074 MSIQUERY *view;
4075 UINT rc;
4077 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4078 if (rc != ERROR_SUCCESS)
4079 return ERROR_SUCCESS;
4081 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4082 msiobj_release( &view->hdr );
4083 return rc;
4086 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4088 MSIPACKAGE* package = param;
4089 HANDLE the_file;
4090 LPWSTR FilePath;
4091 LPCWSTR FileName;
4092 CHAR buffer[1024];
4093 DWORD sz;
4094 UINT rc;
4096 FileName = MSI_RecordGetString(row,1);
4097 if (!FileName)
4099 ERR("Unable to get FileName\n");
4100 return ERROR_SUCCESS;
4103 FilePath = msi_build_icon_path(package, FileName);
4105 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4107 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4108 FILE_ATTRIBUTE_NORMAL, NULL);
4110 if (the_file == INVALID_HANDLE_VALUE)
4112 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4113 msi_free(FilePath);
4114 return ERROR_SUCCESS;
4119 DWORD write;
4120 sz = 1024;
4121 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4122 if (rc != ERROR_SUCCESS)
4124 ERR("Failed to get stream\n");
4125 DeleteFileW(FilePath);
4126 break;
4128 WriteFile(the_file,buffer,sz,&write,NULL);
4129 } while (sz == 1024);
4131 msi_free(FilePath);
4132 CloseHandle(the_file);
4134 return ERROR_SUCCESS;
4137 static UINT msi_publish_icons(MSIPACKAGE *package)
4139 static const WCHAR query[]= {
4140 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4141 '`','I','c','o','n','`',0};
4142 MSIQUERY *view;
4143 UINT r;
4145 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4146 if (r == ERROR_SUCCESS)
4148 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4149 msiobj_release(&view->hdr);
4150 if (r != ERROR_SUCCESS)
4151 return r;
4153 return ERROR_SUCCESS;
4156 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4158 UINT r;
4159 HKEY source;
4160 LPWSTR buffer;
4161 MSIMEDIADISK *disk;
4162 MSISOURCELISTINFO *info;
4164 r = RegCreateKeyW(hkey, szSourceList, &source);
4165 if (r != ERROR_SUCCESS)
4166 return r;
4168 RegCloseKey(source);
4170 buffer = strrchrW(package->PackagePath, '\\') + 1;
4171 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4172 package->Context, MSICODE_PRODUCT,
4173 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4174 if (r != ERROR_SUCCESS)
4175 return r;
4177 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4178 package->Context, MSICODE_PRODUCT,
4179 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4180 if (r != ERROR_SUCCESS)
4181 return r;
4183 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4184 package->Context, MSICODE_PRODUCT,
4185 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4186 if (r != ERROR_SUCCESS)
4187 return r;
4189 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4191 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4192 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4193 info->options, info->value);
4194 else
4195 MsiSourceListSetInfoW(package->ProductCode, NULL,
4196 info->context, info->options,
4197 info->property, info->value);
4200 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4202 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4203 disk->context, disk->options,
4204 disk->disk_id, disk->volume_label, disk->disk_prompt);
4207 return ERROR_SUCCESS;
4210 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4212 static const WCHAR szARPProductIcon[] =
4213 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4214 static const WCHAR szAssignment[] =
4215 {'A','s','s','i','g','n','m','e','n','t',0};
4216 static const WCHAR szAdvertiseFlags[] =
4217 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4218 static const WCHAR szClients[] =
4219 {'C','l','i','e','n','t','s',0};
4220 static const WCHAR szColon[] = {':',0};
4221 MSIHANDLE hdb, suminfo;
4222 WCHAR *buffer, *ptr, guids[MAX_PATH], packcode[SQUASHED_GUID_SIZE];
4223 DWORD langid, size;
4224 UINT r;
4226 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4227 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4228 msi_free(buffer);
4230 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4231 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4233 /* FIXME */
4234 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4236 buffer = msi_dup_property(package->db, szARPProductIcon);
4237 if (buffer)
4239 LPWSTR path = msi_build_icon_path(package, buffer);
4240 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4241 msi_free(path);
4242 msi_free(buffer);
4245 buffer = msi_dup_property(package->db, szProductVersion);
4246 if (buffer)
4248 DWORD verdword = msi_version_str_to_dword(buffer);
4249 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4250 msi_free(buffer);
4253 msi_reg_set_val_dword(hkey, szAssignment, 0);
4254 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4255 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4256 msi_reg_set_val_str(hkey, szClients, szColon);
4258 hdb = alloc_msihandle(&package->db->hdr);
4259 if (!hdb)
4260 return ERROR_NOT_ENOUGH_MEMORY;
4262 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4263 MsiCloseHandle(hdb);
4264 if (r != ERROR_SUCCESS)
4265 goto done;
4267 size = MAX_PATH;
4268 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4269 NULL, guids, &size);
4270 if (r != ERROR_SUCCESS)
4271 goto done;
4273 ptr = strchrW(guids, ';');
4274 if (ptr) *ptr = 0;
4275 squash_guid(guids, packcode);
4276 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4278 done:
4279 MsiCloseHandle(suminfo);
4280 return ERROR_SUCCESS;
4283 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4285 UINT r;
4286 HKEY hkey;
4287 WCHAR *upgrade, squashed_pc[SQUASHED_GUID_SIZE];
4289 upgrade = msi_dup_property(package->db, szUpgradeCode);
4290 if (!upgrade)
4291 return ERROR_SUCCESS;
4293 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4294 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4295 else
4296 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4298 if (r != ERROR_SUCCESS)
4300 WARN("failed to open upgrade code key\n");
4301 msi_free(upgrade);
4302 return ERROR_SUCCESS;
4304 squash_guid(package->ProductCode, squashed_pc);
4305 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4306 RegCloseKey(hkey);
4307 msi_free(upgrade);
4308 return ERROR_SUCCESS;
4311 static BOOL msi_check_publish(MSIPACKAGE *package)
4313 MSIFEATURE *feature;
4315 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4317 feature->Action = msi_get_feature_action( package, feature );
4318 if (feature->Action == INSTALLSTATE_LOCAL || feature->Action == INSTALLSTATE_SOURCE)
4319 return TRUE;
4322 return FALSE;
4325 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4327 MSIFEATURE *feature;
4329 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4331 feature->Action = msi_get_feature_action( package, feature );
4332 if (feature->Action != INSTALLSTATE_ABSENT)
4333 return FALSE;
4336 return TRUE;
4339 static UINT msi_publish_patches( MSIPACKAGE *package )
4341 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4342 WCHAR patch_squashed[GUID_SIZE];
4343 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4344 LONG res;
4345 MSIPATCHINFO *patch;
4346 UINT r;
4347 WCHAR *p, *all_patches = NULL;
4348 DWORD len = 0;
4350 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4351 if (r != ERROR_SUCCESS)
4352 return ERROR_FUNCTION_FAILED;
4354 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4355 if (res != ERROR_SUCCESS)
4357 r = ERROR_FUNCTION_FAILED;
4358 goto done;
4361 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4362 if (r != ERROR_SUCCESS)
4363 goto done;
4365 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4367 squash_guid( patch->patchcode, patch_squashed );
4368 len += strlenW( patch_squashed ) + 1;
4371 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4372 if (!all_patches)
4373 goto done;
4375 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4377 HKEY patch_key;
4379 squash_guid( patch->patchcode, p );
4380 p += strlenW( p ) + 1;
4382 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4383 (const BYTE *)patch->transforms,
4384 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4385 if (res != ERROR_SUCCESS)
4386 goto done;
4388 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4389 if (r != ERROR_SUCCESS)
4390 goto done;
4392 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4393 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4394 RegCloseKey( patch_key );
4395 if (res != ERROR_SUCCESS)
4396 goto done;
4398 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4400 res = GetLastError();
4401 ERR("Unable to copy patch package %d\n", res);
4402 goto done;
4404 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4405 if (res != ERROR_SUCCESS)
4406 goto done;
4408 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state,
4409 sizeof(patch->state) );
4410 if (res != ERROR_SUCCESS)
4412 RegCloseKey( patch_key );
4413 goto done;
4416 res = RegSetValueExW( patch_key, szUninstallable, 0, REG_DWORD, (const BYTE *)&patch->uninstallable,
4417 sizeof(patch->uninstallable) );
4418 RegCloseKey( patch_key );
4419 if (res != ERROR_SUCCESS)
4420 goto done;
4423 all_patches[len] = 0;
4424 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4425 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4426 if (res != ERROR_SUCCESS)
4427 goto done;
4429 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4430 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4431 if (res != ERROR_SUCCESS)
4432 r = ERROR_FUNCTION_FAILED;
4434 done:
4435 RegCloseKey( product_patches_key );
4436 RegCloseKey( patches_key );
4437 RegCloseKey( product_key );
4438 msi_free( all_patches );
4439 return r;
4442 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4444 UINT rc;
4445 HKEY hukey = NULL, hudkey = NULL;
4446 MSIRECORD *uirow;
4448 if (!list_empty(&package->patches))
4450 rc = msi_publish_patches(package);
4451 if (rc != ERROR_SUCCESS)
4452 goto end;
4455 /* FIXME: also need to publish if the product is in advertise mode */
4456 if (!msi_check_publish(package))
4457 return ERROR_SUCCESS;
4459 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4460 &hukey, TRUE);
4461 if (rc != ERROR_SUCCESS)
4462 goto end;
4464 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4465 NULL, &hudkey, TRUE);
4466 if (rc != ERROR_SUCCESS)
4467 goto end;
4469 rc = msi_publish_upgrade_code(package);
4470 if (rc != ERROR_SUCCESS)
4471 goto end;
4473 rc = msi_publish_product_properties(package, hukey);
4474 if (rc != ERROR_SUCCESS)
4475 goto end;
4477 rc = msi_publish_sourcelist(package, hukey);
4478 if (rc != ERROR_SUCCESS)
4479 goto end;
4481 rc = msi_publish_icons(package);
4483 end:
4484 uirow = MSI_CreateRecord( 1 );
4485 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4486 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4487 msiobj_release( &uirow->hdr );
4489 RegCloseKey(hukey);
4490 RegCloseKey(hudkey);
4491 return rc;
4494 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4496 WCHAR *filename, *ptr, *folder, *ret;
4497 const WCHAR *dirprop;
4499 filename = msi_dup_record_field( row, 2 );
4500 if (filename && (ptr = strchrW( filename, '|' )))
4501 ptr++;
4502 else
4503 ptr = filename;
4505 dirprop = MSI_RecordGetString( row, 3 );
4506 if (dirprop)
4508 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4509 if (!folder) folder = msi_dup_property( package->db, dirprop );
4511 else
4512 folder = msi_dup_property( package->db, szWindowsFolder );
4514 if (!folder)
4516 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4517 msi_free( filename );
4518 return NULL;
4521 ret = msi_build_directory_name( 2, folder, ptr );
4523 msi_free( filename );
4524 msi_free( folder );
4525 return ret;
4528 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4530 MSIPACKAGE *package = param;
4531 LPCWSTR component, section, key, value, identifier;
4532 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4533 MSIRECORD * uirow;
4534 INT action;
4535 MSICOMPONENT *comp;
4537 component = MSI_RecordGetString(row, 8);
4538 comp = msi_get_loaded_component(package,component);
4539 if (!comp)
4540 return ERROR_SUCCESS;
4542 comp->Action = msi_get_component_action( package, comp );
4543 if (comp->Action != INSTALLSTATE_LOCAL)
4545 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4546 return ERROR_SUCCESS;
4549 identifier = MSI_RecordGetString(row,1);
4550 section = MSI_RecordGetString(row,4);
4551 key = MSI_RecordGetString(row,5);
4552 value = MSI_RecordGetString(row,6);
4553 action = MSI_RecordGetInteger(row,7);
4555 deformat_string(package,section,&deformated_section);
4556 deformat_string(package,key,&deformated_key);
4557 deformat_string(package,value,&deformated_value);
4559 fullname = get_ini_file_name(package, row);
4561 if (action == 0)
4563 TRACE("Adding value %s to section %s in %s\n",
4564 debugstr_w(deformated_key), debugstr_w(deformated_section),
4565 debugstr_w(fullname));
4566 WritePrivateProfileStringW(deformated_section, deformated_key,
4567 deformated_value, fullname);
4569 else if (action == 1)
4571 WCHAR returned[10];
4572 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4573 returned, 10, fullname);
4574 if (returned[0] == 0)
4576 TRACE("Adding value %s to section %s in %s\n",
4577 debugstr_w(deformated_key), debugstr_w(deformated_section),
4578 debugstr_w(fullname));
4580 WritePrivateProfileStringW(deformated_section, deformated_key,
4581 deformated_value, fullname);
4584 else if (action == 3)
4585 FIXME("Append to existing section not yet implemented\n");
4587 uirow = MSI_CreateRecord(4);
4588 MSI_RecordSetStringW(uirow,1,identifier);
4589 MSI_RecordSetStringW(uirow,2,deformated_section);
4590 MSI_RecordSetStringW(uirow,3,deformated_key);
4591 MSI_RecordSetStringW(uirow,4,deformated_value);
4592 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4593 msiobj_release( &uirow->hdr );
4595 msi_free(fullname);
4596 msi_free(deformated_key);
4597 msi_free(deformated_value);
4598 msi_free(deformated_section);
4599 return ERROR_SUCCESS;
4602 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4604 static const WCHAR query[] = {
4605 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4606 '`','I','n','i','F','i','l','e','`',0};
4607 MSIQUERY *view;
4608 UINT rc;
4610 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4611 if (rc != ERROR_SUCCESS)
4612 return ERROR_SUCCESS;
4614 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4615 msiobj_release(&view->hdr);
4616 return rc;
4619 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4621 MSIPACKAGE *package = param;
4622 LPCWSTR component, section, key, value, identifier;
4623 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4624 MSICOMPONENT *comp;
4625 MSIRECORD *uirow;
4626 INT action;
4628 component = MSI_RecordGetString( row, 8 );
4629 comp = msi_get_loaded_component( package, component );
4630 if (!comp)
4631 return ERROR_SUCCESS;
4633 comp->Action = msi_get_component_action( package, comp );
4634 if (comp->Action != INSTALLSTATE_ABSENT)
4636 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4637 return ERROR_SUCCESS;
4640 identifier = MSI_RecordGetString( row, 1 );
4641 section = MSI_RecordGetString( row, 4 );
4642 key = MSI_RecordGetString( row, 5 );
4643 value = MSI_RecordGetString( row, 6 );
4644 action = MSI_RecordGetInteger( row, 7 );
4646 deformat_string( package, section, &deformated_section );
4647 deformat_string( package, key, &deformated_key );
4648 deformat_string( package, value, &deformated_value );
4650 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4652 filename = get_ini_file_name( package, row );
4654 TRACE("Removing key %s from section %s in %s\n",
4655 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4657 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4659 WARN("Unable to remove key %u\n", GetLastError());
4661 msi_free( filename );
4663 else
4664 FIXME("Unsupported action %d\n", action);
4667 uirow = MSI_CreateRecord( 4 );
4668 MSI_RecordSetStringW( uirow, 1, identifier );
4669 MSI_RecordSetStringW( uirow, 2, deformated_section );
4670 MSI_RecordSetStringW( uirow, 3, deformated_key );
4671 MSI_RecordSetStringW( uirow, 4, deformated_value );
4672 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4673 msiobj_release( &uirow->hdr );
4675 msi_free( deformated_key );
4676 msi_free( deformated_value );
4677 msi_free( deformated_section );
4678 return ERROR_SUCCESS;
4681 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4683 MSIPACKAGE *package = param;
4684 LPCWSTR component, section, key, value, identifier;
4685 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4686 MSICOMPONENT *comp;
4687 MSIRECORD *uirow;
4688 INT action;
4690 component = MSI_RecordGetString( row, 8 );
4691 comp = msi_get_loaded_component( package, component );
4692 if (!comp)
4693 return ERROR_SUCCESS;
4695 comp->Action = msi_get_component_action( package, comp );
4696 if (comp->Action != INSTALLSTATE_LOCAL)
4698 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4699 return ERROR_SUCCESS;
4702 identifier = MSI_RecordGetString( row, 1 );
4703 section = MSI_RecordGetString( row, 4 );
4704 key = MSI_RecordGetString( row, 5 );
4705 value = MSI_RecordGetString( row, 6 );
4706 action = MSI_RecordGetInteger( row, 7 );
4708 deformat_string( package, section, &deformated_section );
4709 deformat_string( package, key, &deformated_key );
4710 deformat_string( package, value, &deformated_value );
4712 if (action == msidbIniFileActionRemoveLine)
4714 filename = get_ini_file_name( package, row );
4716 TRACE("Removing key %s from section %s in %s\n",
4717 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4719 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4721 WARN("Unable to remove key %u\n", GetLastError());
4723 msi_free( filename );
4725 else
4726 FIXME("Unsupported action %d\n", action);
4728 uirow = MSI_CreateRecord( 4 );
4729 MSI_RecordSetStringW( uirow, 1, identifier );
4730 MSI_RecordSetStringW( uirow, 2, deformated_section );
4731 MSI_RecordSetStringW( uirow, 3, deformated_key );
4732 MSI_RecordSetStringW( uirow, 4, deformated_value );
4733 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4734 msiobj_release( &uirow->hdr );
4736 msi_free( deformated_key );
4737 msi_free( deformated_value );
4738 msi_free( deformated_section );
4739 return ERROR_SUCCESS;
4742 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4744 static const WCHAR query[] = {
4745 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4746 '`','I','n','i','F','i','l','e','`',0};
4747 static const WCHAR remove_query[] = {
4748 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4749 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4750 MSIQUERY *view;
4751 UINT rc;
4753 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4754 if (rc == ERROR_SUCCESS)
4756 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4757 msiobj_release( &view->hdr );
4758 if (rc != ERROR_SUCCESS)
4759 return rc;
4761 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4762 if (rc == ERROR_SUCCESS)
4764 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4765 msiobj_release( &view->hdr );
4766 if (rc != ERROR_SUCCESS)
4767 return rc;
4769 return ERROR_SUCCESS;
4772 static void register_dll( const WCHAR *dll, BOOL unregister )
4774 static const WCHAR regW[] =
4775 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4776 static const WCHAR unregW[] =
4777 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4778 PROCESS_INFORMATION pi;
4779 STARTUPINFOW si;
4780 WCHAR *cmd;
4782 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4784 if (unregister) sprintfW( cmd, unregW, dll );
4785 else sprintfW( cmd, regW, dll );
4787 memset( &si, 0, sizeof(STARTUPINFOW) );
4788 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4790 CloseHandle( pi.hThread );
4791 msi_dialog_check_messages( pi.hProcess );
4792 CloseHandle( pi.hProcess );
4794 msi_free( cmd );
4797 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4799 MSIPACKAGE *package = param;
4800 LPCWSTR filename;
4801 MSIFILE *file;
4802 MSIRECORD *uirow;
4804 filename = MSI_RecordGetString( row, 1 );
4805 file = msi_get_loaded_file( package, filename );
4806 if (!file)
4808 WARN("unable to find file %s\n", debugstr_w(filename));
4809 return ERROR_SUCCESS;
4811 file->Component->Action = msi_get_component_action( package, file->Component );
4812 if (file->Component->Action != INSTALLSTATE_LOCAL)
4814 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4815 return ERROR_SUCCESS;
4818 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4819 register_dll( file->TargetPath, FALSE );
4821 uirow = MSI_CreateRecord( 2 );
4822 MSI_RecordSetStringW( uirow, 1, file->File );
4823 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4824 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4825 msiobj_release( &uirow->hdr );
4827 return ERROR_SUCCESS;
4830 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4832 static const WCHAR query[] = {
4833 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4834 '`','S','e','l','f','R','e','g','`',0};
4835 MSIQUERY *view;
4836 UINT rc;
4838 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4839 if (rc != ERROR_SUCCESS)
4840 return ERROR_SUCCESS;
4842 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4843 msiobj_release(&view->hdr);
4844 return rc;
4847 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4849 MSIPACKAGE *package = param;
4850 LPCWSTR filename;
4851 MSIFILE *file;
4852 MSIRECORD *uirow;
4854 filename = MSI_RecordGetString( row, 1 );
4855 file = msi_get_loaded_file( package, filename );
4856 if (!file)
4858 WARN("unable to find file %s\n", debugstr_w(filename));
4859 return ERROR_SUCCESS;
4861 file->Component->Action = msi_get_component_action( package, file->Component );
4862 if (file->Component->Action != INSTALLSTATE_ABSENT)
4864 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4865 return ERROR_SUCCESS;
4868 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4869 register_dll( file->TargetPath, TRUE );
4871 uirow = MSI_CreateRecord( 2 );
4872 MSI_RecordSetStringW( uirow, 1, file->File );
4873 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4874 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4875 msiobj_release( &uirow->hdr );
4877 return ERROR_SUCCESS;
4880 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4882 static const WCHAR query[] = {
4883 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4884 '`','S','e','l','f','R','e','g','`',0};
4885 MSIQUERY *view;
4886 UINT rc;
4888 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4889 if (rc != ERROR_SUCCESS)
4890 return ERROR_SUCCESS;
4892 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4893 msiobj_release( &view->hdr );
4894 return rc;
4897 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4899 MSIFEATURE *feature;
4900 UINT rc;
4901 HKEY hkey = NULL, userdata = NULL;
4903 if (!msi_check_publish(package))
4904 return ERROR_SUCCESS;
4906 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4907 &hkey, TRUE);
4908 if (rc != ERROR_SUCCESS)
4909 goto end;
4911 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4912 &userdata, TRUE);
4913 if (rc != ERROR_SUCCESS)
4914 goto end;
4916 /* here the guids are base 85 encoded */
4917 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4919 ComponentList *cl;
4920 LPWSTR data = NULL;
4921 GUID clsid;
4922 INT size;
4923 BOOL absent = FALSE;
4924 MSIRECORD *uirow;
4926 if (feature->Level <= 0) continue;
4928 if (feature->Action != INSTALLSTATE_LOCAL &&
4929 feature->Action != INSTALLSTATE_SOURCE &&
4930 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4932 size = 1;
4933 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4935 size += 21;
4937 if (feature->Feature_Parent)
4938 size += strlenW( feature->Feature_Parent )+2;
4940 data = msi_alloc(size * sizeof(WCHAR));
4942 data[0] = 0;
4943 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4945 MSICOMPONENT* component = cl->component;
4946 WCHAR buf[21];
4948 buf[0] = 0;
4949 if (component->ComponentId)
4951 TRACE("From %s\n",debugstr_w(component->ComponentId));
4952 CLSIDFromString(component->ComponentId, &clsid);
4953 encode_base85_guid(&clsid,buf);
4954 TRACE("to %s\n",debugstr_w(buf));
4955 strcatW(data,buf);
4959 if (feature->Feature_Parent)
4961 static const WCHAR sep[] = {'\2',0};
4962 strcatW(data,sep);
4963 strcatW(data,feature->Feature_Parent);
4966 msi_reg_set_val_str( userdata, feature->Feature, data );
4967 msi_free(data);
4969 size = 0;
4970 if (feature->Feature_Parent)
4971 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4972 if (!absent)
4974 size += sizeof(WCHAR);
4975 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4976 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4978 else
4980 size += 2*sizeof(WCHAR);
4981 data = msi_alloc(size);
4982 data[0] = 0x6;
4983 data[1] = 0;
4984 if (feature->Feature_Parent)
4985 strcpyW( &data[1], feature->Feature_Parent );
4986 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4987 (LPBYTE)data,size);
4988 msi_free(data);
4991 /* the UI chunk */
4992 uirow = MSI_CreateRecord( 1 );
4993 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4994 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4995 msiobj_release( &uirow->hdr );
4996 /* FIXME: call msi_ui_progress? */
4999 end:
5000 RegCloseKey(hkey);
5001 RegCloseKey(userdata);
5002 return rc;
5005 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
5007 UINT r;
5008 HKEY hkey;
5009 MSIRECORD *uirow;
5011 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
5013 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
5014 &hkey, FALSE);
5015 if (r == ERROR_SUCCESS)
5017 RegDeleteValueW(hkey, feature->Feature);
5018 RegCloseKey(hkey);
5021 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5022 &hkey, FALSE);
5023 if (r == ERROR_SUCCESS)
5025 RegDeleteValueW(hkey, feature->Feature);
5026 RegCloseKey(hkey);
5029 uirow = MSI_CreateRecord( 1 );
5030 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5031 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5032 msiobj_release( &uirow->hdr );
5034 return ERROR_SUCCESS;
5037 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5039 MSIFEATURE *feature;
5041 if (!msi_check_unpublish(package))
5042 return ERROR_SUCCESS;
5044 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5046 msi_unpublish_feature(package, feature);
5049 return ERROR_SUCCESS;
5052 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5054 SYSTEMTIME systime;
5055 DWORD size, langid;
5056 WCHAR date[9], *val, *buffer;
5057 const WCHAR *prop, *key;
5059 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5060 static const WCHAR modpath_fmt[] =
5061 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5062 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5063 static const WCHAR szModifyPath[] =
5064 {'M','o','d','i','f','y','P','a','t','h',0};
5065 static const WCHAR szUninstallString[] =
5066 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5067 static const WCHAR szEstimatedSize[] =
5068 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5069 static const WCHAR szDisplayVersion[] =
5070 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5071 static const WCHAR szInstallSource[] =
5072 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5073 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5074 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5075 static const WCHAR szAuthorizedCDFPrefix[] =
5076 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5077 static const WCHAR szARPCONTACT[] =
5078 {'A','R','P','C','O','N','T','A','C','T',0};
5079 static const WCHAR szContact[] =
5080 {'C','o','n','t','a','c','t',0};
5081 static const WCHAR szARPCOMMENTS[] =
5082 {'A','R','P','C','O','M','M','E','N','T','S',0};
5083 static const WCHAR szComments[] =
5084 {'C','o','m','m','e','n','t','s',0};
5085 static const WCHAR szProductName[] =
5086 {'P','r','o','d','u','c','t','N','a','m','e',0};
5087 static const WCHAR szDisplayName[] =
5088 {'D','i','s','p','l','a','y','N','a','m','e',0};
5089 static const WCHAR szARPHELPLINK[] =
5090 {'A','R','P','H','E','L','P','L','I','N','K',0};
5091 static const WCHAR szHelpLink[] =
5092 {'H','e','l','p','L','i','n','k',0};
5093 static const WCHAR szARPHELPTELEPHONE[] =
5094 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5095 static const WCHAR szHelpTelephone[] =
5096 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5097 static const WCHAR szARPINSTALLLOCATION[] =
5098 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5099 static const WCHAR szManufacturer[] =
5100 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5101 static const WCHAR szPublisher[] =
5102 {'P','u','b','l','i','s','h','e','r',0};
5103 static const WCHAR szARPREADME[] =
5104 {'A','R','P','R','E','A','D','M','E',0};
5105 static const WCHAR szReadme[] =
5106 {'R','e','a','d','M','e',0};
5107 static const WCHAR szARPSIZE[] =
5108 {'A','R','P','S','I','Z','E',0};
5109 static const WCHAR szSize[] =
5110 {'S','i','z','e',0};
5111 static const WCHAR szARPURLINFOABOUT[] =
5112 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5113 static const WCHAR szURLInfoAbout[] =
5114 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5115 static const WCHAR szARPURLUPDATEINFO[] =
5116 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5117 static const WCHAR szURLUpdateInfo[] =
5118 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5119 static const WCHAR szARPSYSTEMCOMPONENT[] =
5120 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5121 static const WCHAR szSystemComponent[] =
5122 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5124 static const WCHAR *propval[] = {
5125 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5126 szARPCONTACT, szContact,
5127 szARPCOMMENTS, szComments,
5128 szProductName, szDisplayName,
5129 szARPHELPLINK, szHelpLink,
5130 szARPHELPTELEPHONE, szHelpTelephone,
5131 szARPINSTALLLOCATION, szInstallLocation,
5132 szSourceDir, szInstallSource,
5133 szManufacturer, szPublisher,
5134 szARPREADME, szReadme,
5135 szARPSIZE, szSize,
5136 szARPURLINFOABOUT, szURLInfoAbout,
5137 szARPURLUPDATEINFO, szURLUpdateInfo,
5138 NULL
5140 const WCHAR **p = propval;
5142 while (*p)
5144 prop = *p++;
5145 key = *p++;
5146 val = msi_dup_property(package->db, prop);
5147 msi_reg_set_val_str(hkey, key, val);
5148 msi_free(val);
5151 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5152 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5154 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5156 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5157 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5158 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5159 msi_free(buffer);
5161 /* FIXME: Write real Estimated Size when we have it */
5162 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5164 GetLocalTime(&systime);
5165 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5166 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5168 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5169 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5171 buffer = msi_dup_property(package->db, szProductVersion);
5172 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5173 if (buffer)
5175 DWORD verdword = msi_version_str_to_dword(buffer);
5177 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5178 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5179 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5180 msi_free(buffer);
5183 return ERROR_SUCCESS;
5186 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5188 WCHAR *upgrade_code, squashed_pc[SQUASHED_GUID_SIZE];
5189 MSIRECORD *uirow;
5190 HKEY hkey, props, upgrade_key;
5191 UINT rc;
5193 /* FIXME: also need to publish if the product is in advertise mode */
5194 if (!msi_check_publish(package))
5195 return ERROR_SUCCESS;
5197 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5198 if (rc != ERROR_SUCCESS)
5199 return rc;
5201 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5202 if (rc != ERROR_SUCCESS)
5203 goto done;
5205 rc = msi_publish_install_properties(package, hkey);
5206 if (rc != ERROR_SUCCESS)
5207 goto done;
5209 rc = msi_publish_install_properties(package, props);
5210 if (rc != ERROR_SUCCESS)
5211 goto done;
5213 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5214 if (upgrade_code)
5216 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5217 if (rc == ERROR_SUCCESS)
5219 squash_guid( package->ProductCode, squashed_pc );
5220 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5221 RegCloseKey( upgrade_key );
5223 msi_free( upgrade_code );
5225 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5226 package->delete_on_close = FALSE;
5228 done:
5229 uirow = MSI_CreateRecord( 1 );
5230 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5231 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5232 msiobj_release( &uirow->hdr );
5234 RegCloseKey(hkey);
5235 return ERROR_SUCCESS;
5238 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5240 return execute_script(package, SCRIPT_INSTALL);
5243 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5245 MSIPACKAGE *package = param;
5246 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5247 WCHAR *p, *icon_path;
5249 if (!icon) return ERROR_SUCCESS;
5250 if ((icon_path = msi_build_icon_path( package, icon )))
5252 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5253 DeleteFileW( icon_path );
5254 if ((p = strrchrW( icon_path, '\\' )))
5256 *p = 0;
5257 RemoveDirectoryW( icon_path );
5259 msi_free( icon_path );
5261 return ERROR_SUCCESS;
5264 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5266 static const WCHAR query[]= {
5267 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5268 MSIQUERY *view;
5269 UINT r;
5271 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5272 if (r == ERROR_SUCCESS)
5274 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5275 msiobj_release( &view->hdr );
5276 if (r != ERROR_SUCCESS)
5277 return r;
5279 return ERROR_SUCCESS;
5282 static void remove_product_upgrade_code( MSIPACKAGE *package )
5284 WCHAR *code, product[SQUASHED_GUID_SIZE];
5285 HKEY hkey;
5286 LONG res;
5287 DWORD count;
5289 squash_guid( package->ProductCode, product );
5290 if (!(code = msi_dup_property( package->db, szUpgradeCode )))
5292 WARN( "upgrade code not found\n" );
5293 return;
5295 if (!MSIREG_OpenUpgradeCodesKey( code, &hkey, FALSE ))
5297 RegDeleteValueW( hkey, product );
5298 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5299 RegCloseKey( hkey );
5300 if (!res && !count) MSIREG_DeleteUpgradeCodesKey( code );
5302 if (!MSIREG_OpenUserUpgradeCodesKey( code, &hkey, FALSE ))
5304 RegDeleteValueW( hkey, product );
5305 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5306 RegCloseKey( hkey );
5307 if (!res && !count) MSIREG_DeleteUserUpgradeCodesKey( code );
5309 if (!MSIREG_OpenClassesUpgradeCodesKey( code, &hkey, FALSE ))
5311 RegDeleteValueW( hkey, product );
5312 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5313 RegCloseKey( hkey );
5314 if (!res && !count) MSIREG_DeleteClassesUpgradeCodesKey( code );
5317 msi_free( code );
5320 static UINT ACTION_UnpublishProduct(MSIPACKAGE *package)
5322 MSIPATCHINFO *patch;
5324 MSIREG_DeleteProductKey(package->ProductCode);
5325 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5326 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5328 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5329 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5330 MSIREG_DeleteUserProductKey(package->ProductCode);
5331 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5333 remove_product_upgrade_code( package );
5335 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5337 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5338 if (!strcmpW( package->ProductCode, patch->products ))
5340 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5341 patch->delete_on_close = TRUE;
5343 /* FIXME: remove local patch package if this is the last product */
5345 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5346 package->delete_on_close = TRUE;
5348 msi_unpublish_icons( package );
5349 return ERROR_SUCCESS;
5352 static BOOL is_full_uninstall( MSIPACKAGE *package )
5354 WCHAR **features, *remove = msi_dup_property( package->db, szRemove );
5355 MSIFEATURE *feature;
5356 BOOL ret = TRUE;
5357 UINT i;
5359 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5361 if (feature->Action == INSTALLSTATE_LOCAL || feature->Action == INSTALLSTATE_SOURCE) ret = FALSE;
5364 features = msi_split_string( remove, ',' );
5365 for (i = 0; features && features[i]; i++)
5367 if (!strcmpW( features[i], szAll )) ret = TRUE;
5370 msi_free(features);
5371 msi_free(remove);
5372 return ret;
5375 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5377 UINT rc;
5379 /* first do the same as an InstallExecute */
5380 rc = execute_script(package, SCRIPT_INSTALL);
5381 if (rc != ERROR_SUCCESS)
5382 return rc;
5384 /* then handle commit actions */
5385 rc = execute_script(package, SCRIPT_COMMIT);
5386 if (rc != ERROR_SUCCESS)
5387 return rc;
5389 if (is_full_uninstall(package))
5390 rc = ACTION_UnpublishProduct(package);
5392 return rc;
5395 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5397 static const WCHAR RunOnce[] = {
5398 'S','o','f','t','w','a','r','e','\\',
5399 'M','i','c','r','o','s','o','f','t','\\',
5400 'W','i','n','d','o','w','s','\\',
5401 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5402 'R','u','n','O','n','c','e',0};
5403 static const WCHAR InstallRunOnce[] = {
5404 'S','o','f','t','w','a','r','e','\\',
5405 'M','i','c','r','o','s','o','f','t','\\',
5406 'W','i','n','d','o','w','s','\\',
5407 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5408 'I','n','s','t','a','l','l','e','r','\\',
5409 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5411 static const WCHAR msiexec_fmt[] = {
5412 '%','s',
5413 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5414 '\"','%','s','\"',0};
5415 static const WCHAR install_fmt[] = {
5416 '/','I',' ','\"','%','s','\"',' ',
5417 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5418 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5419 WCHAR buffer[256], sysdir[MAX_PATH], squashed_pc[SQUASHED_GUID_SIZE];
5420 HKEY hkey;
5422 squash_guid( package->ProductCode, squashed_pc );
5424 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5425 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5426 snprintfW( buffer, sizeof(buffer)/sizeof(buffer[0]), msiexec_fmt, sysdir, squashed_pc );
5428 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5429 RegCloseKey(hkey);
5431 TRACE("Reboot command %s\n",debugstr_w(buffer));
5433 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5434 sprintfW( buffer, install_fmt, package->ProductCode, squashed_pc );
5436 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5437 RegCloseKey(hkey);
5439 return ERROR_INSTALL_SUSPEND;
5442 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5444 DWORD attrib;
5445 UINT rc;
5448 * We are currently doing what should be done here in the top level Install
5449 * however for Administrative and uninstalls this step will be needed
5451 if (!package->PackagePath)
5452 return ERROR_SUCCESS;
5454 msi_set_sourcedir_props(package, TRUE);
5456 attrib = GetFileAttributesW(package->db->path);
5457 if (attrib == INVALID_FILE_ATTRIBUTES)
5459 MSIRECORD *record;
5460 LPWSTR prompt;
5461 DWORD size = 0;
5463 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5464 package->Context, MSICODE_PRODUCT,
5465 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5466 if (rc == ERROR_MORE_DATA)
5468 prompt = msi_alloc(size * sizeof(WCHAR));
5469 MsiSourceListGetInfoW(package->ProductCode, NULL,
5470 package->Context, MSICODE_PRODUCT,
5471 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5473 else
5474 prompt = strdupW(package->db->path);
5476 record = MSI_CreateRecord(2);
5477 MSI_RecordSetInteger(record, 1, MSIERR_INSERTDISK);
5478 MSI_RecordSetStringW(record, 2, prompt);
5479 msi_free(prompt);
5480 while(attrib == INVALID_FILE_ATTRIBUTES)
5482 MSI_RecordSetStringW(record, 0, NULL);
5483 rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ERROR, record);
5484 if (rc == IDCANCEL)
5485 return ERROR_INSTALL_USEREXIT;
5486 attrib = GetFileAttributesW(package->db->path);
5488 rc = ERROR_SUCCESS;
5490 else
5491 return ERROR_SUCCESS;
5493 return rc;
5496 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5498 HKEY hkey = 0;
5499 LPWSTR buffer, productid = NULL;
5500 UINT i, rc = ERROR_SUCCESS;
5501 MSIRECORD *uirow;
5503 static const WCHAR szPropKeys[][80] =
5505 {'P','r','o','d','u','c','t','I','D',0},
5506 {'U','S','E','R','N','A','M','E',0},
5507 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5508 {0},
5511 static const WCHAR szRegKeys[][80] =
5513 {'P','r','o','d','u','c','t','I','D',0},
5514 {'R','e','g','O','w','n','e','r',0},
5515 {'R','e','g','C','o','m','p','a','n','y',0},
5516 {0},
5519 if (msi_check_unpublish(package))
5521 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5522 goto end;
5525 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5526 if (!productid)
5527 goto end;
5529 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5530 NULL, &hkey, TRUE);
5531 if (rc != ERROR_SUCCESS)
5532 goto end;
5534 for( i = 0; szPropKeys[i][0]; i++ )
5536 buffer = msi_dup_property( package->db, szPropKeys[i] );
5537 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5538 msi_free( buffer );
5541 end:
5542 uirow = MSI_CreateRecord( 1 );
5543 MSI_RecordSetStringW( uirow, 1, productid );
5544 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5545 msiobj_release( &uirow->hdr );
5547 msi_free(productid);
5548 RegCloseKey(hkey);
5549 return rc;
5552 static UINT iterate_properties(MSIRECORD *record, void *param)
5554 static const WCHAR prop_template[] =
5555 {'P','r','o','p','e','r','t','y','(','S',')',':',' ','[','1',']',' ','=',' ','[','2',']',0};
5556 MSIRECORD *uirow;
5558 uirow = MSI_CloneRecord(record);
5559 if (!uirow) return ERROR_OUTOFMEMORY;
5560 MSI_RecordSetStringW(uirow, 0, prop_template);
5561 MSI_ProcessMessage(param, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow);
5562 msiobj_release(&uirow->hdr);
5564 return ERROR_SUCCESS;
5568 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5570 static const WCHAR prop_query[] =
5571 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','_','P','r','o','p','e','r','t','y','`',0};
5572 WCHAR *productname;
5573 WCHAR *action;
5574 WCHAR *info_template;
5575 MSIQUERY *view;
5576 MSIRECORD *uirow, *uirow_info;
5577 UINT rc;
5579 /* Send COMMONDATA and INFO messages. */
5580 /* FIXME: when should these messages be sent? [see also MsiOpenPackage()] */
5581 uirow = MSI_CreateRecord(3);
5582 if (!uirow) return ERROR_OUTOFMEMORY;
5583 MSI_RecordSetStringW(uirow, 0, NULL);
5584 MSI_RecordSetInteger(uirow, 1, 0);
5585 MSI_RecordSetInteger(uirow, 2, package->num_langids ? package->langids[0] : 0);
5586 MSI_RecordSetInteger(uirow, 3, msi_get_string_table_codepage(package->db->strings));
5587 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5588 /* FIXME: send INSTALLMESSAGE_PROGRESS */
5589 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5591 if (!(needs_ui_sequence(package) && ui_sequence_exists(package)))
5593 uirow_info = MSI_CreateRecord(0);
5594 if (!uirow_info)
5596 msiobj_release(&uirow->hdr);
5597 return ERROR_OUTOFMEMORY;
5599 info_template = msi_get_error_message(package->db, MSIERR_INFO_LOGGINGSTART);
5600 MSI_RecordSetStringW(uirow_info, 0, info_template);
5601 msi_free(info_template);
5602 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow_info);
5603 msiobj_release(&uirow_info->hdr);
5606 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5608 productname = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
5609 MSI_RecordSetInteger(uirow, 1, 1);
5610 MSI_RecordSetStringW(uirow, 2, productname);
5611 MSI_RecordSetStringW(uirow, 3, NULL);
5612 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5613 msiobj_release(&uirow->hdr);
5615 package->LastActionResult = MSI_NULL_INTEGER;
5617 action = msi_dup_property(package->db, szEXECUTEACTION);
5618 if (!action) action = msi_strdupW(szINSTALL, strlenW(szINSTALL));
5620 /* Perform the action. Top-level actions trigger a sequence. */
5621 if (!strcmpW(action, szINSTALL))
5623 /* Send ACTIONSTART/INFO and INSTALLSTART. */
5624 ui_actionstart(package, szINSTALL, NULL, NULL);
5625 ui_actioninfo(package, szINSTALL, TRUE, 0);
5626 uirow = MSI_CreateRecord(2);
5627 if (!uirow)
5629 rc = ERROR_OUTOFMEMORY;
5630 goto end;
5632 MSI_RecordSetStringW(uirow, 0, NULL);
5633 MSI_RecordSetStringW(uirow, 1, productname);
5634 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5635 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLSTART, uirow);
5636 msiobj_release(&uirow->hdr);
5638 /* Perform the installation. Always use the ExecuteSequence. */
5639 package->InWhatSequence |= SEQUENCE_EXEC;
5640 rc = ACTION_ProcessExecSequence(package);
5642 /* Send return value and INSTALLEND. */
5643 ui_actioninfo(package, szINSTALL, FALSE, !rc);
5644 uirow = MSI_CreateRecord(3);
5645 if (!uirow)
5647 rc = ERROR_OUTOFMEMORY;
5648 goto end;
5650 MSI_RecordSetStringW(uirow, 0, NULL);
5651 MSI_RecordSetStringW(uirow, 1, productname);
5652 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5653 MSI_RecordSetInteger(uirow, 3, !rc);
5654 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLEND, uirow);
5655 msiobj_release(&uirow->hdr);
5657 else
5658 rc = ACTION_PerformAction(package, action);
5660 /* Send all set properties. */
5661 if (!MSI_OpenQuery(package->db, &view, prop_query))
5663 MSI_IterateRecords(view, NULL, iterate_properties, package);
5664 msiobj_release(&view->hdr);
5667 /* And finally, toggle the cancel off and on. */
5668 uirow = MSI_CreateRecord(2);
5669 if (!uirow)
5671 rc = ERROR_OUTOFMEMORY;
5672 goto end;
5674 MSI_RecordSetStringW(uirow, 0, NULL);
5675 MSI_RecordSetInteger(uirow, 1, 2);
5676 MSI_RecordSetInteger(uirow, 2, 0);
5677 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5678 MSI_RecordSetInteger(uirow, 2, 1);
5679 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5680 msiobj_release(&uirow->hdr);
5682 end:
5683 msi_free(productname);
5684 msi_free(action);
5685 return rc;
5688 static UINT ACTION_INSTALL(MSIPACKAGE *package)
5690 msi_set_property(package->db, szEXECUTEACTION, szINSTALL, -1);
5691 if (needs_ui_sequence(package) && ui_sequence_exists(package))
5693 package->InWhatSequence |= SEQUENCE_UI;
5694 return ACTION_ProcessUISequence(package);
5696 else
5697 return ACTION_ExecuteAction(package);
5700 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5702 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5703 WCHAR productid_85[21], component_85[21], *ret;
5704 GUID clsid;
5705 DWORD sz;
5707 /* > is used if there is a component GUID and < if not. */
5709 productid_85[0] = 0;
5710 component_85[0] = 0;
5711 CLSIDFromString( package->ProductCode, &clsid );
5713 encode_base85_guid( &clsid, productid_85 );
5714 if (component)
5716 CLSIDFromString( component->ComponentId, &clsid );
5717 encode_base85_guid( &clsid, component_85 );
5720 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5721 debugstr_w(component_85));
5723 sz = 20 + strlenW( feature ) + 20 + 3;
5724 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5725 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5726 return ret;
5729 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5731 MSIPACKAGE *package = param;
5732 LPCWSTR compgroupid, component, feature, qualifier, text;
5733 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5734 HKEY hkey = NULL;
5735 UINT rc;
5736 MSICOMPONENT *comp;
5737 MSIFEATURE *feat;
5738 DWORD sz;
5739 MSIRECORD *uirow;
5740 int len;
5742 feature = MSI_RecordGetString(rec, 5);
5743 feat = msi_get_loaded_feature(package, feature);
5744 if (!feat)
5745 return ERROR_SUCCESS;
5747 feat->Action = msi_get_feature_action( package, feat );
5748 if (feat->Action != INSTALLSTATE_LOCAL &&
5749 feat->Action != INSTALLSTATE_SOURCE &&
5750 feat->Action != INSTALLSTATE_ADVERTISED)
5752 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5753 return ERROR_SUCCESS;
5756 component = MSI_RecordGetString(rec, 3);
5757 comp = msi_get_loaded_component(package, component);
5758 if (!comp)
5759 return ERROR_SUCCESS;
5761 compgroupid = MSI_RecordGetString(rec,1);
5762 qualifier = MSI_RecordGetString(rec,2);
5764 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5765 if (rc != ERROR_SUCCESS)
5766 goto end;
5768 advertise = msi_create_component_advertise_string( package, comp, feature );
5769 text = MSI_RecordGetString( rec, 4 );
5770 if (text)
5772 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5773 strcpyW( p, advertise );
5774 strcatW( p, text );
5775 msi_free( advertise );
5776 advertise = p;
5778 existing = msi_reg_get_val_str( hkey, qualifier );
5780 sz = strlenW( advertise ) + 1;
5781 if (existing)
5783 for (p = existing; *p; p += len)
5785 len = strlenW( p ) + 1;
5786 if (strcmpW( advertise, p )) sz += len;
5789 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5791 rc = ERROR_OUTOFMEMORY;
5792 goto end;
5794 q = output;
5795 if (existing)
5797 for (p = existing; *p; p += len)
5799 len = strlenW( p ) + 1;
5800 if (strcmpW( advertise, p ))
5802 memcpy( q, p, len * sizeof(WCHAR) );
5803 q += len;
5807 strcpyW( q, advertise );
5808 q[strlenW( q ) + 1] = 0;
5810 msi_reg_set_val_multi_str( hkey, qualifier, output );
5812 end:
5813 RegCloseKey(hkey);
5814 msi_free( output );
5815 msi_free( advertise );
5816 msi_free( existing );
5818 /* the UI chunk */
5819 uirow = MSI_CreateRecord( 2 );
5820 MSI_RecordSetStringW( uirow, 1, compgroupid );
5821 MSI_RecordSetStringW( uirow, 2, qualifier);
5822 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5823 msiobj_release( &uirow->hdr );
5824 /* FIXME: call ui_progress? */
5826 return rc;
5830 * At present I am ignorning the advertised components part of this and only
5831 * focusing on the qualified component sets
5833 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5835 static const WCHAR query[] = {
5836 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5837 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5838 MSIQUERY *view;
5839 UINT rc;
5841 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5842 if (rc != ERROR_SUCCESS)
5843 return ERROR_SUCCESS;
5845 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5846 msiobj_release(&view->hdr);
5847 return rc;
5850 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5852 static const WCHAR szInstallerComponents[] = {
5853 'S','o','f','t','w','a','r','e','\\',
5854 'M','i','c','r','o','s','o','f','t','\\',
5855 'I','n','s','t','a','l','l','e','r','\\',
5856 'C','o','m','p','o','n','e','n','t','s','\\',0};
5858 MSIPACKAGE *package = param;
5859 LPCWSTR compgroupid, component, feature, qualifier;
5860 MSICOMPONENT *comp;
5861 MSIFEATURE *feat;
5862 MSIRECORD *uirow;
5863 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5864 LONG res;
5866 feature = MSI_RecordGetString( rec, 5 );
5867 feat = msi_get_loaded_feature( package, feature );
5868 if (!feat)
5869 return ERROR_SUCCESS;
5871 feat->Action = msi_get_feature_action( package, feat );
5872 if (feat->Action != INSTALLSTATE_ABSENT)
5874 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5875 return ERROR_SUCCESS;
5878 component = MSI_RecordGetString( rec, 3 );
5879 comp = msi_get_loaded_component( package, component );
5880 if (!comp)
5881 return ERROR_SUCCESS;
5883 compgroupid = MSI_RecordGetString( rec, 1 );
5884 qualifier = MSI_RecordGetString( rec, 2 );
5886 squash_guid( compgroupid, squashed );
5887 strcpyW( keypath, szInstallerComponents );
5888 strcatW( keypath, squashed );
5890 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5891 if (res != ERROR_SUCCESS)
5893 WARN("Unable to delete component key %d\n", res);
5896 uirow = MSI_CreateRecord( 2 );
5897 MSI_RecordSetStringW( uirow, 1, compgroupid );
5898 MSI_RecordSetStringW( uirow, 2, qualifier );
5899 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5900 msiobj_release( &uirow->hdr );
5902 return ERROR_SUCCESS;
5905 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5907 static const WCHAR query[] = {
5908 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5909 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5910 MSIQUERY *view;
5911 UINT rc;
5913 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5914 if (rc != ERROR_SUCCESS)
5915 return ERROR_SUCCESS;
5917 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5918 msiobj_release( &view->hdr );
5919 return rc;
5922 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5924 static const WCHAR query[] =
5925 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5926 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5927 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5928 MSIPACKAGE *package = param;
5929 MSICOMPONENT *component;
5930 MSIRECORD *row;
5931 MSIFILE *file;
5932 SC_HANDLE hscm = NULL, service = NULL;
5933 LPCWSTR comp, key;
5934 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5935 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5936 DWORD serv_type, start_type, err_control;
5937 BOOL is_vital;
5938 SERVICE_DESCRIPTIONW sd = {NULL};
5939 UINT ret = ERROR_SUCCESS;
5941 comp = MSI_RecordGetString( rec, 12 );
5942 component = msi_get_loaded_component( package, comp );
5943 if (!component)
5945 WARN("service component not found\n");
5946 goto done;
5948 component->Action = msi_get_component_action( package, component );
5949 if (component->Action != INSTALLSTATE_LOCAL)
5951 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5952 goto done;
5954 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5955 if (!hscm)
5957 ERR("Failed to open the SC Manager!\n");
5958 goto done;
5961 start_type = MSI_RecordGetInteger(rec, 5);
5962 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5963 goto done;
5965 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5966 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5967 serv_type = MSI_RecordGetInteger(rec, 4);
5968 err_control = MSI_RecordGetInteger(rec, 6);
5969 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5970 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5971 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5972 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5973 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5974 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5976 /* Should the complete install fail if CreateService fails? */
5977 is_vital = (err_control & msidbServiceInstallErrorControlVital);
5979 /* Remove the msidbServiceInstallErrorControlVital-flag from err_control.
5980 CreateService (under Windows) would fail if not. */
5981 err_control &= ~msidbServiceInstallErrorControlVital;
5983 /* fetch the service path */
5984 row = MSI_QueryGetRecord(package->db, query, comp);
5985 if (!row)
5987 ERR("Query failed\n");
5988 goto done;
5990 if (!(key = MSI_RecordGetString(row, 6)))
5992 msiobj_release(&row->hdr);
5993 goto done;
5995 file = msi_get_loaded_file(package, key);
5996 msiobj_release(&row->hdr);
5997 if (!file)
5999 ERR("Failed to load the service file\n");
6000 goto done;
6003 if (!args || !args[0]) image_path = file->TargetPath;
6004 else
6006 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
6007 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
6009 ret = ERROR_OUTOFMEMORY;
6010 goto done;
6013 strcpyW(image_path, file->TargetPath);
6014 strcatW(image_path, szSpace);
6015 strcatW(image_path, args);
6017 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
6018 start_type, err_control, image_path, load_order,
6019 NULL, depends, serv_name, pass);
6021 if (!service)
6023 if (GetLastError() != ERROR_SERVICE_EXISTS)
6025 WARN("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
6026 if (is_vital)
6027 ret = ERROR_INSTALL_FAILURE;
6031 else if (sd.lpDescription)
6033 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
6034 WARN("failed to set service description %u\n", GetLastError());
6037 if (image_path != file->TargetPath) msi_free(image_path);
6038 done:
6039 if (service) CloseServiceHandle(service);
6040 if (hscm) CloseServiceHandle(hscm);
6041 msi_free(name);
6042 msi_free(disp);
6043 msi_free(sd.lpDescription);
6044 msi_free(load_order);
6045 msi_free(serv_name);
6046 msi_free(pass);
6047 msi_free(depends);
6048 msi_free(args);
6050 return ret;
6053 static UINT ACTION_InstallServices( MSIPACKAGE *package )
6055 static const WCHAR query[] = {
6056 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6057 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
6058 MSIQUERY *view;
6059 UINT rc;
6061 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6062 if (rc != ERROR_SUCCESS)
6063 return ERROR_SUCCESS;
6065 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
6066 msiobj_release(&view->hdr);
6067 return rc;
6070 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
6071 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
6073 LPCWSTR *vector, *temp_vector;
6074 LPWSTR p, q;
6075 DWORD sep_len;
6077 static const WCHAR separator[] = {'[','~',']',0};
6079 *numargs = 0;
6080 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
6082 if (!args)
6083 return NULL;
6085 vector = msi_alloc(sizeof(LPWSTR));
6086 if (!vector)
6087 return NULL;
6089 p = args;
6092 (*numargs)++;
6093 vector[*numargs - 1] = p;
6095 if ((q = strstrW(p, separator)))
6097 *q = '\0';
6099 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
6100 if (!temp_vector)
6102 msi_free(vector);
6103 return NULL;
6105 vector = temp_vector;
6107 p = q + sep_len;
6109 } while (q);
6111 return vector;
6114 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
6116 MSIPACKAGE *package = param;
6117 MSICOMPONENT *comp;
6118 MSIRECORD *uirow;
6119 SC_HANDLE scm = NULL, service = NULL;
6120 LPCWSTR component, *vector = NULL;
6121 LPWSTR name, args, display_name = NULL;
6122 DWORD event, numargs, len, wait, dummy;
6123 UINT r = ERROR_FUNCTION_FAILED;
6124 SERVICE_STATUS_PROCESS status;
6125 ULONGLONG start_time;
6127 component = MSI_RecordGetString(rec, 6);
6128 comp = msi_get_loaded_component(package, component);
6129 if (!comp)
6130 return ERROR_SUCCESS;
6132 event = MSI_RecordGetInteger( rec, 3 );
6133 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6135 comp->Action = msi_get_component_action( package, comp );
6136 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
6137 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
6139 TRACE("not starting %s\n", debugstr_w(name));
6140 msi_free( name );
6141 return ERROR_SUCCESS;
6144 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6145 wait = MSI_RecordGetInteger(rec, 5);
6147 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
6148 if (!scm)
6150 ERR("Failed to open the service control manager\n");
6151 goto done;
6154 len = 0;
6155 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6156 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6158 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6159 GetServiceDisplayNameW( scm, name, display_name, &len );
6162 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6163 if (!service)
6165 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6166 goto done;
6169 vector = msi_service_args_to_vector(args, &numargs);
6171 if (!StartServiceW(service, numargs, vector) &&
6172 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6174 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6175 goto done;
6178 r = ERROR_SUCCESS;
6179 if (wait)
6181 /* wait for at most 30 seconds for the service to be up and running */
6182 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6183 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6185 TRACE("failed to query service status (%u)\n", GetLastError());
6186 goto done;
6188 start_time = GetTickCount64();
6189 while (status.dwCurrentState == SERVICE_START_PENDING)
6191 if (GetTickCount64() - start_time > 30000) break;
6192 Sleep(1000);
6193 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6194 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6196 TRACE("failed to query service status (%u)\n", GetLastError());
6197 goto done;
6200 if (status.dwCurrentState != SERVICE_RUNNING)
6202 WARN("service failed to start %u\n", status.dwCurrentState);
6203 r = ERROR_FUNCTION_FAILED;
6207 done:
6208 uirow = MSI_CreateRecord( 2 );
6209 MSI_RecordSetStringW( uirow, 1, display_name );
6210 MSI_RecordSetStringW( uirow, 2, name );
6211 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6212 msiobj_release( &uirow->hdr );
6214 if (service) CloseServiceHandle(service);
6215 if (scm) CloseServiceHandle(scm);
6217 msi_free(name);
6218 msi_free(args);
6219 msi_free(vector);
6220 msi_free(display_name);
6221 return r;
6224 static UINT ACTION_StartServices( MSIPACKAGE *package )
6226 static const WCHAR query[] = {
6227 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6228 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6229 MSIQUERY *view;
6230 UINT rc;
6232 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6233 if (rc != ERROR_SUCCESS)
6234 return ERROR_SUCCESS;
6236 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6237 msiobj_release(&view->hdr);
6238 return rc;
6241 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6243 DWORD i, needed, count;
6244 ENUM_SERVICE_STATUSW *dependencies;
6245 SERVICE_STATUS ss;
6246 SC_HANDLE depserv;
6247 BOOL stopped, ret = FALSE;
6249 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6250 0, &needed, &count))
6251 return TRUE;
6253 if (GetLastError() != ERROR_MORE_DATA)
6254 return FALSE;
6256 dependencies = msi_alloc(needed);
6257 if (!dependencies)
6258 return FALSE;
6260 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6261 needed, &needed, &count))
6262 goto done;
6264 for (i = 0; i < count; i++)
6266 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6267 SERVICE_STOP | SERVICE_QUERY_STATUS);
6268 if (!depserv)
6269 goto done;
6271 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6272 CloseServiceHandle(depserv);
6273 if (!stopped)
6274 goto done;
6277 ret = TRUE;
6279 done:
6280 msi_free(dependencies);
6281 return ret;
6284 static UINT stop_service( LPCWSTR name )
6286 SC_HANDLE scm = NULL, service = NULL;
6287 SERVICE_STATUS status;
6288 SERVICE_STATUS_PROCESS ssp;
6289 DWORD needed;
6291 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6292 if (!scm)
6294 WARN("Failed to open the SCM: %d\n", GetLastError());
6295 goto done;
6298 service = OpenServiceW(scm, name,
6299 SERVICE_STOP |
6300 SERVICE_QUERY_STATUS |
6301 SERVICE_ENUMERATE_DEPENDENTS);
6302 if (!service)
6304 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6305 goto done;
6308 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6309 sizeof(SERVICE_STATUS_PROCESS), &needed))
6311 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6312 goto done;
6315 if (ssp.dwCurrentState == SERVICE_STOPPED)
6316 goto done;
6318 stop_service_dependents(scm, service);
6320 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6321 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6323 done:
6324 if (service) CloseServiceHandle(service);
6325 if (scm) CloseServiceHandle(scm);
6327 return ERROR_SUCCESS;
6330 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6332 MSIPACKAGE *package = param;
6333 MSICOMPONENT *comp;
6334 MSIRECORD *uirow;
6335 LPCWSTR component;
6336 WCHAR *name, *display_name = NULL;
6337 DWORD event, len;
6338 SC_HANDLE scm;
6340 component = MSI_RecordGetString( rec, 6 );
6341 comp = msi_get_loaded_component( package, component );
6342 if (!comp)
6343 return ERROR_SUCCESS;
6345 event = MSI_RecordGetInteger( rec, 3 );
6346 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6348 comp->Action = msi_get_component_action( package, comp );
6349 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6350 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6352 TRACE("not stopping %s\n", debugstr_w(name));
6353 msi_free( name );
6354 return ERROR_SUCCESS;
6357 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6358 if (!scm)
6360 ERR("Failed to open the service control manager\n");
6361 goto done;
6364 len = 0;
6365 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6366 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6368 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6369 GetServiceDisplayNameW( scm, name, display_name, &len );
6371 CloseServiceHandle( scm );
6373 stop_service( name );
6375 done:
6376 uirow = MSI_CreateRecord( 2 );
6377 MSI_RecordSetStringW( uirow, 1, display_name );
6378 MSI_RecordSetStringW( uirow, 2, name );
6379 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6380 msiobj_release( &uirow->hdr );
6382 msi_free( name );
6383 msi_free( display_name );
6384 return ERROR_SUCCESS;
6387 static UINT ACTION_StopServices( MSIPACKAGE *package )
6389 static const WCHAR query[] = {
6390 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6391 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6392 MSIQUERY *view;
6393 UINT rc;
6395 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6396 if (rc != ERROR_SUCCESS)
6397 return ERROR_SUCCESS;
6399 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6400 msiobj_release(&view->hdr);
6401 return rc;
6404 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6406 MSIPACKAGE *package = param;
6407 MSICOMPONENT *comp;
6408 MSIRECORD *uirow;
6409 LPWSTR name = NULL, display_name = NULL;
6410 DWORD event, len;
6411 SC_HANDLE scm = NULL, service = NULL;
6413 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6414 if (!comp)
6415 return ERROR_SUCCESS;
6417 event = MSI_RecordGetInteger( rec, 3 );
6418 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6420 comp->Action = msi_get_component_action( package, comp );
6421 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6422 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6424 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6425 msi_free( name );
6426 return ERROR_SUCCESS;
6428 stop_service( name );
6430 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6431 if (!scm)
6433 WARN("Failed to open the SCM: %d\n", GetLastError());
6434 goto done;
6437 len = 0;
6438 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6439 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6441 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6442 GetServiceDisplayNameW( scm, name, display_name, &len );
6445 service = OpenServiceW( scm, name, DELETE );
6446 if (!service)
6448 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6449 goto done;
6452 if (!DeleteService( service ))
6453 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6455 done:
6456 uirow = MSI_CreateRecord( 2 );
6457 MSI_RecordSetStringW( uirow, 1, display_name );
6458 MSI_RecordSetStringW( uirow, 2, name );
6459 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6460 msiobj_release( &uirow->hdr );
6462 if (service) CloseServiceHandle( service );
6463 if (scm) CloseServiceHandle( scm );
6464 msi_free( name );
6465 msi_free( display_name );
6467 return ERROR_SUCCESS;
6470 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6472 static const WCHAR query[] = {
6473 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6474 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6475 MSIQUERY *view;
6476 UINT rc;
6478 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6479 if (rc != ERROR_SUCCESS)
6480 return ERROR_SUCCESS;
6482 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6483 msiobj_release( &view->hdr );
6484 return rc;
6487 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6489 MSIPACKAGE *package = param;
6490 LPWSTR driver, driver_path, ptr;
6491 WCHAR outpath[MAX_PATH];
6492 MSIFILE *driver_file = NULL, *setup_file = NULL;
6493 MSICOMPONENT *comp;
6494 MSIRECORD *uirow;
6495 LPCWSTR desc, file_key, component;
6496 DWORD len, usage;
6497 UINT r = ERROR_SUCCESS;
6499 static const WCHAR driver_fmt[] = {
6500 'D','r','i','v','e','r','=','%','s',0};
6501 static const WCHAR setup_fmt[] = {
6502 'S','e','t','u','p','=','%','s',0};
6503 static const WCHAR usage_fmt[] = {
6504 'F','i','l','e','U','s','a','g','e','=','1',0};
6506 component = MSI_RecordGetString( rec, 2 );
6507 comp = msi_get_loaded_component( package, component );
6508 if (!comp)
6509 return ERROR_SUCCESS;
6511 comp->Action = msi_get_component_action( package, comp );
6512 if (comp->Action != INSTALLSTATE_LOCAL)
6514 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6515 return ERROR_SUCCESS;
6517 desc = MSI_RecordGetString(rec, 3);
6519 file_key = MSI_RecordGetString( rec, 4 );
6520 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6522 file_key = MSI_RecordGetString( rec, 5 );
6523 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6525 if (!driver_file)
6527 ERR("ODBC Driver entry not found!\n");
6528 return ERROR_FUNCTION_FAILED;
6531 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6532 if (setup_file)
6533 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6534 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6536 driver = msi_alloc(len * sizeof(WCHAR));
6537 if (!driver)
6538 return ERROR_OUTOFMEMORY;
6540 ptr = driver;
6541 lstrcpyW(ptr, desc);
6542 ptr += lstrlenW(ptr) + 1;
6544 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6545 ptr += len + 1;
6547 if (setup_file)
6549 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6550 ptr += len + 1;
6553 lstrcpyW(ptr, usage_fmt);
6554 ptr += lstrlenW(ptr) + 1;
6555 *ptr = '\0';
6557 if (!driver_file->TargetPath)
6559 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6560 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6562 driver_path = strdupW(driver_file->TargetPath);
6563 ptr = strrchrW(driver_path, '\\');
6564 if (ptr) *ptr = '\0';
6566 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6567 NULL, ODBC_INSTALL_COMPLETE, &usage))
6569 ERR("Failed to install SQL driver!\n");
6570 r = ERROR_FUNCTION_FAILED;
6573 uirow = MSI_CreateRecord( 5 );
6574 MSI_RecordSetStringW( uirow, 1, desc );
6575 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6576 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6577 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6578 msiobj_release( &uirow->hdr );
6580 msi_free(driver);
6581 msi_free(driver_path);
6583 return r;
6586 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6588 MSIPACKAGE *package = param;
6589 LPWSTR translator, translator_path, ptr;
6590 WCHAR outpath[MAX_PATH];
6591 MSIFILE *translator_file = NULL, *setup_file = NULL;
6592 MSICOMPONENT *comp;
6593 MSIRECORD *uirow;
6594 LPCWSTR desc, file_key, component;
6595 DWORD len, usage;
6596 UINT r = ERROR_SUCCESS;
6598 static const WCHAR translator_fmt[] = {
6599 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6600 static const WCHAR setup_fmt[] = {
6601 'S','e','t','u','p','=','%','s',0};
6603 component = MSI_RecordGetString( rec, 2 );
6604 comp = msi_get_loaded_component( package, component );
6605 if (!comp)
6606 return ERROR_SUCCESS;
6608 comp->Action = msi_get_component_action( package, comp );
6609 if (comp->Action != INSTALLSTATE_LOCAL)
6611 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6612 return ERROR_SUCCESS;
6614 desc = MSI_RecordGetString(rec, 3);
6616 file_key = MSI_RecordGetString( rec, 4 );
6617 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6619 file_key = MSI_RecordGetString( rec, 5 );
6620 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6622 if (!translator_file)
6624 ERR("ODBC Translator entry not found!\n");
6625 return ERROR_FUNCTION_FAILED;
6628 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6629 if (setup_file)
6630 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6632 translator = msi_alloc(len * sizeof(WCHAR));
6633 if (!translator)
6634 return ERROR_OUTOFMEMORY;
6636 ptr = translator;
6637 lstrcpyW(ptr, desc);
6638 ptr += lstrlenW(ptr) + 1;
6640 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6641 ptr += len + 1;
6643 if (setup_file)
6645 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6646 ptr += len + 1;
6648 *ptr = '\0';
6650 translator_path = strdupW(translator_file->TargetPath);
6651 ptr = strrchrW(translator_path, '\\');
6652 if (ptr) *ptr = '\0';
6654 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6655 NULL, ODBC_INSTALL_COMPLETE, &usage))
6657 ERR("Failed to install SQL translator!\n");
6658 r = ERROR_FUNCTION_FAILED;
6661 uirow = MSI_CreateRecord( 5 );
6662 MSI_RecordSetStringW( uirow, 1, desc );
6663 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6664 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6665 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6666 msiobj_release( &uirow->hdr );
6668 msi_free(translator);
6669 msi_free(translator_path);
6671 return r;
6674 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6676 MSIPACKAGE *package = param;
6677 MSICOMPONENT *comp;
6678 LPWSTR attrs;
6679 LPCWSTR desc, driver, component;
6680 WORD request = ODBC_ADD_SYS_DSN;
6681 INT registration;
6682 DWORD len;
6683 UINT r = ERROR_SUCCESS;
6684 MSIRECORD *uirow;
6686 static const WCHAR attrs_fmt[] = {
6687 'D','S','N','=','%','s',0 };
6689 component = MSI_RecordGetString( rec, 2 );
6690 comp = msi_get_loaded_component( package, component );
6691 if (!comp)
6692 return ERROR_SUCCESS;
6694 comp->Action = msi_get_component_action( package, comp );
6695 if (comp->Action != INSTALLSTATE_LOCAL)
6697 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6698 return ERROR_SUCCESS;
6701 desc = MSI_RecordGetString(rec, 3);
6702 driver = MSI_RecordGetString(rec, 4);
6703 registration = MSI_RecordGetInteger(rec, 5);
6705 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6706 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6708 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6709 attrs = msi_alloc(len * sizeof(WCHAR));
6710 if (!attrs)
6711 return ERROR_OUTOFMEMORY;
6713 len = sprintfW(attrs, attrs_fmt, desc);
6714 attrs[len + 1] = 0;
6716 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6718 ERR("Failed to install SQL data source!\n");
6719 r = ERROR_FUNCTION_FAILED;
6722 uirow = MSI_CreateRecord( 5 );
6723 MSI_RecordSetStringW( uirow, 1, desc );
6724 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6725 MSI_RecordSetInteger( uirow, 3, request );
6726 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6727 msiobj_release( &uirow->hdr );
6729 msi_free(attrs);
6731 return r;
6734 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6736 static const WCHAR driver_query[] = {
6737 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6738 'O','D','B','C','D','r','i','v','e','r',0};
6739 static const WCHAR translator_query[] = {
6740 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6741 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6742 static const WCHAR source_query[] = {
6743 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6744 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6745 MSIQUERY *view;
6746 UINT rc;
6748 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6749 if (rc == ERROR_SUCCESS)
6751 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6752 msiobj_release(&view->hdr);
6753 if (rc != ERROR_SUCCESS)
6754 return rc;
6756 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6757 if (rc == ERROR_SUCCESS)
6759 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6760 msiobj_release(&view->hdr);
6761 if (rc != ERROR_SUCCESS)
6762 return rc;
6764 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6765 if (rc == ERROR_SUCCESS)
6767 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6768 msiobj_release(&view->hdr);
6769 if (rc != ERROR_SUCCESS)
6770 return rc;
6772 return ERROR_SUCCESS;
6775 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6777 MSIPACKAGE *package = param;
6778 MSICOMPONENT *comp;
6779 MSIRECORD *uirow;
6780 DWORD usage;
6781 LPCWSTR desc, component;
6783 component = MSI_RecordGetString( rec, 2 );
6784 comp = msi_get_loaded_component( package, component );
6785 if (!comp)
6786 return ERROR_SUCCESS;
6788 comp->Action = msi_get_component_action( package, comp );
6789 if (comp->Action != INSTALLSTATE_ABSENT)
6791 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6792 return ERROR_SUCCESS;
6795 desc = MSI_RecordGetString( rec, 3 );
6796 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6798 WARN("Failed to remove ODBC driver\n");
6800 else if (!usage)
6802 FIXME("Usage count reached 0\n");
6805 uirow = MSI_CreateRecord( 2 );
6806 MSI_RecordSetStringW( uirow, 1, desc );
6807 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6808 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6809 msiobj_release( &uirow->hdr );
6811 return ERROR_SUCCESS;
6814 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6816 MSIPACKAGE *package = param;
6817 MSICOMPONENT *comp;
6818 MSIRECORD *uirow;
6819 DWORD usage;
6820 LPCWSTR desc, component;
6822 component = MSI_RecordGetString( rec, 2 );
6823 comp = msi_get_loaded_component( package, component );
6824 if (!comp)
6825 return ERROR_SUCCESS;
6827 comp->Action = msi_get_component_action( package, comp );
6828 if (comp->Action != INSTALLSTATE_ABSENT)
6830 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6831 return ERROR_SUCCESS;
6834 desc = MSI_RecordGetString( rec, 3 );
6835 if (!SQLRemoveTranslatorW( desc, &usage ))
6837 WARN("Failed to remove ODBC translator\n");
6839 else if (!usage)
6841 FIXME("Usage count reached 0\n");
6844 uirow = MSI_CreateRecord( 2 );
6845 MSI_RecordSetStringW( uirow, 1, desc );
6846 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6847 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6848 msiobj_release( &uirow->hdr );
6850 return ERROR_SUCCESS;
6853 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6855 MSIPACKAGE *package = param;
6856 MSICOMPONENT *comp;
6857 MSIRECORD *uirow;
6858 LPWSTR attrs;
6859 LPCWSTR desc, driver, component;
6860 WORD request = ODBC_REMOVE_SYS_DSN;
6861 INT registration;
6862 DWORD len;
6864 static const WCHAR attrs_fmt[] = {
6865 'D','S','N','=','%','s',0 };
6867 component = MSI_RecordGetString( rec, 2 );
6868 comp = msi_get_loaded_component( package, component );
6869 if (!comp)
6870 return ERROR_SUCCESS;
6872 comp->Action = msi_get_component_action( package, comp );
6873 if (comp->Action != INSTALLSTATE_ABSENT)
6875 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6876 return ERROR_SUCCESS;
6879 desc = MSI_RecordGetString( rec, 3 );
6880 driver = MSI_RecordGetString( rec, 4 );
6881 registration = MSI_RecordGetInteger( rec, 5 );
6883 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6884 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6886 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6887 attrs = msi_alloc( len * sizeof(WCHAR) );
6888 if (!attrs)
6889 return ERROR_OUTOFMEMORY;
6891 FIXME("Use ODBCSourceAttribute table\n");
6893 len = sprintfW( attrs, attrs_fmt, desc );
6894 attrs[len + 1] = 0;
6896 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6898 WARN("Failed to remove ODBC data source\n");
6900 msi_free( attrs );
6902 uirow = MSI_CreateRecord( 3 );
6903 MSI_RecordSetStringW( uirow, 1, desc );
6904 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6905 MSI_RecordSetInteger( uirow, 3, request );
6906 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6907 msiobj_release( &uirow->hdr );
6909 return ERROR_SUCCESS;
6912 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6914 static const WCHAR driver_query[] = {
6915 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6916 'O','D','B','C','D','r','i','v','e','r',0};
6917 static const WCHAR translator_query[] = {
6918 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6919 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6920 static const WCHAR source_query[] = {
6921 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6922 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6923 MSIQUERY *view;
6924 UINT rc;
6926 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6927 if (rc == ERROR_SUCCESS)
6929 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6930 msiobj_release( &view->hdr );
6931 if (rc != ERROR_SUCCESS)
6932 return rc;
6934 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6935 if (rc == ERROR_SUCCESS)
6937 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6938 msiobj_release( &view->hdr );
6939 if (rc != ERROR_SUCCESS)
6940 return rc;
6942 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6943 if (rc == ERROR_SUCCESS)
6945 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6946 msiobj_release( &view->hdr );
6947 if (rc != ERROR_SUCCESS)
6948 return rc;
6950 return ERROR_SUCCESS;
6953 #define ENV_ACT_SETALWAYS 0x1
6954 #define ENV_ACT_SETABSENT 0x2
6955 #define ENV_ACT_REMOVE 0x4
6956 #define ENV_ACT_REMOVEMATCH 0x8
6958 #define ENV_MOD_MACHINE 0x20000000
6959 #define ENV_MOD_APPEND 0x40000000
6960 #define ENV_MOD_PREFIX 0x80000000
6961 #define ENV_MOD_MASK 0xC0000000
6963 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6965 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6967 LPCWSTR cptr = *name;
6969 static const WCHAR prefix[] = {'[','~',']',0};
6970 static const int prefix_len = 3;
6972 *flags = 0;
6973 while (*cptr)
6975 if (*cptr == '=')
6976 *flags |= ENV_ACT_SETALWAYS;
6977 else if (*cptr == '+')
6978 *flags |= ENV_ACT_SETABSENT;
6979 else if (*cptr == '-')
6980 *flags |= ENV_ACT_REMOVE;
6981 else if (*cptr == '!')
6982 *flags |= ENV_ACT_REMOVEMATCH;
6983 else if (*cptr == '*')
6984 *flags |= ENV_MOD_MACHINE;
6985 else
6986 break;
6988 cptr++;
6989 (*name)++;
6992 if (!*cptr)
6994 ERR("Missing environment variable\n");
6995 return ERROR_FUNCTION_FAILED;
6998 if (*value)
7000 LPCWSTR ptr = *value;
7001 if (!strncmpW(ptr, prefix, prefix_len))
7003 if (ptr[prefix_len] == szSemiColon[0])
7005 *flags |= ENV_MOD_APPEND;
7006 *value += lstrlenW(prefix);
7008 else
7010 *value = NULL;
7013 else if (lstrlenW(*value) >= prefix_len)
7015 ptr += lstrlenW(ptr) - prefix_len;
7016 if (!strcmpW( ptr, prefix ))
7018 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
7020 *flags |= ENV_MOD_PREFIX;
7021 /* the "[~]" will be removed by deformat_string */;
7023 else
7025 *value = NULL;
7031 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
7032 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
7033 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
7034 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
7036 ERR("Invalid flags: %08x\n", *flags);
7037 return ERROR_FUNCTION_FAILED;
7040 if (!*flags)
7041 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
7043 return ERROR_SUCCESS;
7046 static UINT open_env_key( DWORD flags, HKEY *key )
7048 static const WCHAR user_env[] =
7049 {'E','n','v','i','r','o','n','m','e','n','t',0};
7050 static const WCHAR machine_env[] =
7051 {'S','y','s','t','e','m','\\',
7052 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
7053 'C','o','n','t','r','o','l','\\',
7054 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
7055 'E','n','v','i','r','o','n','m','e','n','t',0};
7056 const WCHAR *env;
7057 HKEY root;
7058 LONG res;
7060 if (flags & ENV_MOD_MACHINE)
7062 env = machine_env;
7063 root = HKEY_LOCAL_MACHINE;
7065 else
7067 env = user_env;
7068 root = HKEY_CURRENT_USER;
7071 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
7072 if (res != ERROR_SUCCESS)
7074 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
7075 return ERROR_FUNCTION_FAILED;
7078 return ERROR_SUCCESS;
7081 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
7083 MSIPACKAGE *package = param;
7084 LPCWSTR name, value, component;
7085 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
7086 DWORD flags, type, size, len, len_value = 0;
7087 UINT res;
7088 HKEY env = NULL;
7089 MSICOMPONENT *comp;
7090 MSIRECORD *uirow;
7091 int action = 0, found = 0;
7093 component = MSI_RecordGetString(rec, 4);
7094 comp = msi_get_loaded_component(package, component);
7095 if (!comp)
7096 return ERROR_SUCCESS;
7098 comp->Action = msi_get_component_action( package, comp );
7099 if (comp->Action != INSTALLSTATE_LOCAL)
7101 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
7102 return ERROR_SUCCESS;
7104 name = MSI_RecordGetString(rec, 2);
7105 value = MSI_RecordGetString(rec, 3);
7107 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7109 res = env_parse_flags(&name, &value, &flags);
7110 if (res != ERROR_SUCCESS || !value)
7111 goto done;
7113 if (value && !deformat_string(package, value, &deformatted))
7115 res = ERROR_OUTOFMEMORY;
7116 goto done;
7119 if ((value = deformatted))
7121 if (flags & ENV_MOD_PREFIX)
7123 p = strrchrW( value, ';' );
7124 len_value = p - value;
7126 else if (flags & ENV_MOD_APPEND)
7128 value = strchrW( value, ';' ) + 1;
7129 len_value = strlenW( value );
7131 else len_value = strlenW( value );
7134 res = open_env_key( flags, &env );
7135 if (res != ERROR_SUCCESS)
7136 goto done;
7138 if (flags & ENV_MOD_MACHINE)
7139 action |= 0x20000000;
7141 size = 0;
7142 type = REG_SZ;
7143 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
7144 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7145 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7146 goto done;
7148 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
7150 action = 0x2;
7152 /* Nothing to do. */
7153 if (!value)
7155 res = ERROR_SUCCESS;
7156 goto done;
7158 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
7159 newval = strdupW(value);
7160 if (!newval)
7162 res = ERROR_OUTOFMEMORY;
7163 goto done;
7166 else
7168 action = 0x1;
7170 /* Contrary to MSDN, +-variable to [~];path works */
7171 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7173 res = ERROR_SUCCESS;
7174 goto done;
7177 if (!(p = q = data = msi_alloc( size )))
7179 msi_free(deformatted);
7180 RegCloseKey(env);
7181 return ERROR_OUTOFMEMORY;
7184 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
7185 if (res != ERROR_SUCCESS)
7186 goto done;
7188 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7190 action = 0x4;
7191 res = RegDeleteValueW(env, name);
7192 if (res != ERROR_SUCCESS)
7193 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7194 goto done;
7197 for (;;)
7199 while (*q && *q != ';') q++;
7200 len = q - p;
7201 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
7202 (!p[len] || p[len] == ';'))
7204 found = 1;
7205 break;
7207 if (!*q) break;
7208 p = ++q;
7211 if (found)
7213 TRACE("string already set\n");
7214 goto done;
7217 size = (len_value + 1 + strlenW( data ) + 1) * sizeof(WCHAR);
7218 if (!(p = newval = msi_alloc( size )))
7220 res = ERROR_OUTOFMEMORY;
7221 goto done;
7224 if (flags & ENV_MOD_PREFIX)
7226 memcpy( newval, value, len_value * sizeof(WCHAR) );
7227 newval[len_value] = ';';
7228 p = newval + len_value + 1;
7229 action |= 0x80000000;
7232 strcpyW( p, data );
7234 if (flags & ENV_MOD_APPEND)
7236 p += strlenW( data );
7237 *p++ = ';';
7238 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
7239 action |= 0x40000000;
7242 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7243 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
7244 if (res)
7246 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7249 done:
7250 uirow = MSI_CreateRecord( 3 );
7251 MSI_RecordSetStringW( uirow, 1, name );
7252 MSI_RecordSetStringW( uirow, 2, newval );
7253 MSI_RecordSetInteger( uirow, 3, action );
7254 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7255 msiobj_release( &uirow->hdr );
7257 if (env) RegCloseKey(env);
7258 msi_free(deformatted);
7259 msi_free(data);
7260 msi_free(newval);
7261 return res;
7264 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7266 static const WCHAR query[] = {
7267 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7268 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7269 MSIQUERY *view;
7270 UINT rc;
7272 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7273 if (rc != ERROR_SUCCESS)
7274 return ERROR_SUCCESS;
7276 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7277 msiobj_release(&view->hdr);
7278 return rc;
7281 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7283 MSIPACKAGE *package = param;
7284 LPCWSTR name, value, component;
7285 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7286 DWORD flags, type, size, len, len_value = 0, len_new_value;
7287 HKEY env;
7288 MSICOMPONENT *comp;
7289 MSIRECORD *uirow;
7290 int action = 0;
7291 LONG res;
7292 UINT r;
7294 component = MSI_RecordGetString( rec, 4 );
7295 comp = msi_get_loaded_component( package, component );
7296 if (!comp)
7297 return ERROR_SUCCESS;
7299 comp->Action = msi_get_component_action( package, comp );
7300 if (comp->Action != INSTALLSTATE_ABSENT)
7302 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7303 return ERROR_SUCCESS;
7305 name = MSI_RecordGetString( rec, 2 );
7306 value = MSI_RecordGetString( rec, 3 );
7308 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7310 r = env_parse_flags( &name, &value, &flags );
7311 if (r != ERROR_SUCCESS)
7312 return r;
7314 if (!(flags & ENV_ACT_REMOVE))
7316 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7317 return ERROR_SUCCESS;
7320 if (value && !deformat_string( package, value, &deformatted ))
7321 return ERROR_OUTOFMEMORY;
7323 if ((value = deformatted))
7325 if (flags & ENV_MOD_PREFIX)
7327 p = strchrW( value, ';' );
7328 len_value = p - value;
7330 else if (flags & ENV_MOD_APPEND)
7332 value = strchrW( value, ';' ) + 1;
7333 len_value = strlenW( value );
7335 else len_value = strlenW( value );
7338 r = open_env_key( flags, &env );
7339 if (r != ERROR_SUCCESS)
7341 r = ERROR_SUCCESS;
7342 goto done;
7345 if (flags & ENV_MOD_MACHINE)
7346 action |= 0x20000000;
7348 size = 0;
7349 type = REG_SZ;
7350 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7351 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7352 goto done;
7354 if (!(new_value = msi_alloc( size ))) goto done;
7356 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7357 if (res != ERROR_SUCCESS)
7358 goto done;
7360 len_new_value = size / sizeof(WCHAR) - 1;
7361 p = q = new_value;
7362 for (;;)
7364 while (*q && *q != ';') q++;
7365 len = q - p;
7366 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7368 if (*q == ';') q++;
7369 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7370 break;
7372 if (!*q) break;
7373 p = ++q;
7376 if (!new_value[0] || !value)
7378 TRACE("removing %s\n", debugstr_w(name));
7379 res = RegDeleteValueW( env, name );
7380 if (res != ERROR_SUCCESS)
7381 WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
7383 else
7385 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7386 size = (strlenW( new_value ) + 1) * sizeof(WCHAR);
7387 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7388 if (res != ERROR_SUCCESS)
7389 WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7392 done:
7393 uirow = MSI_CreateRecord( 3 );
7394 MSI_RecordSetStringW( uirow, 1, name );
7395 MSI_RecordSetStringW( uirow, 2, value );
7396 MSI_RecordSetInteger( uirow, 3, action );
7397 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7398 msiobj_release( &uirow->hdr );
7400 if (env) RegCloseKey( env );
7401 msi_free( deformatted );
7402 msi_free( new_value );
7403 return r;
7406 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7408 static const WCHAR query[] = {
7409 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7410 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7411 MSIQUERY *view;
7412 UINT rc;
7414 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7415 if (rc != ERROR_SUCCESS)
7416 return ERROR_SUCCESS;
7418 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7419 msiobj_release( &view->hdr );
7420 return rc;
7423 UINT msi_validate_product_id( MSIPACKAGE *package )
7425 LPWSTR key, template, id;
7426 UINT r = ERROR_SUCCESS;
7428 id = msi_dup_property( package->db, szProductID );
7429 if (id)
7431 msi_free( id );
7432 return ERROR_SUCCESS;
7434 template = msi_dup_property( package->db, szPIDTemplate );
7435 key = msi_dup_property( package->db, szPIDKEY );
7436 if (key && template)
7438 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7439 r = msi_set_property( package->db, szProductID, key, -1 );
7441 msi_free( template );
7442 msi_free( key );
7443 return r;
7446 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7448 return msi_validate_product_id( package );
7451 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7453 TRACE("\n");
7454 package->need_reboot_at_end = 1;
7455 return ERROR_SUCCESS;
7458 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7460 static const WCHAR szAvailableFreeReg[] =
7461 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7462 MSIRECORD *uirow;
7463 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7465 TRACE("%p %d kilobytes\n", package, space);
7467 uirow = MSI_CreateRecord( 1 );
7468 MSI_RecordSetInteger( uirow, 1, space );
7469 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7470 msiobj_release( &uirow->hdr );
7472 return ERROR_SUCCESS;
7475 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7477 TRACE("%p\n", package);
7479 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7480 return ERROR_SUCCESS;
7483 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7485 FIXME("%p\n", package);
7486 return ERROR_SUCCESS;
7489 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7491 static const WCHAR driver_query[] = {
7492 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7493 'O','D','B','C','D','r','i','v','e','r',0};
7494 static const WCHAR translator_query[] = {
7495 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7496 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7497 MSIQUERY *view;
7498 UINT r, count;
7500 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7501 if (r == ERROR_SUCCESS)
7503 count = 0;
7504 r = MSI_IterateRecords( view, &count, NULL, package );
7505 msiobj_release( &view->hdr );
7506 if (r != ERROR_SUCCESS)
7507 return r;
7508 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7510 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7511 if (r == ERROR_SUCCESS)
7513 count = 0;
7514 r = MSI_IterateRecords( view, &count, NULL, package );
7515 msiobj_release( &view->hdr );
7516 if (r != ERROR_SUCCESS)
7517 return r;
7518 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7520 return ERROR_SUCCESS;
7523 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7525 static const WCHAR fmtW[] =
7526 {'m','s','i','e','x','e','c',' ','/','q','n',' ','/','i',' ','%','s',' ',
7527 'R','E','M','O','V','E','=','%','s',0};
7528 MSIPACKAGE *package = param;
7529 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7530 int attrs = MSI_RecordGetInteger( rec, 5 );
7531 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7532 WCHAR *product, *features, *cmd;
7533 STARTUPINFOW si;
7534 PROCESS_INFORMATION info;
7535 BOOL ret;
7537 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7538 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7540 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7542 len += strlenW( product );
7543 if (features)
7544 len += strlenW( features );
7545 else
7546 len += sizeof(szAll) / sizeof(szAll[0]);
7548 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7550 msi_free( product );
7551 msi_free( features );
7552 return ERROR_OUTOFMEMORY;
7554 sprintfW( cmd, fmtW, product, features ? features : szAll );
7555 msi_free( product );
7556 msi_free( features );
7558 memset( &si, 0, sizeof(STARTUPINFOW) );
7559 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7560 msi_free( cmd );
7561 if (!ret) return GetLastError();
7562 CloseHandle( info.hThread );
7564 WaitForSingleObject( info.hProcess, INFINITE );
7565 CloseHandle( info.hProcess );
7566 return ERROR_SUCCESS;
7569 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7571 static const WCHAR query[] = {
7572 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7573 MSIQUERY *view;
7574 UINT r;
7576 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7577 if (r == ERROR_SUCCESS)
7579 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7580 msiobj_release( &view->hdr );
7581 if (r != ERROR_SUCCESS)
7582 return r;
7584 return ERROR_SUCCESS;
7587 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7589 MSIPACKAGE *package = param;
7590 int attributes = MSI_RecordGetInteger( rec, 5 );
7592 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7594 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7595 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7596 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7597 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7598 HKEY hkey;
7599 UINT r;
7601 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7603 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7604 if (r != ERROR_SUCCESS)
7605 return ERROR_SUCCESS;
7607 else
7609 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7610 if (r != ERROR_SUCCESS)
7611 return ERROR_SUCCESS;
7613 RegCloseKey( hkey );
7615 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7616 debugstr_w(upgrade_code), debugstr_w(version_min),
7617 debugstr_w(version_max), debugstr_w(language));
7619 return ERROR_SUCCESS;
7622 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7624 static const WCHAR query[] = {
7625 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7626 'U','p','g','r','a','d','e',0};
7627 MSIQUERY *view;
7628 UINT r;
7630 if (msi_get_property_int( package->db, szInstalled, 0 ))
7632 TRACE("product is installed, skipping action\n");
7633 return ERROR_SUCCESS;
7635 if (msi_get_property_int( package->db, szPreselected, 0 ))
7637 TRACE("Preselected property is set, not migrating feature states\n");
7638 return ERROR_SUCCESS;
7640 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7641 if (r == ERROR_SUCCESS)
7643 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7644 msiobj_release( &view->hdr );
7645 if (r != ERROR_SUCCESS)
7646 return r;
7648 return ERROR_SUCCESS;
7651 static void bind_image( const char *filename, const char *path )
7653 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7655 WARN("failed to bind image %u\n", GetLastError());
7659 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7661 UINT i;
7662 MSIFILE *file;
7663 MSIPACKAGE *package = param;
7664 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7665 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7666 char *filenameA, *pathA;
7667 WCHAR *pathW, **path_list;
7669 if (!(file = msi_get_loaded_file( package, key )))
7671 WARN("file %s not found\n", debugstr_w(key));
7672 return ERROR_SUCCESS;
7674 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7675 path_list = msi_split_string( paths, ';' );
7676 if (!path_list) bind_image( filenameA, NULL );
7677 else
7679 for (i = 0; path_list[i] && path_list[i][0]; i++)
7681 deformat_string( package, path_list[i], &pathW );
7682 if ((pathA = strdupWtoA( pathW )))
7684 bind_image( filenameA, pathA );
7685 msi_free( pathA );
7687 msi_free( pathW );
7690 msi_free( path_list );
7691 msi_free( filenameA );
7692 return ERROR_SUCCESS;
7695 static UINT ACTION_BindImage( MSIPACKAGE *package )
7697 static const WCHAR query[] = {
7698 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7699 'B','i','n','d','I','m','a','g','e',0};
7700 MSIQUERY *view;
7701 UINT r;
7703 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7704 if (r == ERROR_SUCCESS)
7706 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7707 msiobj_release( &view->hdr );
7708 if (r != ERROR_SUCCESS)
7709 return r;
7711 return ERROR_SUCCESS;
7714 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7716 static const WCHAR query[] = {
7717 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7718 MSIQUERY *view;
7719 DWORD count = 0;
7720 UINT r;
7722 r = MSI_OpenQuery( package->db, &view, query, table );
7723 if (r == ERROR_SUCCESS)
7725 r = MSI_IterateRecords(view, &count, NULL, package);
7726 msiobj_release(&view->hdr);
7727 if (r != ERROR_SUCCESS)
7728 return r;
7730 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7731 return ERROR_SUCCESS;
7734 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7736 static const WCHAR table[] = {
7737 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7738 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7741 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7743 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7744 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7747 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7749 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7750 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7753 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7755 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7756 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7759 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7761 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7762 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7765 static const struct
7767 const WCHAR *action;
7768 const UINT description;
7769 const UINT template;
7770 UINT (*handler)(MSIPACKAGE *);
7771 const WCHAR *action_rollback;
7773 StandardActions[] =
7775 { szAllocateRegistrySpace, IDS_DESC_ALLOCATEREGISTRYSPACE, IDS_TEMP_ALLOCATEREGISTRYSPACE, ACTION_AllocateRegistrySpace, NULL },
7776 { szAppSearch, IDS_DESC_APPSEARCH, IDS_TEMP_APPSEARCH, ACTION_AppSearch, NULL },
7777 { szBindImage, IDS_DESC_BINDIMAGE, IDS_TEMP_BINDIMAGE, ACTION_BindImage, NULL },
7778 { szCCPSearch, IDS_DESC_CCPSEARCH, 0, ACTION_CCPSearch, NULL },
7779 { szCostFinalize, IDS_DESC_COSTFINALIZE, 0, ACTION_CostFinalize, NULL },
7780 { szCostInitialize, IDS_DESC_COSTINITIALIZE, 0, ACTION_CostInitialize, NULL },
7781 { szCreateFolders, IDS_DESC_CREATEFOLDERS, IDS_TEMP_CREATEFOLDERS, ACTION_CreateFolders, szRemoveFolders },
7782 { szCreateShortcuts, IDS_DESC_CREATESHORTCUTS, IDS_TEMP_CREATESHORTCUTS, ACTION_CreateShortcuts, szRemoveShortcuts },
7783 { szDeleteServices, IDS_DESC_DELETESERVICES, IDS_TEMP_DELETESERVICES, ACTION_DeleteServices, szInstallServices },
7784 { szDisableRollback, 0, 0, ACTION_DisableRollback, NULL },
7785 { szDuplicateFiles, IDS_DESC_DUPLICATEFILES, IDS_TEMP_DUPLICATEFILES, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7786 { szExecuteAction, 0, 0, ACTION_ExecuteAction, NULL },
7787 { szFileCost, IDS_DESC_FILECOST, 0, ACTION_FileCost, NULL },
7788 { szFindRelatedProducts, IDS_DESC_FINDRELATEDPRODUCTS, IDS_TEMP_FINDRELATEDPRODUCTS, ACTION_FindRelatedProducts, NULL },
7789 { szForceReboot, 0, 0, ACTION_ForceReboot, NULL },
7790 { szInstallAdminPackage, IDS_DESC_INSTALLADMINPACKAGE, IDS_TEMP_INSTALLADMINPACKAGE, ACTION_InstallAdminPackage, NULL },
7791 { szInstallExecute, 0, 0, ACTION_InstallExecute, NULL },
7792 { szInstallExecuteAgain, 0, 0, ACTION_InstallExecute, NULL },
7793 { szInstallFiles, IDS_DESC_INSTALLFILES, IDS_TEMP_INSTALLFILES, ACTION_InstallFiles, szRemoveFiles },
7794 { szInstallFinalize, 0, 0, ACTION_InstallFinalize, NULL },
7795 { szInstallInitialize, 0, 0, ACTION_InstallInitialize, NULL },
7796 { szInstallODBC, IDS_DESC_INSTALLODBC, 0, ACTION_InstallODBC, szRemoveODBC },
7797 { szInstallServices, IDS_DESC_INSTALLSERVICES, IDS_TEMP_INSTALLSERVICES, ACTION_InstallServices, szDeleteServices },
7798 { szInstallSFPCatalogFile, IDS_DESC_INSTALLSFPCATALOGFILE, IDS_TEMP_INSTALLSFPCATALOGFILE, ACTION_InstallSFPCatalogFile, NULL },
7799 { szInstallValidate, IDS_DESC_INSTALLVALIDATE, 0, ACTION_InstallValidate, NULL },
7800 { szIsolateComponents, 0, 0, ACTION_IsolateComponents, NULL },
7801 { szLaunchConditions, IDS_DESC_LAUNCHCONDITIONS, 0, ACTION_LaunchConditions, NULL },
7802 { szMigrateFeatureStates, IDS_DESC_MIGRATEFEATURESTATES, IDS_TEMP_MIGRATEFEATURESTATES, ACTION_MigrateFeatureStates, NULL },
7803 { szMoveFiles, IDS_DESC_MOVEFILES, IDS_TEMP_MOVEFILES, ACTION_MoveFiles, NULL },
7804 { szMsiPublishAssemblies, IDS_DESC_MSIPUBLISHASSEMBLIES, IDS_TEMP_MSIPUBLISHASSEMBLIES, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7805 { szMsiUnpublishAssemblies, IDS_DESC_MSIUNPUBLISHASSEMBLIES, IDS_TEMP_MSIUNPUBLISHASSEMBLIES, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7806 { szPatchFiles, IDS_DESC_PATCHFILES, IDS_TEMP_PATCHFILES, ACTION_PatchFiles, NULL },
7807 { szProcessComponents, IDS_DESC_PROCESSCOMPONENTS, 0, ACTION_ProcessComponents, szProcessComponents },
7808 { szPublishComponents, IDS_DESC_PUBLISHCOMPONENTS, IDS_TEMP_PUBLISHCOMPONENTS, ACTION_PublishComponents, szUnpublishComponents },
7809 { szPublishFeatures, IDS_DESC_PUBLISHFEATURES, IDS_TEMP_PUBLISHFEATURES, ACTION_PublishFeatures, szUnpublishFeatures },
7810 { szPublishProduct, IDS_DESC_PUBLISHPRODUCT, 0, ACTION_PublishProduct, szUnpublishProduct },
7811 { szRegisterClassInfo, IDS_DESC_REGISTERCLASSINFO, IDS_TEMP_REGISTERCLASSINFO, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7812 { szRegisterComPlus, IDS_DESC_REGISTERCOMPLUS, IDS_TEMP_REGISTERCOMPLUS, ACTION_RegisterComPlus, szUnregisterComPlus },
7813 { szRegisterExtensionInfo, IDS_DESC_REGISTEREXTENSIONINFO, 0, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7814 { szRegisterFonts, IDS_DESC_REGISTERFONTS, IDS_TEMP_REGISTERFONTS, ACTION_RegisterFonts, szUnregisterFonts },
7815 { szRegisterMIMEInfo, IDS_DESC_REGISTERMIMEINFO, IDS_TEMP_REGISTERMIMEINFO, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7816 { szRegisterProduct, IDS_DESC_REGISTERPRODUCT, 0, ACTION_RegisterProduct, NULL },
7817 { szRegisterProgIdInfo, IDS_DESC_REGISTERPROGIDINFO, IDS_TEMP_REGISTERPROGIDINFO, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7818 { szRegisterTypeLibraries, IDS_DESC_REGISTERTYPELIBRARIES, IDS_TEMP_REGISTERTYPELIBRARIES, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7819 { szRegisterUser, IDS_DESC_REGISTERUSER, 0, ACTION_RegisterUser, NULL },
7820 { szRemoveDuplicateFiles, IDS_DESC_REMOVEDUPLICATEFILES, IDS_TEMP_REMOVEDUPLICATEFILES, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7821 { szRemoveEnvironmentStrings, IDS_DESC_REMOVEENVIRONMENTSTRINGS, IDS_TEMP_REMOVEENVIRONMENTSTRINGS, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7822 { szRemoveExistingProducts, IDS_DESC_REMOVEEXISTINGPRODUCTS, IDS_TEMP_REMOVEEXISTINGPRODUCTS, ACTION_RemoveExistingProducts, NULL },
7823 { szRemoveFiles, IDS_DESC_REMOVEFILES, IDS_TEMP_REMOVEFILES, ACTION_RemoveFiles, szInstallFiles },
7824 { szRemoveFolders, IDS_DESC_REMOVEFOLDERS, IDS_TEMP_REMOVEFOLDERS, ACTION_RemoveFolders, szCreateFolders },
7825 { szRemoveIniValues, IDS_DESC_REMOVEINIVALUES, IDS_TEMP_REMOVEINIVALUES, ACTION_RemoveIniValues, szWriteIniValues },
7826 { szRemoveODBC, IDS_DESC_REMOVEODBC, 0, ACTION_RemoveODBC, szInstallODBC },
7827 { szRemoveRegistryValues, IDS_DESC_REMOVEREGISTRYVALUES, IDS_TEMP_REMOVEREGISTRYVALUES, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7828 { szRemoveShortcuts, IDS_DESC_REMOVESHORTCUTS, IDS_TEMP_REMOVESHORTCUTS, ACTION_RemoveShortcuts, szCreateShortcuts },
7829 { szResolveSource, 0, 0, ACTION_ResolveSource, NULL },
7830 { szRMCCPSearch, IDS_DESC_RMCCPSEARCH, 0, ACTION_RMCCPSearch, NULL },
7831 { szScheduleReboot, 0, 0, ACTION_ScheduleReboot, NULL },
7832 { szSelfRegModules, IDS_DESC_SELFREGMODULES, IDS_TEMP_SELFREGMODULES, ACTION_SelfRegModules, szSelfUnregModules },
7833 { szSelfUnregModules, IDS_DESC_SELFUNREGMODULES, IDS_TEMP_SELFUNREGMODULES, ACTION_SelfUnregModules, szSelfRegModules },
7834 { szSetODBCFolders, IDS_DESC_SETODBCFOLDERS, 0, ACTION_SetODBCFolders, NULL },
7835 { szStartServices, IDS_DESC_STARTSERVICES, IDS_TEMP_STARTSERVICES, ACTION_StartServices, szStopServices },
7836 { szStopServices, IDS_DESC_STOPSERVICES, IDS_TEMP_STOPSERVICES, ACTION_StopServices, szStartServices },
7837 { szUnpublishComponents, IDS_DESC_UNPUBLISHCOMPONENTS, IDS_TEMP_UNPUBLISHCOMPONENTS, ACTION_UnpublishComponents, szPublishComponents },
7838 { szUnpublishFeatures, IDS_DESC_UNPUBLISHFEATURES, IDS_TEMP_UNPUBLISHFEATURES, ACTION_UnpublishFeatures, szPublishFeatures },
7839 { szUnpublishProduct, IDS_DESC_UNPUBLISHPRODUCT, 0, ACTION_UnpublishProduct, NULL }, /* for rollback only */
7840 { szUnregisterClassInfo, IDS_DESC_UNREGISTERCLASSINFO, IDS_TEMP_UNREGISTERCLASSINFO, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7841 { szUnregisterComPlus, IDS_DESC_UNREGISTERCOMPLUS, IDS_TEMP_UNREGISTERCOMPLUS, ACTION_UnregisterComPlus, szRegisterComPlus },
7842 { szUnregisterExtensionInfo, IDS_DESC_UNREGISTEREXTENSIONINFO, IDS_TEMP_UNREGISTEREXTENSIONINFO, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7843 { szUnregisterFonts, IDS_DESC_UNREGISTERFONTS, IDS_TEMP_UNREGISTERFONTS, ACTION_UnregisterFonts, szRegisterFonts },
7844 { szUnregisterMIMEInfo, IDS_DESC_UNREGISTERMIMEINFO, IDS_TEMP_UNREGISTERMIMEINFO, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7845 { szUnregisterProgIdInfo, IDS_DESC_UNREGISTERPROGIDINFO, IDS_TEMP_UNREGISTERPROGIDINFO, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7846 { szUnregisterTypeLibraries, IDS_DESC_UNREGISTERTYPELIBRARIES, IDS_TEMP_UNREGISTERTYPELIBRARIES, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7847 { szValidateProductID, 0, 0, ACTION_ValidateProductID, NULL },
7848 { szWriteEnvironmentStrings, IDS_DESC_WRITEENVIRONMENTSTRINGS, IDS_TEMP_WRITEENVIRONMENTSTRINGS, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7849 { szWriteIniValues, IDS_DESC_WRITEINIVALUES, IDS_TEMP_WRITEINIVALUES, ACTION_WriteIniValues, szRemoveIniValues },
7850 { szWriteRegistryValues, IDS_DESC_WRITEREGISTRYVALUES, IDS_TEMP_WRITEREGISTRYVALUES, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7851 { szINSTALL, 0, 0, ACTION_INSTALL, NULL },
7852 { 0 }
7855 static UINT ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action)
7857 UINT rc = ERROR_FUNCTION_NOT_CALLED;
7858 UINT i;
7860 i = 0;
7861 while (StandardActions[i].action != NULL)
7863 if (!strcmpW( StandardActions[i].action, action ))
7865 WCHAR description[100] = {0}, template[100] = {0};
7867 if (StandardActions[i].description != 0)
7868 LoadStringW(msi_hInstance, StandardActions[i].description, (LPWSTR)&description, 100);
7869 if (StandardActions[i].template != 0)
7870 LoadStringW(msi_hInstance, StandardActions[i].template, (LPWSTR)&template, 100);
7872 ui_actionstart(package, action, description, template);
7873 if (StandardActions[i].handler)
7875 ui_actioninfo( package, action, TRUE, 0 );
7876 rc = StandardActions[i].handler( package );
7877 ui_actioninfo( package, action, FALSE, !rc );
7879 if (StandardActions[i].action_rollback && !package->need_rollback)
7881 TRACE("scheduling rollback action\n");
7882 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7885 else
7887 FIXME("unhandled standard action %s\n", debugstr_w(action));
7888 rc = ERROR_SUCCESS;
7890 break;
7892 i++;
7894 return rc;
7897 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action)
7899 UINT rc;
7901 TRACE("Performing action (%s)\n", debugstr_w(action));
7903 package->action_progress_increment = 0;
7904 rc = ACTION_HandleStandardAction(package, action);
7906 if (rc == ERROR_FUNCTION_NOT_CALLED)
7907 rc = ACTION_HandleCustomAction(package, action);
7909 if (rc == ERROR_FUNCTION_NOT_CALLED)
7910 WARN("unhandled msi action %s\n", debugstr_w(action));
7912 return rc;
7915 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7917 UINT rc = ERROR_SUCCESS;
7918 MSIRECORD *row;
7920 static const WCHAR query[] =
7921 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7922 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7923 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7924 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7925 static const WCHAR ui_query[] =
7926 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7927 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7928 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7929 ' ', '=',' ','%','i',0};
7931 if (needs_ui_sequence(package))
7932 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7933 else
7934 row = MSI_QueryGetRecord(package->db, query, seq);
7936 if (row)
7938 LPCWSTR action, cond;
7940 TRACE("Running the actions\n");
7942 /* check conditions */
7943 cond = MSI_RecordGetString(row, 2);
7945 /* this is a hack to skip errors in the condition code */
7946 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7948 msiobj_release(&row->hdr);
7949 return ERROR_SUCCESS;
7952 action = MSI_RecordGetString(row, 1);
7953 if (!action)
7955 ERR("failed to fetch action\n");
7956 msiobj_release(&row->hdr);
7957 return ERROR_FUNCTION_FAILED;
7960 rc = ACTION_PerformAction(package, action);
7962 msiobj_release(&row->hdr);
7965 return rc;
7968 /****************************************************
7969 * TOP level entry points
7970 *****************************************************/
7972 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7973 LPCWSTR szCommandLine )
7975 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7976 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7977 WCHAR *reinstall, *remove, *patch, *productcode, *action;
7978 UINT rc;
7979 DWORD len = 0;
7981 if (szPackagePath)
7983 LPWSTR p, dir;
7984 LPCWSTR file;
7986 dir = strdupW(szPackagePath);
7987 p = strrchrW(dir, '\\');
7988 if (p)
7990 *(++p) = 0;
7991 file = szPackagePath + (p - dir);
7993 else
7995 msi_free(dir);
7996 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7997 GetCurrentDirectoryW(MAX_PATH, dir);
7998 lstrcatW(dir, szBackSlash);
7999 file = szPackagePath;
8002 msi_free( package->PackagePath );
8003 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
8004 if (!package->PackagePath)
8006 msi_free(dir);
8007 return ERROR_OUTOFMEMORY;
8010 lstrcpyW(package->PackagePath, dir);
8011 lstrcatW(package->PackagePath, file);
8012 msi_free(dir);
8014 msi_set_sourcedir_props(package, FALSE);
8017 rc = msi_parse_command_line( package, szCommandLine, FALSE );
8018 if (rc != ERROR_SUCCESS)
8019 return rc;
8021 msi_apply_transforms( package );
8022 msi_apply_patches( package );
8024 patch = msi_dup_property( package->db, szPatch );
8025 remove = msi_dup_property( package->db, szRemove );
8026 reinstall = msi_dup_property( package->db, szReinstall );
8027 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
8029 TRACE("setting REINSTALL property to ALL\n");
8030 msi_set_property( package->db, szReinstall, szAll, -1 );
8031 package->full_reinstall = 1;
8033 if (msi_get_property( package->db, szAction, NULL, &len ))
8034 msi_set_property( package->db, szAction, szINSTALL, -1 );
8035 action = msi_dup_property( package->db, szAction );
8036 CharUpperW(action);
8038 msi_set_original_database_property( package->db, szPackagePath );
8039 msi_parse_command_line( package, szCommandLine, FALSE );
8040 msi_adjust_privilege_properties( package );
8041 msi_set_context( package );
8043 productcode = msi_dup_property( package->db, szProductCode );
8044 if (strcmpiW( productcode, package->ProductCode ))
8046 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
8047 msi_free( package->ProductCode );
8048 package->ProductCode = productcode;
8050 else msi_free( productcode );
8052 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
8054 TRACE("disabling rollback\n");
8055 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
8058 rc = ACTION_PerformAction(package, action);
8060 /* process the ending type action */
8061 if (rc == ERROR_SUCCESS)
8062 ACTION_PerformActionSequence(package, -1);
8063 else if (rc == ERROR_INSTALL_USEREXIT)
8064 ACTION_PerformActionSequence(package, -2);
8065 else if (rc == ERROR_INSTALL_SUSPEND)
8066 ACTION_PerformActionSequence(package, -4);
8067 else /* failed */
8069 ACTION_PerformActionSequence(package, -3);
8070 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
8072 package->need_rollback = TRUE;
8076 /* finish up running custom actions */
8077 ACTION_FinishCustomActions(package);
8079 if (package->need_rollback && !reinstall)
8081 WARN("installation failed, running rollback script\n");
8082 execute_script( package, SCRIPT_ROLLBACK );
8084 msi_free( reinstall );
8085 msi_free( remove );
8086 msi_free( patch );
8087 msi_free( action );
8089 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
8090 return ERROR_SUCCESS_REBOOT_REQUIRED;
8092 return rc;