msi: Add support for ProductToBeRegistered property.
[wine.git] / dlls / msi / action.c
blob74917afdd1fe83d2c5434dd25662eeba1e584cf7
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)
200 msi_free(template);
201 return;
203 MSI_RecordSetStringW(row, 0, template);
204 MSI_RecordSetStringW(row, 1, action);
205 MSI_RecordSetInteger(row, 2, start ? package->LastActionResult : rc);
206 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
207 msiobj_release(&row->hdr);
208 msi_free(template);
209 if (!start) package->LastActionResult = rc;
212 enum parse_state
214 state_whitespace,
215 state_token,
216 state_quote
219 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
221 enum parse_state state = state_quote;
222 const WCHAR *p;
223 WCHAR *out = value;
224 BOOL ignore, in_quotes = FALSE;
225 int count = 0, len = 0;
227 for (p = str; *p; p++)
229 ignore = FALSE;
230 switch (state)
232 case state_whitespace:
233 switch (*p)
235 case ' ':
236 in_quotes = TRUE;
237 ignore = TRUE;
238 len++;
239 break;
240 case '"':
241 state = state_quote;
242 if (in_quotes && p[1] != '\"') count--;
243 else count++;
244 break;
245 default:
246 state = state_token;
247 in_quotes = TRUE;
248 len++;
249 break;
251 break;
253 case state_token:
254 switch (*p)
256 case '"':
257 state = state_quote;
258 if (in_quotes) count--;
259 else count++;
260 break;
261 case ' ':
262 state = state_whitespace;
263 if (!count) goto done;
264 in_quotes = TRUE;
265 len++;
266 break;
267 default:
268 if (count) in_quotes = TRUE;
269 len++;
270 break;
272 break;
274 case state_quote:
275 switch (*p)
277 case '"':
278 if (in_quotes && p[1] != '\"') count--;
279 else count++;
280 break;
281 case ' ':
282 state = state_whitespace;
283 if (!count || (count > 1 && !len)) goto done;
284 in_quotes = TRUE;
285 len++;
286 break;
287 default:
288 state = state_token;
289 if (count) in_quotes = TRUE;
290 len++;
291 break;
293 break;
295 default: break;
297 if (!ignore) *out++ = *p;
298 if (!count) in_quotes = FALSE;
301 done:
302 if (!len) *value = 0;
303 else *out = 0;
305 *quotes = count;
306 return p - str;
309 static void remove_quotes( WCHAR *str )
311 WCHAR *p = str;
312 int len = strlenW( str );
314 while ((p = strchrW( p, '"' )))
316 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
317 p++;
321 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
322 BOOL preserve_case )
324 LPCWSTR ptr, ptr2;
325 int num_quotes;
326 DWORD len;
327 WCHAR *prop, *val;
328 UINT r;
330 if (!szCommandLine)
331 return ERROR_SUCCESS;
333 ptr = szCommandLine;
334 while (*ptr)
336 while (*ptr == ' ') ptr++;
337 if (!*ptr) break;
339 ptr2 = strchrW( ptr, '=' );
340 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
342 len = ptr2 - ptr;
343 if (!len) return ERROR_INVALID_COMMAND_LINE;
345 while (ptr[len - 1] == ' ') len--;
347 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
348 memcpy( prop, ptr, len * sizeof(WCHAR) );
349 prop[len] = 0;
350 if (!preserve_case) struprW( prop );
352 ptr2++;
353 while (*ptr2 == ' ') ptr2++;
355 num_quotes = 0;
356 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
357 len = parse_prop( ptr2, val, &num_quotes );
358 if (num_quotes % 2)
360 WARN("unbalanced quotes\n");
361 msi_free( val );
362 msi_free( prop );
363 return ERROR_INVALID_COMMAND_LINE;
365 remove_quotes( val );
366 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
368 r = msi_set_property( package->db, prop, val, -1 );
369 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
370 msi_reset_folders( package, TRUE );
372 msi_free( val );
373 msi_free( prop );
375 ptr = ptr2 + len;
378 return ERROR_SUCCESS;
381 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
383 LPCWSTR pc;
384 LPWSTR p, *ret = NULL;
385 UINT count = 0;
387 if (!str)
388 return ret;
390 /* count the number of substrings */
391 for ( pc = str, count = 0; pc; count++ )
393 pc = strchrW( pc, sep );
394 if (pc)
395 pc++;
398 /* allocate space for an array of substring pointers and the substrings */
399 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
400 (lstrlenW(str)+1) * sizeof(WCHAR) );
401 if (!ret)
402 return ret;
404 /* copy the string and set the pointers */
405 p = (LPWSTR) &ret[count+1];
406 lstrcpyW( p, str );
407 for( count = 0; (ret[count] = p); count++ )
409 p = strchrW( p, sep );
410 if (p)
411 *p++ = 0;
414 return ret;
417 static BOOL ui_sequence_exists( MSIPACKAGE *package )
419 static const WCHAR query [] = {
420 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
421 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
422 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
423 MSIQUERY *view;
424 DWORD count = 0;
426 if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
428 MSI_IterateRecords( view, &count, NULL, package );
429 msiobj_release( &view->hdr );
431 return count != 0;
434 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
436 WCHAR *source, *check, *p, *db;
437 DWORD len;
439 if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
440 return ERROR_OUTOFMEMORY;
442 if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' )))
444 msi_free(db);
445 return ERROR_SUCCESS;
447 len = p - db + 2;
448 source = msi_alloc( len * sizeof(WCHAR) );
449 lstrcpynW( source, db, len );
450 msi_free( db );
452 check = msi_dup_property( package->db, szSourceDir );
453 if (!check || replace)
455 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
456 if (r == ERROR_SUCCESS)
457 msi_reset_folders( package, TRUE );
459 msi_free( check );
461 check = msi_dup_property( package->db, szSOURCEDIR );
462 if (!check || replace)
463 msi_set_property( package->db, szSOURCEDIR, source, -1 );
465 msi_free( check );
466 msi_free( source );
468 return ERROR_SUCCESS;
471 static BOOL needs_ui_sequence(MSIPACKAGE *package)
473 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
476 UINT msi_set_context(MSIPACKAGE *package)
478 UINT r = msi_locate_product( package->ProductCode, &package->Context );
479 if (r != ERROR_SUCCESS)
481 int num = msi_get_property_int( package->db, szAllUsers, 0 );
482 if (num == 1 || num == 2)
483 package->Context = MSIINSTALLCONTEXT_MACHINE;
484 else
485 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
487 return ERROR_SUCCESS;
490 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
492 UINT rc;
493 LPCWSTR cond, action;
494 MSIPACKAGE *package = param;
496 action = MSI_RecordGetString(row,1);
497 if (!action)
499 ERR("Error is retrieving action name\n");
500 return ERROR_FUNCTION_FAILED;
503 /* check conditions */
504 cond = MSI_RecordGetString(row,2);
506 /* this is a hack to skip errors in the condition code */
507 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
509 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
510 return ERROR_SUCCESS;
513 rc = ACTION_PerformAction(package, action);
515 msi_dialog_check_messages( NULL );
517 if (rc == ERROR_FUNCTION_NOT_CALLED)
518 rc = ERROR_SUCCESS;
520 if (rc != ERROR_SUCCESS)
521 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
523 if (package->need_reboot_now)
525 TRACE("action %s asked for immediate reboot, suspending installation\n",
526 debugstr_w(action));
527 rc = ACTION_ForceReboot( package );
529 return rc;
532 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
534 static const WCHAR query[] = {
535 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
536 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
537 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
538 '`','S','e','q','u','e','n','c','e','`',0};
539 MSIQUERY *view;
540 UINT r;
542 TRACE("%p %s\n", package, debugstr_w(table));
544 r = MSI_OpenQuery( package->db, &view, query, table );
545 if (r == ERROR_SUCCESS)
547 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
548 msiobj_release(&view->hdr);
550 return r;
553 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package)
555 static const WCHAR query[] = {
556 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
557 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
558 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
559 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','0',' ',
560 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
561 MSIQUERY *view;
562 UINT rc;
564 if (package->ExecuteSequenceRun)
566 TRACE("Execute Sequence already Run\n");
567 return ERROR_SUCCESS;
570 package->ExecuteSequenceRun = TRUE;
572 rc = MSI_OpenQuery(package->db, &view, query);
573 if (rc == ERROR_SUCCESS)
575 TRACE("Running the actions\n");
577 msi_set_property( package->db, szSourceDir, NULL, -1 );
578 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
579 msiobj_release(&view->hdr);
581 return rc;
584 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
586 static const WCHAR query[] = {
587 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
588 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
589 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
590 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
591 MSIQUERY *view;
592 UINT rc;
594 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
595 if (rc == ERROR_SUCCESS)
597 TRACE("Running the actions\n");
598 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
599 msiobj_release(&view->hdr);
601 return rc;
604 /********************************************************
605 * ACTION helper functions and functions that perform the actions
606 *******************************************************/
607 static UINT ACTION_HandleCustomAction(MSIPACKAGE *package, LPCWSTR action)
609 UINT arc;
610 INT uirc;
612 uirc = ui_actionstart(package, action, NULL, NULL);
613 if (uirc == IDCANCEL)
614 return ERROR_INSTALL_USEREXIT;
615 ui_actioninfo(package, action, TRUE, 0);
616 arc = ACTION_CustomAction(package, action);
617 uirc = !arc;
619 if (arc == ERROR_FUNCTION_NOT_CALLED && needs_ui_sequence(package))
621 uirc = ACTION_ShowDialog(package, action);
622 switch (uirc)
624 case -1:
625 return ERROR_SUCCESS; /* stop immediately */
626 case 0: arc = ERROR_FUNCTION_NOT_CALLED; break;
627 case 1: arc = ERROR_SUCCESS; break;
628 case 2: arc = ERROR_INSTALL_USEREXIT; break;
629 case 3: arc = ERROR_INSTALL_FAILURE; break;
630 case 4: arc = ERROR_INSTALL_SUSPEND; break;
631 case 5: arc = ERROR_MORE_DATA; break;
632 case 6: arc = ERROR_INVALID_HANDLE_STATE; break;
633 case 7: arc = ERROR_INVALID_DATA; break;
634 case 8: arc = ERROR_INSTALL_ALREADY_RUNNING; break;
635 case 9: arc = ERROR_INSTALL_PACKAGE_REJECTED; break;
636 default: arc = ERROR_FUNCTION_FAILED; break;
640 ui_actioninfo(package, action, FALSE, uirc);
642 return arc;
645 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
647 MSICOMPONENT *comp;
649 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
651 if (!strcmpW( Component, comp->Component )) return comp;
653 return NULL;
656 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
658 MSIFEATURE *feature;
660 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
662 if (!strcmpW( Feature, feature->Feature )) return feature;
664 return NULL;
667 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
669 MSIFILE *file;
671 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
673 if (!strcmpW( key, file->File )) return file;
675 return NULL;
678 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
680 MSIFOLDER *folder;
682 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
684 if (!strcmpW( dir, folder->Directory )) return folder;
686 return NULL;
690 * Recursively create all directories in the path.
691 * shamelessly stolen from setupapi/queue.c
693 BOOL msi_create_full_path( const WCHAR *path )
695 BOOL ret = TRUE;
696 WCHAR *new_path;
697 int len;
699 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
700 strcpyW( new_path, path );
702 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
703 new_path[len - 1] = 0;
705 while (!CreateDirectoryW( new_path, NULL ))
707 WCHAR *slash;
708 DWORD last_error = GetLastError();
709 if (last_error == ERROR_ALREADY_EXISTS) break;
710 if (last_error != ERROR_PATH_NOT_FOUND)
712 ret = FALSE;
713 break;
715 if (!(slash = strrchrW( new_path, '\\' )))
717 ret = FALSE;
718 break;
720 len = slash - new_path;
721 new_path[len] = 0;
722 if (!msi_create_full_path( new_path ))
724 ret = FALSE;
725 break;
727 new_path[len] = '\\';
729 msi_free( new_path );
730 return ret;
733 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
735 MSIRECORD *row;
737 row = MSI_CreateRecord( 4 );
738 MSI_RecordSetInteger( row, 1, a );
739 MSI_RecordSetInteger( row, 2, b );
740 MSI_RecordSetInteger( row, 3, c );
741 MSI_RecordSetInteger( row, 4, d );
742 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
743 msiobj_release( &row->hdr );
745 msi_dialog_check_messages( NULL );
748 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
750 if (!comp->Enabled)
752 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
753 return INSTALLSTATE_UNKNOWN;
755 if (package->need_rollback) return comp->Installed;
756 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
758 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
759 return INSTALLSTATE_UNKNOWN;
761 return comp->ActionRequest;
764 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
766 if (package->need_rollback) return feature->Installed;
767 return feature->ActionRequest;
770 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
772 MSIPACKAGE *package = param;
773 LPCWSTR dir, component, full_path;
774 MSIRECORD *uirow;
775 MSIFOLDER *folder;
776 MSICOMPONENT *comp;
778 component = MSI_RecordGetString(row, 2);
779 if (!component)
780 return ERROR_SUCCESS;
782 comp = msi_get_loaded_component(package, component);
783 if (!comp)
784 return ERROR_SUCCESS;
786 comp->Action = msi_get_component_action( package, comp );
787 if (comp->Action != INSTALLSTATE_LOCAL)
789 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
790 return ERROR_SUCCESS;
793 dir = MSI_RecordGetString(row,1);
794 if (!dir)
796 ERR("Unable to get folder id\n");
797 return ERROR_SUCCESS;
800 uirow = MSI_CreateRecord(1);
801 MSI_RecordSetStringW(uirow, 1, dir);
802 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
803 msiobj_release(&uirow->hdr);
805 full_path = msi_get_target_folder( package, dir );
806 if (!full_path)
808 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
809 return ERROR_SUCCESS;
811 TRACE("folder is %s\n", debugstr_w(full_path));
813 folder = msi_get_loaded_folder( package, dir );
814 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
815 folder->State = FOLDER_STATE_CREATED;
816 return ERROR_SUCCESS;
819 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
821 static const WCHAR query[] = {
822 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
823 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
824 MSIQUERY *view;
825 UINT rc;
827 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
828 if (rc != ERROR_SUCCESS)
829 return ERROR_SUCCESS;
831 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
832 msiobj_release(&view->hdr);
833 return rc;
836 static void remove_persistent_folder( MSIFOLDER *folder )
838 FolderList *fl;
840 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
842 remove_persistent_folder( fl->folder );
844 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
846 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
850 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
852 MSIPACKAGE *package = param;
853 LPCWSTR dir, component, full_path;
854 MSIRECORD *uirow;
855 MSIFOLDER *folder;
856 MSICOMPONENT *comp;
858 component = MSI_RecordGetString(row, 2);
859 if (!component)
860 return ERROR_SUCCESS;
862 comp = msi_get_loaded_component(package, component);
863 if (!comp)
864 return ERROR_SUCCESS;
866 comp->Action = msi_get_component_action( package, comp );
867 if (comp->Action != INSTALLSTATE_ABSENT)
869 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
870 return ERROR_SUCCESS;
873 dir = MSI_RecordGetString( row, 1 );
874 if (!dir)
876 ERR("Unable to get folder id\n");
877 return ERROR_SUCCESS;
880 full_path = msi_get_target_folder( package, dir );
881 if (!full_path)
883 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
884 return ERROR_SUCCESS;
886 TRACE("folder is %s\n", debugstr_w(full_path));
888 uirow = MSI_CreateRecord( 1 );
889 MSI_RecordSetStringW( uirow, 1, dir );
890 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
891 msiobj_release( &uirow->hdr );
893 folder = msi_get_loaded_folder( package, dir );
894 remove_persistent_folder( folder );
895 return ERROR_SUCCESS;
898 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
900 static const WCHAR query[] = {
901 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
902 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
903 MSIQUERY *view;
904 UINT rc;
906 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
907 if (rc != ERROR_SUCCESS)
908 return ERROR_SUCCESS;
910 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
911 msiobj_release( &view->hdr );
912 return rc;
915 static UINT load_component( MSIRECORD *row, LPVOID param )
917 MSIPACKAGE *package = param;
918 MSICOMPONENT *comp;
920 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
921 if (!comp)
922 return ERROR_FUNCTION_FAILED;
924 list_add_tail( &package->components, &comp->entry );
926 /* fill in the data */
927 comp->Component = msi_dup_record_field( row, 1 );
929 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
931 comp->ComponentId = msi_dup_record_field( row, 2 );
932 comp->Directory = msi_dup_record_field( row, 3 );
933 comp->Attributes = MSI_RecordGetInteger(row,4);
934 comp->Condition = msi_dup_record_field( row, 5 );
935 comp->KeyPath = msi_dup_record_field( row, 6 );
937 comp->Installed = INSTALLSTATE_UNKNOWN;
938 comp->Action = INSTALLSTATE_UNKNOWN;
939 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
941 comp->assembly = msi_load_assembly( package, comp );
942 return ERROR_SUCCESS;
945 UINT msi_load_all_components( MSIPACKAGE *package )
947 static const WCHAR query[] = {
948 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
949 '`','C','o','m','p','o','n','e','n','t','`',0};
950 MSIQUERY *view;
951 UINT r;
953 if (!list_empty(&package->components))
954 return ERROR_SUCCESS;
956 r = MSI_DatabaseOpenViewW( package->db, query, &view );
957 if (r != ERROR_SUCCESS)
958 return r;
960 if (!msi_init_assembly_caches( package ))
962 ERR("can't initialize assembly caches\n");
963 msiobj_release( &view->hdr );
964 return ERROR_FUNCTION_FAILED;
967 r = MSI_IterateRecords(view, NULL, load_component, package);
968 msiobj_release(&view->hdr);
969 return r;
972 typedef struct {
973 MSIPACKAGE *package;
974 MSIFEATURE *feature;
975 } _ilfs;
977 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
979 ComponentList *cl;
981 cl = msi_alloc( sizeof (*cl) );
982 if ( !cl )
983 return ERROR_NOT_ENOUGH_MEMORY;
984 cl->component = comp;
985 list_add_tail( &feature->Components, &cl->entry );
987 return ERROR_SUCCESS;
990 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
992 FeatureList *fl;
994 fl = msi_alloc( sizeof(*fl) );
995 if ( !fl )
996 return ERROR_NOT_ENOUGH_MEMORY;
997 fl->feature = child;
998 list_add_tail( &parent->Children, &fl->entry );
1000 return ERROR_SUCCESS;
1003 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1005 _ilfs* ilfs = param;
1006 LPCWSTR component;
1007 MSICOMPONENT *comp;
1009 component = MSI_RecordGetString(row,1);
1011 /* check to see if the component is already loaded */
1012 comp = msi_get_loaded_component( ilfs->package, component );
1013 if (!comp)
1015 WARN("ignoring unknown component %s\n", debugstr_w(component));
1016 return ERROR_SUCCESS;
1018 add_feature_component( ilfs->feature, comp );
1019 comp->Enabled = TRUE;
1021 return ERROR_SUCCESS;
1024 static UINT load_feature(MSIRECORD * row, LPVOID param)
1026 static const WCHAR query[] = {
1027 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1028 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1029 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1030 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1031 MSIPACKAGE *package = param;
1032 MSIFEATURE *feature;
1033 MSIQUERY *view;
1034 _ilfs ilfs;
1035 UINT rc;
1037 /* fill in the data */
1039 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1040 if (!feature)
1041 return ERROR_NOT_ENOUGH_MEMORY;
1043 list_init( &feature->Children );
1044 list_init( &feature->Components );
1046 feature->Feature = msi_dup_record_field( row, 1 );
1048 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1050 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1051 feature->Title = msi_dup_record_field( row, 3 );
1052 feature->Description = msi_dup_record_field( row, 4 );
1054 if (!MSI_RecordIsNull(row,5))
1055 feature->Display = MSI_RecordGetInteger(row,5);
1057 feature->Level= MSI_RecordGetInteger(row,6);
1058 feature->Directory = msi_dup_record_field( row, 7 );
1059 feature->Attributes = MSI_RecordGetInteger(row,8);
1061 feature->Installed = INSTALLSTATE_UNKNOWN;
1062 feature->Action = INSTALLSTATE_UNKNOWN;
1063 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1065 list_add_tail( &package->features, &feature->entry );
1067 /* load feature components */
1069 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1070 if (rc != ERROR_SUCCESS)
1071 return ERROR_SUCCESS;
1073 ilfs.package = package;
1074 ilfs.feature = feature;
1076 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1077 msiobj_release(&view->hdr);
1078 return rc;
1081 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1083 MSIPACKAGE *package = param;
1084 MSIFEATURE *parent, *child;
1086 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1087 if (!child)
1088 return ERROR_FUNCTION_FAILED;
1090 if (!child->Feature_Parent)
1091 return ERROR_SUCCESS;
1093 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1094 if (!parent)
1095 return ERROR_FUNCTION_FAILED;
1097 add_feature_child( parent, child );
1098 return ERROR_SUCCESS;
1101 UINT msi_load_all_features( MSIPACKAGE *package )
1103 static const WCHAR query[] = {
1104 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1105 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1106 '`','D','i','s','p','l','a','y','`',0};
1107 MSIQUERY *view;
1108 UINT r;
1110 if (!list_empty(&package->features))
1111 return ERROR_SUCCESS;
1113 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1114 if (r != ERROR_SUCCESS)
1115 return r;
1117 r = MSI_IterateRecords( view, NULL, load_feature, package );
1118 if (r != ERROR_SUCCESS)
1120 msiobj_release( &view->hdr );
1121 return r;
1123 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1124 msiobj_release( &view->hdr );
1125 return r;
1128 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1130 if (!p)
1131 return p;
1132 p = strchrW(p, ch);
1133 if (!p)
1134 return p;
1135 *p = 0;
1136 return p+1;
1139 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1141 static const WCHAR query[] = {
1142 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1143 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1144 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1145 MSIQUERY *view = NULL;
1146 MSIRECORD *row = NULL;
1147 UINT r;
1149 TRACE("%s\n", debugstr_w(file->File));
1151 r = MSI_OpenQuery(package->db, &view, query, file->File);
1152 if (r != ERROR_SUCCESS)
1153 goto done;
1155 r = MSI_ViewExecute(view, NULL);
1156 if (r != ERROR_SUCCESS)
1157 goto done;
1159 r = MSI_ViewFetch(view, &row);
1160 if (r != ERROR_SUCCESS)
1161 goto done;
1163 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1164 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1165 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1166 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1167 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1169 done:
1170 if (view) msiobj_release(&view->hdr);
1171 if (row) msiobj_release(&row->hdr);
1172 return r;
1175 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1177 MSIRECORD *row;
1178 static const WCHAR query[] = {
1179 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1180 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1181 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1183 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1184 if (!row)
1186 WARN("query failed\n");
1187 return ERROR_FUNCTION_FAILED;
1190 file->disk_id = MSI_RecordGetInteger( row, 1 );
1191 msiobj_release( &row->hdr );
1192 return ERROR_SUCCESS;
1195 static UINT load_file(MSIRECORD *row, LPVOID param)
1197 MSIPACKAGE* package = param;
1198 LPCWSTR component;
1199 MSIFILE *file;
1201 /* fill in the data */
1203 file = msi_alloc_zero( sizeof (MSIFILE) );
1204 if (!file)
1205 return ERROR_NOT_ENOUGH_MEMORY;
1207 file->File = msi_dup_record_field( row, 1 );
1209 component = MSI_RecordGetString( row, 2 );
1210 file->Component = msi_get_loaded_component( package, component );
1212 if (!file->Component)
1214 WARN("Component not found: %s\n", debugstr_w(component));
1215 msi_free(file->File);
1216 msi_free(file);
1217 return ERROR_SUCCESS;
1220 file->FileName = msi_dup_record_field( row, 3 );
1221 msi_reduce_to_long_filename( file->FileName );
1223 file->ShortName = msi_dup_record_field( row, 3 );
1224 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1226 file->FileSize = MSI_RecordGetInteger( row, 4 );
1227 file->Version = msi_dup_record_field( row, 5 );
1228 file->Language = msi_dup_record_field( row, 6 );
1229 file->Attributes = MSI_RecordGetInteger( row, 7 );
1230 file->Sequence = MSI_RecordGetInteger( row, 8 );
1232 file->state = msifs_invalid;
1234 /* if the compressed bits are not set in the file attributes,
1235 * then read the information from the package word count property
1237 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1239 file->IsCompressed = FALSE;
1241 else if (file->Attributes &
1242 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1244 file->IsCompressed = TRUE;
1246 else if (file->Attributes & msidbFileAttributesNoncompressed)
1248 file->IsCompressed = FALSE;
1250 else
1252 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1255 load_file_hash(package, file);
1256 load_file_disk_id(package, file);
1258 TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1260 list_add_tail( &package->files, &file->entry );
1262 return ERROR_SUCCESS;
1265 static UINT load_all_files(MSIPACKAGE *package)
1267 static const WCHAR query[] = {
1268 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1269 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1270 '`','S','e','q','u','e','n','c','e','`', 0};
1271 MSIQUERY *view;
1272 UINT rc;
1274 if (!list_empty(&package->files))
1275 return ERROR_SUCCESS;
1277 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1278 if (rc != ERROR_SUCCESS)
1279 return ERROR_SUCCESS;
1281 rc = MSI_IterateRecords(view, NULL, load_file, package);
1282 msiobj_release(&view->hdr);
1283 return rc;
1286 static UINT load_media( MSIRECORD *row, LPVOID param )
1288 MSIPACKAGE *package = param;
1289 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1290 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1292 /* FIXME: load external cabinets and directory sources too */
1293 if (!cabinet || cabinet[0] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
1294 return ERROR_SUCCESS;
1296 return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1299 static UINT load_all_media( MSIPACKAGE *package )
1301 static const WCHAR query[] = {
1302 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1303 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1304 '`','D','i','s','k','I','d','`',0};
1305 MSIQUERY *view;
1306 UINT r;
1308 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1309 if (r != ERROR_SUCCESS)
1310 return ERROR_SUCCESS;
1312 r = MSI_IterateRecords( view, NULL, load_media, package );
1313 msiobj_release( &view->hdr );
1314 return r;
1317 static UINT load_patch_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
1319 static const WCHAR query[] =
1320 {'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1321 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1322 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','u',0};
1323 MSIRECORD *rec;
1325 if (!(rec = MSI_QueryGetRecord( package->db, query, patch->Sequence )))
1327 WARN("query failed\n");
1328 return ERROR_FUNCTION_FAILED;
1331 patch->disk_id = MSI_RecordGetInteger( rec, 1 );
1332 msiobj_release( &rec->hdr );
1333 return ERROR_SUCCESS;
1336 static UINT load_patch(MSIRECORD *row, LPVOID param)
1338 MSIPACKAGE *package = param;
1339 MSIFILEPATCH *patch;
1340 const WCHAR *file_key;
1342 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1343 if (!patch)
1344 return ERROR_NOT_ENOUGH_MEMORY;
1346 file_key = MSI_RecordGetString( row, 1 );
1347 patch->File = msi_get_loaded_file( package, file_key );
1348 if (!patch->File)
1350 ERR("Failed to find target for patch in File table\n");
1351 msi_free(patch);
1352 return ERROR_FUNCTION_FAILED;
1355 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1356 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1357 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1359 /* FIXME:
1360 * Header field - for patch validation.
1361 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1364 load_patch_disk_id( package, patch );
1366 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1368 list_add_tail( &package->filepatches, &patch->entry );
1370 return ERROR_SUCCESS;
1373 static UINT load_all_patches(MSIPACKAGE *package)
1375 static const WCHAR query[] = {
1376 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1377 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1378 '`','S','e','q','u','e','n','c','e','`',0};
1379 MSIQUERY *view;
1380 UINT rc;
1382 if (!list_empty(&package->filepatches))
1383 return ERROR_SUCCESS;
1385 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1386 if (rc != ERROR_SUCCESS)
1387 return ERROR_SUCCESS;
1389 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1390 msiobj_release(&view->hdr);
1391 return rc;
1394 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1396 static const WCHAR query[] = {
1397 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1398 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1399 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1400 MSIQUERY *view;
1402 folder->persistent = FALSE;
1403 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1405 if (!MSI_ViewExecute( view, NULL ))
1407 MSIRECORD *rec;
1408 if (!MSI_ViewFetch( view, &rec ))
1410 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1411 folder->persistent = TRUE;
1412 msiobj_release( &rec->hdr );
1415 msiobj_release( &view->hdr );
1417 return ERROR_SUCCESS;
1420 static UINT load_folder( MSIRECORD *row, LPVOID param )
1422 MSIPACKAGE *package = param;
1423 static WCHAR szEmpty[] = { 0 };
1424 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1425 MSIFOLDER *folder;
1427 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1428 list_init( &folder->children );
1429 folder->Directory = msi_dup_record_field( row, 1 );
1430 folder->Parent = msi_dup_record_field( row, 2 );
1431 p = msi_dup_record_field(row, 3);
1433 TRACE("%s\n", debugstr_w(folder->Directory));
1435 /* split src and target dir */
1436 tgt_short = p;
1437 src_short = folder_split_path( p, ':' );
1439 /* split the long and short paths */
1440 tgt_long = folder_split_path( tgt_short, '|' );
1441 src_long = folder_split_path( src_short, '|' );
1443 /* check for no-op dirs */
1444 if (tgt_short && !strcmpW( szDot, tgt_short ))
1445 tgt_short = szEmpty;
1446 if (src_short && !strcmpW( szDot, src_short ))
1447 src_short = szEmpty;
1449 if (!tgt_long)
1450 tgt_long = tgt_short;
1452 if (!src_short) {
1453 src_short = tgt_short;
1454 src_long = tgt_long;
1457 if (!src_long)
1458 src_long = src_short;
1460 /* FIXME: use the target short path too */
1461 folder->TargetDefault = strdupW(tgt_long);
1462 folder->SourceShortPath = strdupW(src_short);
1463 folder->SourceLongPath = strdupW(src_long);
1464 msi_free(p);
1466 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1467 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1468 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1470 load_folder_persistence( package, folder );
1472 list_add_tail( &package->folders, &folder->entry );
1473 return ERROR_SUCCESS;
1476 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1478 FolderList *fl;
1480 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1481 fl->folder = child;
1482 list_add_tail( &parent->children, &fl->entry );
1483 return ERROR_SUCCESS;
1486 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1488 MSIPACKAGE *package = param;
1489 MSIFOLDER *parent, *child;
1491 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1492 return ERROR_FUNCTION_FAILED;
1494 if (!child->Parent) return ERROR_SUCCESS;
1496 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1497 return ERROR_FUNCTION_FAILED;
1499 return add_folder_child( parent, child );
1502 static UINT load_all_folders( MSIPACKAGE *package )
1504 static const WCHAR query[] = {
1505 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1506 '`','D','i','r','e','c','t','o','r','y','`',0};
1507 MSIQUERY *view;
1508 UINT r;
1510 if (!list_empty(&package->folders))
1511 return ERROR_SUCCESS;
1513 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1514 if (r != ERROR_SUCCESS)
1515 return r;
1517 r = MSI_IterateRecords( view, NULL, load_folder, package );
1518 if (r != ERROR_SUCCESS)
1520 msiobj_release( &view->hdr );
1521 return r;
1523 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1524 msiobj_release( &view->hdr );
1525 return r;
1528 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1530 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1531 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1533 load_all_folders( package );
1534 msi_load_all_components( package );
1535 msi_load_all_features( package );
1536 load_all_files( package );
1537 load_all_patches( package );
1538 load_all_media( package );
1540 return ERROR_SUCCESS;
1543 static UINT execute_script( MSIPACKAGE *package, UINT script )
1545 UINT i, rc = ERROR_SUCCESS;
1547 TRACE("executing script %u\n", script);
1549 package->script = script;
1551 if (script == SCRIPT_ROLLBACK)
1553 for (i = package->script_actions_count[script]; i > 0; i--)
1555 rc = ACTION_PerformAction(package, package->script_actions[script][i-1]);
1556 if (rc != ERROR_SUCCESS)
1558 ERR("Execution of script %i halted; action %s returned %u\n",
1559 script, debugstr_w(package->script_actions[script][i-1]), rc);
1560 break;
1564 else
1566 for (i = 0; i < package->script_actions_count[script]; i++)
1568 rc = ACTION_PerformAction(package, package->script_actions[script][i]);
1569 if (rc != ERROR_SUCCESS)
1571 ERR("Execution of script %i halted; action %s returned %u\n",
1572 script, debugstr_w(package->script_actions[script][i]), rc);
1573 break;
1578 package->script = SCRIPT_NONE;
1580 msi_free_action_script(package, script);
1581 return rc;
1584 static UINT ACTION_FileCost(MSIPACKAGE *package)
1586 return ERROR_SUCCESS;
1589 static void get_client_counts( MSIPACKAGE *package )
1591 MSICOMPONENT *comp;
1592 HKEY hkey;
1594 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1596 if (!comp->ComponentId) continue;
1598 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1599 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1601 comp->num_clients = 0;
1602 continue;
1604 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1605 NULL, NULL, NULL, NULL );
1606 RegCloseKey( hkey );
1610 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1612 MSICOMPONENT *comp;
1613 UINT r;
1615 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1617 if (!comp->ComponentId) continue;
1619 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1620 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1621 &comp->Installed );
1622 if (r == ERROR_SUCCESS) continue;
1624 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1625 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1626 &comp->Installed );
1627 if (r == ERROR_SUCCESS) continue;
1629 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1630 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1631 &comp->Installed );
1632 if (r == ERROR_SUCCESS) continue;
1634 comp->Installed = INSTALLSTATE_ABSENT;
1638 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1640 MSIFEATURE *feature;
1642 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1644 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1646 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1647 feature->Installed = INSTALLSTATE_ABSENT;
1648 else
1649 feature->Installed = state;
1653 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1655 return (feature->Level > 0 && feature->Level <= level);
1658 static BOOL process_state_property(MSIPACKAGE* package, int level,
1659 LPCWSTR property, INSTALLSTATE state)
1661 LPWSTR override;
1662 MSIFEATURE *feature;
1663 BOOL remove = !strcmpW(property, szRemove);
1664 BOOL reinstall = !strcmpW(property, szReinstall);
1666 override = msi_dup_property( package->db, property );
1667 if (!override)
1668 return FALSE;
1670 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1672 if (feature->Level <= 0)
1673 continue;
1675 if (reinstall)
1676 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1677 else if (remove)
1678 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1680 if (!strcmpiW( override, szAll ))
1682 feature->Action = state;
1683 feature->ActionRequest = state;
1685 else
1687 LPWSTR ptr = override;
1688 LPWSTR ptr2 = strchrW(override,',');
1690 while (ptr)
1692 int len = ptr2 - ptr;
1694 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1695 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1697 feature->Action = state;
1698 feature->ActionRequest = state;
1699 break;
1701 if (ptr2)
1703 ptr=ptr2+1;
1704 ptr2 = strchrW(ptr,',');
1706 else
1707 break;
1711 msi_free(override);
1712 return TRUE;
1715 static BOOL process_overrides( MSIPACKAGE *package, int level )
1717 static const WCHAR szAddLocal[] =
1718 {'A','D','D','L','O','C','A','L',0};
1719 static const WCHAR szAddSource[] =
1720 {'A','D','D','S','O','U','R','C','E',0};
1721 static const WCHAR szAdvertise[] =
1722 {'A','D','V','E','R','T','I','S','E',0};
1723 BOOL ret = FALSE;
1725 /* all these activation/deactivation things happen in order and things
1726 * later on the list override things earlier on the list.
1728 * 0 INSTALLLEVEL processing
1729 * 1 ADDLOCAL
1730 * 2 REMOVE
1731 * 3 ADDSOURCE
1732 * 4 ADDDEFAULT
1733 * 5 REINSTALL
1734 * 6 ADVERTISE
1735 * 7 COMPADDLOCAL
1736 * 8 COMPADDSOURCE
1737 * 9 FILEADDLOCAL
1738 * 10 FILEADDSOURCE
1739 * 11 FILEADDDEFAULT
1741 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1742 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1743 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1744 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1745 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1747 if (ret)
1748 msi_set_property( package->db, szPreselected, szOne, -1 );
1750 return ret;
1753 static void disable_children( MSIFEATURE *feature, int level )
1755 FeatureList *fl;
1757 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1759 if (!is_feature_selected( feature, level ))
1761 TRACE("child %s (level %d request %d) follows disabled parent %s (level %d request %d)\n",
1762 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1763 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1765 fl->feature->Level = feature->Level;
1766 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1767 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1769 disable_children( fl->feature, level );
1773 static void follow_parent( MSIFEATURE *feature )
1775 FeatureList *fl;
1777 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1779 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1781 TRACE("child %s (level %d request %d) follows parent %s (level %d request %d)\n",
1782 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1783 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1785 fl->feature->Action = feature->Action;
1786 fl->feature->ActionRequest = feature->ActionRequest;
1788 follow_parent( fl->feature );
1792 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1794 int level;
1795 MSICOMPONENT* component;
1796 MSIFEATURE *feature;
1798 TRACE("Checking Install Level\n");
1800 level = msi_get_property_int(package->db, szInstallLevel, 1);
1802 if (msi_get_property_int( package->db, szPreselected, 0 ))
1804 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1806 if (!is_feature_selected( feature, level )) continue;
1808 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1810 if (feature->Installed == INSTALLSTATE_ABSENT)
1812 feature->Action = INSTALLSTATE_UNKNOWN;
1813 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1815 else
1817 feature->Action = feature->Installed;
1818 feature->ActionRequest = feature->Installed;
1822 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1824 if (feature->Feature_Parent) continue;
1825 disable_children( feature, level );
1826 follow_parent( feature );
1829 else if (!msi_get_property_int( package->db, szInstalled, 0 ))
1831 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1833 if (!is_feature_selected( feature, level )) continue;
1835 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1837 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1839 feature->Action = INSTALLSTATE_SOURCE;
1840 feature->ActionRequest = INSTALLSTATE_SOURCE;
1842 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1844 feature->Action = INSTALLSTATE_ADVERTISED;
1845 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1847 else
1849 feature->Action = INSTALLSTATE_LOCAL;
1850 feature->ActionRequest = INSTALLSTATE_LOCAL;
1854 /* disable child features of unselected parent or follow parent */
1855 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1857 if (feature->Feature_Parent) continue;
1858 disable_children( feature, level );
1859 follow_parent( feature );
1863 /* now we want to set component state based based on feature state */
1864 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1866 ComponentList *cl;
1868 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1869 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1870 feature->ActionRequest, feature->Action);
1872 /* features with components that have compressed files are made local */
1873 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1875 if (cl->component->ForceLocalState &&
1876 feature->ActionRequest == INSTALLSTATE_SOURCE)
1878 feature->Action = INSTALLSTATE_LOCAL;
1879 feature->ActionRequest = INSTALLSTATE_LOCAL;
1880 break;
1884 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1886 component = cl->component;
1888 switch (feature->ActionRequest)
1890 case INSTALLSTATE_ABSENT:
1891 component->anyAbsent = 1;
1892 break;
1893 case INSTALLSTATE_ADVERTISED:
1894 component->hasAdvertisedFeature = 1;
1895 break;
1896 case INSTALLSTATE_SOURCE:
1897 component->hasSourceFeature = 1;
1898 break;
1899 case INSTALLSTATE_LOCAL:
1900 component->hasLocalFeature = 1;
1901 break;
1902 case INSTALLSTATE_DEFAULT:
1903 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1904 component->hasAdvertisedFeature = 1;
1905 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1906 component->hasSourceFeature = 1;
1907 else
1908 component->hasLocalFeature = 1;
1909 break;
1910 case INSTALLSTATE_UNKNOWN:
1911 if (feature->Installed == INSTALLSTATE_ADVERTISED)
1912 component->hasAdvertisedFeature = 1;
1913 if (feature->Installed == INSTALLSTATE_SOURCE)
1914 component->hasSourceFeature = 1;
1915 if (feature->Installed == INSTALLSTATE_LOCAL)
1916 component->hasLocalFeature = 1;
1917 break;
1918 default:
1919 break;
1924 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1926 /* check if it's local or source */
1927 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1928 (component->hasLocalFeature || component->hasSourceFeature))
1930 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1931 !component->ForceLocalState)
1933 component->Action = INSTALLSTATE_SOURCE;
1934 component->ActionRequest = INSTALLSTATE_SOURCE;
1936 else
1938 component->Action = INSTALLSTATE_LOCAL;
1939 component->ActionRequest = INSTALLSTATE_LOCAL;
1941 continue;
1944 /* if any feature is local, the component must be local too */
1945 if (component->hasLocalFeature)
1947 component->Action = INSTALLSTATE_LOCAL;
1948 component->ActionRequest = INSTALLSTATE_LOCAL;
1949 continue;
1951 if (component->hasSourceFeature)
1953 component->Action = INSTALLSTATE_SOURCE;
1954 component->ActionRequest = INSTALLSTATE_SOURCE;
1955 continue;
1957 if (component->hasAdvertisedFeature)
1959 component->Action = INSTALLSTATE_ADVERTISED;
1960 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1961 continue;
1963 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1964 if (component->anyAbsent && component->ComponentId)
1966 component->Action = INSTALLSTATE_ABSENT;
1967 component->ActionRequest = INSTALLSTATE_ABSENT;
1971 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1973 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1975 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1976 component->Action = INSTALLSTATE_LOCAL;
1977 component->ActionRequest = INSTALLSTATE_LOCAL;
1980 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1981 component->Installed == INSTALLSTATE_SOURCE &&
1982 component->hasSourceFeature)
1984 component->Action = INSTALLSTATE_UNKNOWN;
1985 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1988 TRACE("component %s (installed %d request %d action %d)\n",
1989 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1991 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1992 component->num_clients++;
1993 else if (component->Action == INSTALLSTATE_ABSENT)
1994 component->num_clients--;
1997 return ERROR_SUCCESS;
2000 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2002 MSIPACKAGE *package = param;
2003 LPCWSTR name;
2004 MSIFEATURE *feature;
2006 name = MSI_RecordGetString( row, 1 );
2008 feature = msi_get_loaded_feature( package, name );
2009 if (!feature)
2010 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2011 else
2013 LPCWSTR Condition;
2014 Condition = MSI_RecordGetString(row,3);
2016 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2018 int level = MSI_RecordGetInteger(row,2);
2019 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2020 feature->Level = level;
2023 return ERROR_SUCCESS;
2026 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2028 static const WCHAR name[] = {'\\',0};
2029 VS_FIXEDFILEINFO *ptr, *ret;
2030 LPVOID version;
2031 DWORD versize, handle;
2032 UINT sz;
2034 versize = GetFileVersionInfoSizeW( filename, &handle );
2035 if (!versize)
2036 return NULL;
2038 version = msi_alloc( versize );
2039 if (!version)
2040 return NULL;
2042 GetFileVersionInfoW( filename, 0, versize, version );
2044 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2046 msi_free( version );
2047 return NULL;
2050 ret = msi_alloc( sz );
2051 memcpy( ret, ptr, sz );
2053 msi_free( version );
2054 return ret;
2057 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2059 DWORD ms, ls;
2061 msi_parse_version_string( version, &ms, &ls );
2063 if (fi->dwFileVersionMS > ms) return 1;
2064 else if (fi->dwFileVersionMS < ms) return -1;
2065 else if (fi->dwFileVersionLS > ls) return 1;
2066 else if (fi->dwFileVersionLS < ls) return -1;
2067 return 0;
2070 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2072 DWORD ms1, ms2;
2074 msi_parse_version_string( ver1, &ms1, NULL );
2075 msi_parse_version_string( ver2, &ms2, NULL );
2077 if (ms1 > ms2) return 1;
2078 else if (ms1 < ms2) return -1;
2079 return 0;
2082 DWORD msi_get_disk_file_size( LPCWSTR filename )
2084 HANDLE file;
2085 DWORD size;
2087 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2088 if (file == INVALID_HANDLE_VALUE)
2089 return INVALID_FILE_SIZE;
2091 size = GetFileSize( file, NULL );
2092 CloseHandle( file );
2093 return size;
2096 BOOL msi_file_hash_matches( MSIFILE *file )
2098 UINT r;
2099 MSIFILEHASHINFO hash;
2101 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2102 r = msi_get_filehash( file->TargetPath, &hash );
2103 if (r != ERROR_SUCCESS)
2104 return FALSE;
2106 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2109 static WCHAR *create_temp_dir( MSIDATABASE *db )
2111 static UINT id;
2112 WCHAR *ret;
2114 if (!db->tempfolder)
2116 WCHAR tmp[MAX_PATH];
2117 UINT len = sizeof(tmp)/sizeof(tmp[0]);
2119 if (msi_get_property( db, szTempFolder, tmp, &len ) ||
2120 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
2122 GetTempPathW( MAX_PATH, tmp );
2124 if (!(db->tempfolder = strdupW( tmp ))) return NULL;
2127 if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
2129 for (;;)
2131 if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret ))
2133 msi_free( ret );
2134 return NULL;
2136 if (CreateDirectoryW( ret, NULL )) break;
2140 return ret;
2144 * msi_build_directory_name()
2146 * This function is to save messing round with directory names
2147 * It handles adding backslashes between path segments,
2148 * and can add \ at the end of the directory name if told to.
2150 * It takes a variable number of arguments.
2151 * It always allocates a new string for the result, so make sure
2152 * to free the return value when finished with it.
2154 * The first arg is the number of path segments that follow.
2155 * The arguments following count are a list of path segments.
2156 * A path segment may be NULL.
2158 * Path segments will be added with a \ separating them.
2159 * A \ will not be added after the last segment, however if the
2160 * last segment is NULL, then the last character will be a \
2162 WCHAR *msi_build_directory_name( DWORD count, ... )
2164 DWORD sz = 1, i;
2165 WCHAR *dir;
2166 va_list va;
2168 va_start( va, count );
2169 for (i = 0; i < count; i++)
2171 const WCHAR *str = va_arg( va, const WCHAR * );
2172 if (str) sz += strlenW( str ) + 1;
2174 va_end( va );
2176 dir = msi_alloc( sz * sizeof(WCHAR) );
2177 dir[0] = 0;
2179 va_start( va, count );
2180 for (i = 0; i < count; i++)
2182 const WCHAR *str = va_arg( va, const WCHAR * );
2183 if (!str) continue;
2184 strcatW( dir, str );
2185 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2187 va_end( va );
2188 return dir;
2191 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2193 return comp->assembly && !comp->assembly->application;
2196 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2198 msi_free( file->TargetPath );
2199 if (msi_is_global_assembly( file->Component ))
2201 MSIASSEMBLY *assembly = file->Component->assembly;
2203 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2204 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2206 else
2208 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2209 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2212 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2215 static UINT calculate_file_cost( MSIPACKAGE *package )
2217 VS_FIXEDFILEINFO *file_version;
2218 WCHAR *font_version;
2219 MSIFILE *file;
2221 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2223 MSICOMPONENT *comp = file->Component;
2224 DWORD file_size;
2226 if (!comp->Enabled) continue;
2228 if (file->IsCompressed)
2229 comp->ForceLocalState = TRUE;
2231 set_target_path( package, file );
2233 if ((comp->assembly && !comp->assembly->installed) ||
2234 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2236 comp->Cost += file->FileSize;
2237 continue;
2239 file_size = msi_get_disk_file_size( file->TargetPath );
2240 TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2242 if (file->Version)
2244 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2246 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2248 comp->Cost += file->FileSize - file_size;
2250 msi_free( file_version );
2251 continue;
2253 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2255 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2257 comp->Cost += file->FileSize - file_size;
2259 msi_free( font_version );
2260 continue;
2263 if (file_size != file->FileSize)
2265 comp->Cost += file->FileSize - file_size;
2268 return ERROR_SUCCESS;
2271 WCHAR *msi_normalize_path( const WCHAR *in )
2273 const WCHAR *p = in;
2274 WCHAR *q, *ret;
2275 int n, len = strlenW( in ) + 2;
2277 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2279 len = 0;
2280 while (1)
2282 /* copy until the end of the string or a space */
2283 while (*p != ' ' && (*q = *p))
2285 p++, len++;
2286 /* reduce many backslashes to one */
2287 if (*p != '\\' || *q != '\\')
2288 q++;
2291 /* quit at the end of the string */
2292 if (!*p)
2293 break;
2295 /* count the number of spaces */
2296 n = 0;
2297 while (p[n] == ' ')
2298 n++;
2300 /* if it's leading or trailing space, skip it */
2301 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2302 p += n;
2303 else /* copy n spaces */
2304 while (n && (*q++ = *p++)) n--;
2306 while (q - ret > 0 && q[-1] == ' ') q--;
2307 if (q - ret > 0 && q[-1] != '\\')
2309 q[0] = '\\';
2310 q[1] = 0;
2312 return ret;
2315 static WCHAR *get_install_location( MSIPACKAGE *package )
2317 HKEY hkey;
2318 WCHAR *path;
2320 if (!package->ProductCode) return NULL;
2321 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2322 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2324 msi_free( path );
2325 path = NULL;
2327 RegCloseKey( hkey );
2328 return path;
2331 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2333 FolderList *fl;
2334 MSIFOLDER *folder, *parent, *child;
2335 WCHAR *path, *normalized_path;
2337 TRACE("resolving %s\n", debugstr_w(name));
2339 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2341 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2343 if (!(path = get_install_location( package )) &&
2344 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2346 path = msi_dup_property( package->db, szRootDrive );
2349 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2351 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2353 parent = msi_get_loaded_folder( package, folder->Parent );
2354 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2356 else
2357 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2359 normalized_path = msi_normalize_path( path );
2360 msi_free( path );
2361 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2363 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2364 msi_free( normalized_path );
2365 return;
2367 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2368 msi_free( folder->ResolvedTarget );
2369 folder->ResolvedTarget = normalized_path;
2371 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2373 child = fl->folder;
2374 msi_resolve_target_folder( package, child->Directory, load_prop );
2376 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2379 static ULONGLONG get_volume_space_required( MSIPACKAGE *package )
2381 MSICOMPONENT *comp;
2382 ULONGLONG ret = 0;
2384 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2386 if (comp->Action == INSTALLSTATE_LOCAL) ret += comp->Cost;
2388 return ret;
2391 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2393 static const WCHAR query[] =
2394 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2395 '`','C','o','n','d','i','t','i','o','n','`',0};
2396 static const WCHAR szOutOfDiskSpace[] =
2397 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2398 static const WCHAR szPrimaryFolder[] =
2399 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2400 static const WCHAR szPrimaryVolumePath[] =
2401 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2402 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2403 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2404 'A','v','a','i','l','a','b','l','e',0};
2405 static const WCHAR szPrimaryVolumeSpaceRequired[] =
2406 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2407 'R','e','q','u','i','r','e','d',0};
2408 static const WCHAR szPrimaryVolumeSpaceRemaining[] =
2409 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2410 'R','e','m','a','i','n','i','n','g',0};
2411 static const WCHAR szOutOfNoRbDiskSpace[] =
2412 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2413 MSICOMPONENT *comp;
2414 MSIQUERY *view;
2415 WCHAR *level, *primary_key, *primary_folder;
2416 UINT rc;
2418 TRACE("Building directory properties\n");
2419 msi_resolve_target_folder( package, szTargetDir, TRUE );
2421 TRACE("Evaluating component conditions\n");
2422 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2424 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2426 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2427 comp->Enabled = FALSE;
2429 else
2430 comp->Enabled = TRUE;
2432 get_client_counts( package );
2434 /* read components states from the registry */
2435 ACTION_GetComponentInstallStates(package);
2436 ACTION_GetFeatureInstallStates(package);
2438 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2440 TRACE("Evaluating feature conditions\n");
2442 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2443 if (rc == ERROR_SUCCESS)
2445 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2446 msiobj_release( &view->hdr );
2447 if (rc != ERROR_SUCCESS)
2448 return rc;
2452 TRACE("Calculating file cost\n");
2453 calculate_file_cost( package );
2455 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2456 /* set default run level if not set */
2457 level = msi_dup_property( package->db, szInstallLevel );
2458 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2459 msi_free(level);
2461 if ((rc = MSI_SetFeatureStates( package ))) return rc;
2463 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2465 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2467 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2468 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2470 static const WCHAR fmtW[] = {'%','l','u',0};
2471 ULARGE_INTEGER free;
2472 ULONGLONG required;
2473 WCHAR buf[21];
2475 primary_folder[2] = 0;
2476 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2478 sprintfW( buf, fmtW, free.QuadPart / 512 );
2479 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2481 required = get_volume_space_required( package );
2482 sprintfW( buf, fmtW, required / 512 );
2483 msi_set_property( package->db, szPrimaryVolumeSpaceRequired, buf, -1 );
2485 sprintfW( buf, fmtW, (free.QuadPart - required) / 512 );
2486 msi_set_property( package->db, szPrimaryVolumeSpaceRemaining, buf, -1 );
2487 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2489 msi_free( primary_folder );
2491 msi_free( primary_key );
2494 /* FIXME: check volume disk space */
2495 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2496 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2498 return ERROR_SUCCESS;
2501 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2503 BYTE *data;
2505 if (!value)
2507 *size = sizeof(WCHAR);
2508 *type = REG_SZ;
2509 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2510 return data;
2512 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2514 if (value[1]=='x')
2516 LPWSTR ptr;
2517 CHAR byte[5];
2518 LPWSTR deformated = NULL;
2519 int count;
2521 deformat_string(package, &value[2], &deformated);
2523 /* binary value type */
2524 ptr = deformated;
2525 *type = REG_BINARY;
2526 if (strlenW(ptr)%2)
2527 *size = (strlenW(ptr)/2)+1;
2528 else
2529 *size = strlenW(ptr)/2;
2531 data = msi_alloc(*size);
2533 byte[0] = '0';
2534 byte[1] = 'x';
2535 byte[4] = 0;
2536 count = 0;
2537 /* if uneven pad with a zero in front */
2538 if (strlenW(ptr)%2)
2540 byte[2]= '0';
2541 byte[3]= *ptr;
2542 ptr++;
2543 data[count] = (BYTE)strtol(byte,NULL,0);
2544 count ++;
2545 TRACE("Uneven byte count\n");
2547 while (*ptr)
2549 byte[2]= *ptr;
2550 ptr++;
2551 byte[3]= *ptr;
2552 ptr++;
2553 data[count] = (BYTE)strtol(byte,NULL,0);
2554 count ++;
2556 msi_free(deformated);
2558 TRACE("Data %i bytes(%i)\n",*size,count);
2560 else
2562 LPWSTR deformated;
2563 LPWSTR p;
2564 DWORD d = 0;
2565 deformat_string(package, &value[1], &deformated);
2567 *type=REG_DWORD;
2568 *size = sizeof(DWORD);
2569 data = msi_alloc(*size);
2570 p = deformated;
2571 if (*p == '-')
2572 p++;
2573 while (*p)
2575 if ( (*p < '0') || (*p > '9') )
2576 break;
2577 d *= 10;
2578 d += (*p - '0');
2579 p++;
2581 if (deformated[0] == '-')
2582 d = -d;
2583 *(LPDWORD)data = d;
2584 TRACE("DWORD %i\n",*(LPDWORD)data);
2586 msi_free(deformated);
2589 else
2591 const WCHAR *ptr = value;
2593 *type = REG_SZ;
2594 if (value[0] == '#')
2596 ptr++; len--;
2597 if (value[1] == '%')
2599 ptr++; len--;
2600 *type = REG_EXPAND_SZ;
2603 data = (BYTE *)msi_strdupW( ptr, len );
2604 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2605 *size = (len + 1) * sizeof(WCHAR);
2607 return data;
2610 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2612 const WCHAR *ret;
2614 switch (root)
2616 case -1:
2617 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2619 *root_key = HKEY_LOCAL_MACHINE;
2620 ret = szHLM;
2622 else
2624 *root_key = HKEY_CURRENT_USER;
2625 ret = szHCU;
2627 break;
2628 case 0:
2629 *root_key = HKEY_CLASSES_ROOT;
2630 ret = szHCR;
2631 break;
2632 case 1:
2633 *root_key = HKEY_CURRENT_USER;
2634 ret = szHCU;
2635 break;
2636 case 2:
2637 *root_key = HKEY_LOCAL_MACHINE;
2638 ret = szHLM;
2639 break;
2640 case 3:
2641 *root_key = HKEY_USERS;
2642 ret = szHU;
2643 break;
2644 default:
2645 ERR("Unknown root %i\n", root);
2646 return NULL;
2649 return ret;
2652 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2654 REGSAM view = 0;
2655 if (is_wow64 || is_64bit)
2656 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2657 return view;
2660 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access )
2662 WCHAR *subkey, *p, *q;
2663 HKEY hkey, ret = NULL;
2664 LONG res;
2666 access |= get_registry_view( comp );
2668 if (!(subkey = strdupW( path ))) return NULL;
2669 p = subkey;
2670 if ((q = strchrW( p, '\\' ))) *q = 0;
2671 if (create)
2672 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2673 else
2674 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2675 if (res)
2677 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2678 msi_free( subkey );
2679 return NULL;
2681 if (q && q[1])
2683 ret = open_key( comp, hkey, q + 1, create, access );
2684 RegCloseKey( hkey );
2686 else ret = hkey;
2687 msi_free( subkey );
2688 return ret;
2691 static BOOL is_special_entry( const WCHAR *name )
2693 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2696 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2698 const WCHAR *p = str;
2699 WCHAR **ret;
2700 int i = 0;
2702 *count = 0;
2703 if (!str) return NULL;
2704 while ((p - str) < len)
2706 p += strlenW( p ) + 1;
2707 (*count)++;
2709 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2710 p = str;
2711 while ((p - str) < len)
2713 if (!(ret[i] = strdupW( p )))
2715 for (; i >= 0; i--) msi_free( ret[i] );
2716 msi_free( ret );
2717 return NULL;
2719 p += strlenW( p ) + 1;
2720 i++;
2722 return ret;
2725 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2726 WCHAR **right, DWORD right_count, DWORD *size )
2728 WCHAR *ret, *p;
2729 unsigned int i;
2731 *size = sizeof(WCHAR);
2732 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2733 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2735 if (!(ret = p = msi_alloc( *size ))) return NULL;
2737 for (i = 0; i < left_count; i++)
2739 strcpyW( p, left[i] );
2740 p += strlenW( p ) + 1;
2742 for (i = 0; i < right_count; i++)
2744 strcpyW( p, right[i] );
2745 p += strlenW( p ) + 1;
2747 *p = 0;
2748 return ret;
2751 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2752 WCHAR **new, DWORD new_count )
2754 DWORD ret = old_count;
2755 unsigned int i, j, k;
2757 for (i = 0; i < new_count; i++)
2759 for (j = 0; j < old_count; j++)
2761 if (old[j] && !strcmpW( new[i], old[j] ))
2763 msi_free( old[j] );
2764 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2765 old[k] = NULL;
2766 ret--;
2770 return ret;
2773 enum join_op
2775 JOIN_OP_APPEND,
2776 JOIN_OP_PREPEND,
2777 JOIN_OP_REPLACE
2780 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2781 WCHAR **new, DWORD new_count, DWORD *size )
2783 switch (op)
2785 case JOIN_OP_APPEND:
2786 old_count = remove_duplicate_values( old, old_count, new, new_count );
2787 return flatten_multi_string_values( old, old_count, new, new_count, size );
2789 case JOIN_OP_PREPEND:
2790 old_count = remove_duplicate_values( old, old_count, new, new_count );
2791 return flatten_multi_string_values( new, new_count, old, old_count, size );
2793 case JOIN_OP_REPLACE:
2794 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2796 default:
2797 ERR("unhandled join op %u\n", op);
2798 return NULL;
2802 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2803 BYTE *new_value, DWORD new_size, DWORD *size )
2805 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2806 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2807 enum join_op op = JOIN_OP_REPLACE;
2808 WCHAR **old = NULL, **new = NULL;
2809 BYTE *ret;
2811 if (new_size / sizeof(WCHAR) - 1 > 1)
2813 new_ptr = (const WCHAR *)new_value;
2814 new_len = new_size / sizeof(WCHAR) - 1;
2816 if (!new_ptr[0] && new_ptr[new_len - 1])
2818 op = JOIN_OP_APPEND;
2819 new_len--;
2820 new_ptr++;
2822 else if (new_ptr[0] && !new_ptr[new_len - 1])
2824 op = JOIN_OP_PREPEND;
2825 new_len--;
2827 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2829 op = JOIN_OP_REPLACE;
2830 new_len -= 2;
2831 new_ptr++;
2833 new = split_multi_string_values( new_ptr, new_len, &new_count );
2835 if (old_size / sizeof(WCHAR) - 1 > 1)
2837 old_ptr = (const WCHAR *)old_value;
2838 old_len = old_size / sizeof(WCHAR) - 1;
2839 old = split_multi_string_values( old_ptr, old_len, &old_count );
2841 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2842 for (i = 0; i < old_count; i++) msi_free( old[i] );
2843 for (i = 0; i < new_count; i++) msi_free( new[i] );
2844 msi_free( old );
2845 msi_free( new );
2846 return ret;
2849 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2851 BYTE *ret;
2852 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2853 if (!(ret = msi_alloc( *size ))) return NULL;
2854 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2855 return ret;
2858 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2860 MSIPACKAGE *package = param;
2861 BYTE *new_value, *old_value = NULL;
2862 HKEY root_key, hkey;
2863 DWORD type, old_type, new_size, old_size = 0;
2864 LPWSTR deformated, uikey;
2865 const WCHAR *szRoot, *component, *name, *key, *str;
2866 MSICOMPONENT *comp;
2867 MSIRECORD * uirow;
2868 INT root;
2869 BOOL check_first = FALSE;
2870 int len;
2872 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2874 component = MSI_RecordGetString(row, 6);
2875 comp = msi_get_loaded_component(package,component);
2876 if (!comp)
2877 return ERROR_SUCCESS;
2879 comp->Action = msi_get_component_action( package, comp );
2880 if (comp->Action != INSTALLSTATE_LOCAL && comp->Action != INSTALLSTATE_SOURCE)
2882 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2883 return ERROR_SUCCESS;
2886 name = MSI_RecordGetString(row, 4);
2887 if( MSI_RecordIsNull(row,5) && name )
2889 /* null values can have special meanings */
2890 if (name[0]=='-' && name[1] == 0)
2891 return ERROR_SUCCESS;
2892 if ((name[0] == '+' || name[0] == '*') && !name[1])
2893 check_first = TRUE;
2896 root = MSI_RecordGetInteger(row,2);
2897 key = MSI_RecordGetString(row, 3);
2899 szRoot = get_root_key( package, root, &root_key );
2900 if (!szRoot)
2901 return ERROR_SUCCESS;
2903 deformat_string(package, key , &deformated);
2904 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2905 strcpyW(uikey,szRoot);
2906 strcatW(uikey,deformated);
2908 if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE )))
2910 ERR("Could not create key %s\n", debugstr_w(deformated));
2911 msi_free(uikey);
2912 msi_free(deformated);
2913 return ERROR_FUNCTION_FAILED;
2915 msi_free( deformated );
2916 str = msi_record_get_string( row, 5, NULL );
2917 len = deformat_string( package, str, &deformated );
2918 new_value = parse_value( package, deformated, len, &type, &new_size );
2920 msi_free( deformated );
2921 deformat_string(package, name, &deformated);
2923 if (!is_special_entry( name ))
2925 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2926 if (type == REG_MULTI_SZ)
2928 BYTE *new;
2929 if (old_value && old_type != REG_MULTI_SZ)
2931 msi_free( old_value );
2932 old_value = NULL;
2933 old_size = 0;
2935 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2936 msi_free( new_value );
2937 new_value = new;
2939 if (!check_first)
2941 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2942 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2944 else if (!old_value)
2946 if (deformated || new_size)
2948 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2949 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2952 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2954 RegCloseKey(hkey);
2956 uirow = MSI_CreateRecord(3);
2957 MSI_RecordSetStringW(uirow,2,deformated);
2958 MSI_RecordSetStringW(uirow,1,uikey);
2959 if (type == REG_SZ || type == REG_EXPAND_SZ)
2960 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2961 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
2962 msiobj_release( &uirow->hdr );
2964 msi_free(new_value);
2965 msi_free(old_value);
2966 msi_free(deformated);
2967 msi_free(uikey);
2969 return ERROR_SUCCESS;
2972 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2974 static const WCHAR query[] = {
2975 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2976 '`','R','e','g','i','s','t','r','y','`',0};
2977 MSIQUERY *view;
2978 UINT rc;
2980 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2981 if (rc != ERROR_SUCCESS)
2982 return ERROR_SUCCESS;
2984 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2985 msiobj_release(&view->hdr);
2986 return rc;
2989 static int is_key_empty(const MSICOMPONENT *comp, HKEY root, const WCHAR *path)
2991 DWORD subkeys, values;
2992 HKEY key;
2993 LONG res;
2995 key = open_key(comp, root, path, FALSE, get_registry_view(comp) | KEY_READ);
2996 if (!key) return 0;
2998 res = RegQueryInfoKeyW(key, 0, 0, 0, &subkeys, 0, 0, &values, 0, 0, 0, 0);
2999 RegCloseKey(key);
3001 return !res && !subkeys && !values;
3004 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3006 LONG res = ERROR_SUCCESS;
3007 REGSAM access = 0;
3008 WCHAR *subkey, *p;
3009 HKEY hkey;
3011 access |= get_registry_view( comp );
3013 if (!(subkey = strdupW( path ))) return;
3016 if ((p = strrchrW( subkey, '\\' )))
3018 *p = 0;
3019 if (!p[1]) continue; /* trailing backslash */
3020 hkey = open_key( comp, root, subkey, FALSE, access | READ_CONTROL );
3021 if (!hkey) break;
3022 if (!is_key_empty(comp, hkey, p + 1))
3024 RegCloseKey(hkey);
3025 break;
3027 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
3028 RegCloseKey( hkey );
3030 else if (is_key_empty(comp, root, subkey))
3031 res = RegDeleteKeyExW( root, subkey, access, 0 );
3032 if (res)
3034 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
3035 break;
3037 } while (p);
3038 msi_free( subkey );
3041 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
3043 LONG res;
3044 HKEY hkey;
3046 if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE )))
3048 if ((res = RegDeleteValueW( hkey, value )))
3049 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3051 RegCloseKey( hkey );
3052 if (is_key_empty(comp, root, path))
3054 TRACE("removing empty key %s\n", debugstr_w(path));
3055 delete_key( comp, root, path );
3060 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3062 LONG res;
3063 HKEY hkey;
3065 if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return;
3066 res = RegDeleteTreeW( hkey, NULL );
3067 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3068 delete_key( comp, root, path );
3069 RegCloseKey( hkey );
3072 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3074 MSIPACKAGE *package = param;
3075 LPCWSTR component, name, key_str, root_key_str;
3076 LPWSTR deformated_key, deformated_name, ui_key_str;
3077 MSICOMPONENT *comp;
3078 MSIRECORD *uirow;
3079 BOOL delete_key = FALSE;
3080 HKEY hkey_root;
3081 UINT size;
3082 INT root;
3084 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3086 component = MSI_RecordGetString( row, 6 );
3087 comp = msi_get_loaded_component( package, component );
3088 if (!comp)
3089 return ERROR_SUCCESS;
3091 comp->Action = msi_get_component_action( package, comp );
3092 if (comp->Action != INSTALLSTATE_ABSENT)
3094 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3095 return ERROR_SUCCESS;
3098 name = MSI_RecordGetString( row, 4 );
3099 if (MSI_RecordIsNull( row, 5 ) && name )
3101 if (name[0] == '+' && !name[1])
3102 return ERROR_SUCCESS;
3103 if ((name[0] == '-' || name[0] == '*') && !name[1])
3105 delete_key = TRUE;
3106 name = NULL;
3110 root = MSI_RecordGetInteger( row, 2 );
3111 key_str = MSI_RecordGetString( row, 3 );
3113 root_key_str = get_root_key( package, root, &hkey_root );
3114 if (!root_key_str)
3115 return ERROR_SUCCESS;
3117 deformat_string( package, key_str, &deformated_key );
3118 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3119 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3120 strcpyW( ui_key_str, root_key_str );
3121 strcatW( ui_key_str, deformated_key );
3123 deformat_string( package, name, &deformated_name );
3125 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3126 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3127 msi_free( deformated_key );
3129 uirow = MSI_CreateRecord( 2 );
3130 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3131 MSI_RecordSetStringW( uirow, 2, deformated_name );
3132 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3133 msiobj_release( &uirow->hdr );
3135 msi_free( ui_key_str );
3136 msi_free( deformated_name );
3137 return ERROR_SUCCESS;
3140 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3142 MSIPACKAGE *package = param;
3143 LPCWSTR component, name, key_str, root_key_str;
3144 LPWSTR deformated_key, deformated_name, ui_key_str;
3145 MSICOMPONENT *comp;
3146 MSIRECORD *uirow;
3147 BOOL delete_key = FALSE;
3148 HKEY hkey_root;
3149 UINT size;
3150 INT root;
3152 component = MSI_RecordGetString( row, 5 );
3153 comp = msi_get_loaded_component( package, component );
3154 if (!comp)
3155 return ERROR_SUCCESS;
3157 comp->Action = msi_get_component_action( package, comp );
3158 if (comp->Action != INSTALLSTATE_LOCAL)
3160 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3161 return ERROR_SUCCESS;
3164 if ((name = MSI_RecordGetString( row, 4 )))
3166 if (name[0] == '-' && !name[1])
3168 delete_key = TRUE;
3169 name = NULL;
3173 root = MSI_RecordGetInteger( row, 2 );
3174 key_str = MSI_RecordGetString( row, 3 );
3176 root_key_str = get_root_key( package, root, &hkey_root );
3177 if (!root_key_str)
3178 return ERROR_SUCCESS;
3180 deformat_string( package, key_str, &deformated_key );
3181 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3182 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3183 strcpyW( ui_key_str, root_key_str );
3184 strcatW( ui_key_str, deformated_key );
3186 deformat_string( package, name, &deformated_name );
3188 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3189 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3190 msi_free( deformated_key );
3192 uirow = MSI_CreateRecord( 2 );
3193 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3194 MSI_RecordSetStringW( uirow, 2, deformated_name );
3195 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3196 msiobj_release( &uirow->hdr );
3198 msi_free( ui_key_str );
3199 msi_free( deformated_name );
3200 return ERROR_SUCCESS;
3203 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3205 static const WCHAR registry_query[] = {
3206 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3207 '`','R','e','g','i','s','t','r','y','`',0};
3208 static const WCHAR remove_registry_query[] = {
3209 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3210 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3211 MSIQUERY *view;
3212 UINT rc;
3214 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3215 if (rc == ERROR_SUCCESS)
3217 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3218 msiobj_release( &view->hdr );
3219 if (rc != ERROR_SUCCESS)
3220 return rc;
3222 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3223 if (rc == ERROR_SUCCESS)
3225 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3226 msiobj_release( &view->hdr );
3227 if (rc != ERROR_SUCCESS)
3228 return rc;
3230 return ERROR_SUCCESS;
3233 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3235 return ERROR_SUCCESS;
3239 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3241 static const WCHAR query[]= {
3242 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3243 '`','R','e','g','i','s','t','r','y','`',0};
3244 MSICOMPONENT *comp;
3245 DWORD total = 0, count = 0;
3246 MSIQUERY *view;
3247 MSIFEATURE *feature;
3248 MSIFILE *file;
3249 UINT rc;
3251 TRACE("InstallValidate\n");
3253 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3254 if (rc == ERROR_SUCCESS)
3256 rc = MSI_IterateRecords( view, &count, NULL, package );
3257 msiobj_release( &view->hdr );
3258 if (rc != ERROR_SUCCESS)
3259 return rc;
3260 total += count * REG_PROGRESS_VALUE;
3262 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3263 total += COMPONENT_PROGRESS_VALUE;
3265 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3266 total += file->FileSize;
3268 msi_ui_progress( package, 0, total, 0, 0 );
3270 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3272 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3273 debugstr_w(feature->Feature), feature->Installed,
3274 feature->ActionRequest, feature->Action);
3276 return ERROR_SUCCESS;
3279 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3281 MSIPACKAGE* package = param;
3282 LPCWSTR cond = NULL;
3283 LPCWSTR message = NULL;
3284 UINT r;
3286 static const WCHAR title[]=
3287 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3289 cond = MSI_RecordGetString(row,1);
3291 r = MSI_EvaluateConditionW(package,cond);
3292 if (r == MSICONDITION_FALSE)
3294 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3296 LPWSTR deformated;
3297 message = MSI_RecordGetString(row,2);
3298 deformat_string(package,message,&deformated);
3299 MessageBoxW(NULL,deformated,title,MB_OK);
3300 msi_free(deformated);
3303 return ERROR_INSTALL_FAILURE;
3306 return ERROR_SUCCESS;
3309 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3311 static const WCHAR query[] = {
3312 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3313 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3314 MSIQUERY *view;
3315 UINT rc;
3317 TRACE("Checking launch conditions\n");
3319 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3320 if (rc != ERROR_SUCCESS)
3321 return ERROR_SUCCESS;
3323 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3324 msiobj_release(&view->hdr);
3325 return rc;
3328 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3331 if (!cmp->KeyPath)
3332 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3334 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3336 static const WCHAR query[] = {
3337 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3338 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3339 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3340 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3341 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3342 MSIRECORD *row;
3343 UINT root, len;
3344 LPWSTR deformated, buffer, deformated_name;
3345 LPCWSTR key, name;
3347 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3348 if (!row)
3349 return NULL;
3351 root = MSI_RecordGetInteger(row,2);
3352 key = MSI_RecordGetString(row, 3);
3353 name = MSI_RecordGetString(row, 4);
3354 deformat_string(package, key , &deformated);
3355 deformat_string(package, name, &deformated_name);
3357 len = strlenW(deformated) + 6;
3358 if (deformated_name)
3359 len+=strlenW(deformated_name);
3361 buffer = msi_alloc( len *sizeof(WCHAR));
3363 if (deformated_name)
3364 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3365 else
3366 sprintfW(buffer,fmt,root,deformated);
3368 msi_free(deformated);
3369 msi_free(deformated_name);
3370 msiobj_release(&row->hdr);
3372 return buffer;
3374 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3376 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3377 return NULL;
3379 else
3381 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3383 if (file)
3384 return strdupW( file->TargetPath );
3386 return NULL;
3389 static HKEY openSharedDLLsKey(void)
3391 HKEY hkey=0;
3392 static const WCHAR path[] =
3393 {'S','o','f','t','w','a','r','e','\\',
3394 'M','i','c','r','o','s','o','f','t','\\',
3395 'W','i','n','d','o','w','s','\\',
3396 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3397 'S','h','a','r','e','d','D','L','L','s',0};
3399 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3400 return hkey;
3403 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3405 HKEY hkey;
3406 DWORD count=0;
3407 DWORD type;
3408 DWORD sz = sizeof(count);
3409 DWORD rc;
3411 hkey = openSharedDLLsKey();
3412 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3413 if (rc != ERROR_SUCCESS)
3414 count = 0;
3415 RegCloseKey(hkey);
3416 return count;
3419 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3421 HKEY hkey;
3423 hkey = openSharedDLLsKey();
3424 if (count > 0)
3425 msi_reg_set_val_dword( hkey, path, count );
3426 else
3427 RegDeleteValueW(hkey,path);
3428 RegCloseKey(hkey);
3429 return count;
3432 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3434 MSIFEATURE *feature;
3435 INT count = 0;
3436 BOOL write = FALSE;
3438 /* only refcount DLLs */
3439 if (comp->KeyPath == NULL ||
3440 comp->assembly ||
3441 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3442 comp->Attributes & msidbComponentAttributesODBCDataSource)
3443 write = FALSE;
3444 else
3446 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3447 write = (count > 0);
3449 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3450 write = TRUE;
3453 /* increment counts */
3454 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3456 ComponentList *cl;
3458 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3459 continue;
3461 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3463 if ( cl->component == comp )
3464 count++;
3468 /* decrement counts */
3469 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3471 ComponentList *cl;
3473 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3474 continue;
3476 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3478 if ( cl->component == comp )
3479 count--;
3483 /* ref count all the files in the component */
3484 if (write)
3486 MSIFILE *file;
3488 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3490 if (file->Component == comp)
3491 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3495 /* add a count for permanent */
3496 if (comp->Attributes & msidbComponentAttributesPermanent)
3497 count ++;
3499 comp->RefCount = count;
3501 if (write)
3502 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3505 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3507 if (comp->assembly)
3509 const WCHAR prefixW[] = {'<','\\',0};
3510 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3511 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3513 if (keypath)
3515 strcpyW( keypath, prefixW );
3516 strcatW( keypath, comp->assembly->display_name );
3518 return keypath;
3520 return resolve_keypath( package, comp );
3523 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3525 WCHAR squashed_pc[SQUASHED_GUID_SIZE], squashed_cc[SQUASHED_GUID_SIZE];
3526 UINT rc;
3527 MSICOMPONENT *comp;
3528 HKEY hkey;
3530 TRACE("\n");
3532 squash_guid( package->ProductCode, squashed_pc );
3533 msi_set_sourcedir_props(package, FALSE);
3535 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3537 MSIRECORD *uirow;
3538 INSTALLSTATE action;
3540 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3541 if (!comp->ComponentId)
3542 continue;
3544 squash_guid( comp->ComponentId, squashed_cc );
3545 msi_free( comp->FullKeypath );
3546 comp->FullKeypath = build_full_keypath( package, comp );
3548 ACTION_RefCountComponent( package, comp );
3550 if (package->need_rollback) action = comp->Installed;
3551 else action = comp->ActionRequest;
3553 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3554 debugstr_w(comp->Component), debugstr_w(squashed_cc),
3555 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3557 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3559 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3560 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3561 else
3562 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3564 if (rc != ERROR_SUCCESS)
3565 continue;
3567 if (comp->Attributes & msidbComponentAttributesPermanent)
3569 static const WCHAR szPermKey[] =
3570 { '0','0','0','0','0','0','0','0','0','0','0','0',
3571 '0','0','0','0','0','0','0','0','0','0','0','0',
3572 '0','0','0','0','0','0','0','0',0 };
3574 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3576 if (action == INSTALLSTATE_LOCAL)
3577 msi_reg_set_val_str( hkey, squashed_pc, comp->FullKeypath );
3578 else
3580 MSIFILE *file;
3581 MSIRECORD *row;
3582 LPWSTR ptr, ptr2;
3583 WCHAR source[MAX_PATH];
3584 WCHAR base[MAX_PATH];
3585 LPWSTR sourcepath;
3587 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3588 static const WCHAR query[] = {
3589 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3590 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3591 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3592 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3593 '`','D','i','s','k','I','d','`',0};
3595 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3596 continue;
3598 if (!(row = MSI_QueryGetRecord(package->db, query, file->Sequence)))
3599 return ERROR_FUNCTION_FAILED;
3601 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3602 ptr2 = strrchrW(source, '\\') + 1;
3603 msiobj_release(&row->hdr);
3605 lstrcpyW(base, package->PackagePath);
3606 ptr = strrchrW(base, '\\');
3607 *(ptr + 1) = '\0';
3609 sourcepath = msi_resolve_file_source(package, file);
3610 ptr = sourcepath + lstrlenW(base);
3611 lstrcpyW(ptr2, ptr);
3612 msi_free(sourcepath);
3614 msi_reg_set_val_str( hkey, squashed_pc, source );
3616 RegCloseKey(hkey);
3618 else if (action == INSTALLSTATE_ABSENT)
3620 if (comp->num_clients <= 0)
3622 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3623 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3624 else
3625 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3627 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3629 else
3631 LONG res;
3633 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3634 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3635 else
3636 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3638 if (rc != ERROR_SUCCESS)
3640 WARN( "failed to open component key %u\n", rc );
3641 continue;
3643 res = RegDeleteValueW( hkey, squashed_pc );
3644 RegCloseKey(hkey);
3645 if (res) WARN( "failed to delete component value %d\n", res );
3649 /* UI stuff */
3650 uirow = MSI_CreateRecord(3);
3651 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3652 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3653 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3654 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3655 msiobj_release( &uirow->hdr );
3657 return ERROR_SUCCESS;
3660 typedef struct {
3661 CLSID clsid;
3662 LPWSTR source;
3664 LPWSTR path;
3665 ITypeLib *ptLib;
3666 } typelib_struct;
3668 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3669 LPWSTR lpszName, LONG_PTR lParam)
3671 TLIBATTR *attr;
3672 typelib_struct *tl_struct = (typelib_struct*) lParam;
3673 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3674 int sz;
3675 HRESULT res;
3677 if (!IS_INTRESOURCE(lpszName))
3679 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3680 return TRUE;
3683 sz = strlenW(tl_struct->source)+4;
3684 sz *= sizeof(WCHAR);
3686 if ((INT_PTR)lpszName == 1)
3687 tl_struct->path = strdupW(tl_struct->source);
3688 else
3690 tl_struct->path = msi_alloc(sz);
3691 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3694 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3695 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3696 if (FAILED(res))
3698 msi_free(tl_struct->path);
3699 tl_struct->path = NULL;
3701 return TRUE;
3704 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3705 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3707 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3708 return FALSE;
3711 msi_free(tl_struct->path);
3712 tl_struct->path = NULL;
3714 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3715 ITypeLib_Release(tl_struct->ptLib);
3717 return TRUE;
3720 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3722 MSIPACKAGE* package = param;
3723 LPCWSTR component;
3724 MSICOMPONENT *comp;
3725 MSIFILE *file;
3726 typelib_struct tl_struct;
3727 ITypeLib *tlib;
3728 HMODULE module;
3729 HRESULT hr;
3731 component = MSI_RecordGetString(row,3);
3732 comp = msi_get_loaded_component(package,component);
3733 if (!comp)
3734 return ERROR_SUCCESS;
3736 comp->Action = msi_get_component_action( package, comp );
3737 if (comp->Action != INSTALLSTATE_LOCAL)
3739 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3740 return ERROR_SUCCESS;
3743 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3745 TRACE("component has no key path\n");
3746 return ERROR_SUCCESS;
3748 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3750 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3751 if (module)
3753 LPCWSTR guid;
3754 guid = MSI_RecordGetString(row,1);
3755 CLSIDFromString( guid, &tl_struct.clsid);
3756 tl_struct.source = strdupW( file->TargetPath );
3757 tl_struct.path = NULL;
3759 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3760 (LONG_PTR)&tl_struct);
3762 if (tl_struct.path)
3764 LPCWSTR helpid, help_path = NULL;
3765 HRESULT res;
3767 helpid = MSI_RecordGetString(row,6);
3769 if (helpid) help_path = msi_get_target_folder( package, helpid );
3770 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3772 if (FAILED(res))
3773 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3774 else
3775 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3777 ITypeLib_Release(tl_struct.ptLib);
3778 msi_free(tl_struct.path);
3780 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3782 FreeLibrary(module);
3783 msi_free(tl_struct.source);
3785 else
3787 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3788 if (FAILED(hr))
3790 ERR("Failed to load type library: %08x\n", hr);
3791 return ERROR_INSTALL_FAILURE;
3794 ITypeLib_Release(tlib);
3797 return ERROR_SUCCESS;
3800 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3802 static const WCHAR query[] = {
3803 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3804 '`','T','y','p','e','L','i','b','`',0};
3805 MSIQUERY *view;
3806 UINT rc;
3808 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3809 if (rc != ERROR_SUCCESS)
3810 return ERROR_SUCCESS;
3812 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3813 msiobj_release(&view->hdr);
3814 return rc;
3817 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3819 MSIPACKAGE *package = param;
3820 LPCWSTR component, guid;
3821 MSICOMPONENT *comp;
3822 GUID libid;
3823 UINT version;
3824 LCID language;
3825 SYSKIND syskind;
3826 HRESULT hr;
3828 component = MSI_RecordGetString( row, 3 );
3829 comp = msi_get_loaded_component( package, component );
3830 if (!comp)
3831 return ERROR_SUCCESS;
3833 comp->Action = msi_get_component_action( package, comp );
3834 if (comp->Action != INSTALLSTATE_ABSENT)
3836 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3837 return ERROR_SUCCESS;
3839 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3841 guid = MSI_RecordGetString( row, 1 );
3842 CLSIDFromString( guid, &libid );
3843 version = MSI_RecordGetInteger( row, 4 );
3844 language = MSI_RecordGetInteger( row, 2 );
3846 #ifdef _WIN64
3847 syskind = SYS_WIN64;
3848 #else
3849 syskind = SYS_WIN32;
3850 #endif
3852 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3853 if (FAILED(hr))
3855 WARN("Failed to unregister typelib: %08x\n", hr);
3858 return ERROR_SUCCESS;
3861 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3863 static const WCHAR query[] = {
3864 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3865 '`','T','y','p','e','L','i','b','`',0};
3866 MSIQUERY *view;
3867 UINT rc;
3869 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3870 if (rc != ERROR_SUCCESS)
3871 return ERROR_SUCCESS;
3873 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3874 msiobj_release( &view->hdr );
3875 return rc;
3878 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3880 static const WCHAR szlnk[] = {'.','l','n','k',0};
3881 LPCWSTR directory, extension, link_folder;
3882 LPWSTR link_file, filename;
3884 directory = MSI_RecordGetString( row, 2 );
3885 link_folder = msi_get_target_folder( package, directory );
3886 if (!link_folder)
3888 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3889 return NULL;
3891 /* may be needed because of a bug somewhere else */
3892 msi_create_full_path( link_folder );
3894 filename = msi_dup_record_field( row, 3 );
3895 msi_reduce_to_long_filename( filename );
3897 extension = strrchrW( filename, '.' );
3898 if (!extension || strcmpiW( extension, szlnk ))
3900 int len = strlenW( filename );
3901 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3902 memcpy( filename + len, szlnk, sizeof(szlnk) );
3904 link_file = msi_build_directory_name( 2, link_folder, filename );
3905 msi_free( filename );
3907 return link_file;
3910 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3912 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3913 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3914 WCHAR *folder, *dest, *path;
3916 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3917 folder = msi_dup_property( package->db, szWindowsFolder );
3918 else
3920 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3921 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3922 msi_free( appdata );
3924 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3925 msi_create_full_path( dest );
3926 path = msi_build_directory_name( 2, dest, icon_name );
3927 msi_free( folder );
3928 msi_free( dest );
3929 return path;
3932 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3934 MSIPACKAGE *package = param;
3935 LPWSTR link_file, deformated, path;
3936 LPCWSTR component, target;
3937 MSICOMPONENT *comp;
3938 IShellLinkW *sl = NULL;
3939 IPersistFile *pf = NULL;
3940 HRESULT res;
3942 component = MSI_RecordGetString(row, 4);
3943 comp = msi_get_loaded_component(package, component);
3944 if (!comp)
3945 return ERROR_SUCCESS;
3947 comp->Action = msi_get_component_action( package, comp );
3948 if (comp->Action != INSTALLSTATE_LOCAL)
3950 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3951 return ERROR_SUCCESS;
3953 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3955 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3956 &IID_IShellLinkW, (LPVOID *) &sl );
3958 if (FAILED( res ))
3960 ERR("CLSID_ShellLink not available\n");
3961 goto err;
3964 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3965 if (FAILED( res ))
3967 ERR("QueryInterface(IID_IPersistFile) failed\n");
3968 goto err;
3971 target = MSI_RecordGetString(row, 5);
3972 if (strchrW(target, '['))
3974 deformat_string( package, target, &path );
3975 TRACE("target path is %s\n", debugstr_w(path));
3976 IShellLinkW_SetPath( sl, path );
3977 msi_free( path );
3979 else
3981 FIXME("poorly handled shortcut format, advertised shortcut\n");
3982 path = resolve_keypath( package, comp );
3983 IShellLinkW_SetPath( sl, path );
3984 msi_free( path );
3987 if (!MSI_RecordIsNull(row,6))
3989 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3990 deformat_string(package, arguments, &deformated);
3991 IShellLinkW_SetArguments(sl,deformated);
3992 msi_free(deformated);
3995 if (!MSI_RecordIsNull(row,7))
3997 LPCWSTR description = MSI_RecordGetString(row, 7);
3998 IShellLinkW_SetDescription(sl, description);
4001 if (!MSI_RecordIsNull(row,8))
4002 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
4004 if (!MSI_RecordIsNull(row,9))
4006 INT index;
4007 LPCWSTR icon = MSI_RecordGetString(row, 9);
4009 path = msi_build_icon_path(package, icon);
4010 index = MSI_RecordGetInteger(row,10);
4012 /* no value means 0 */
4013 if (index == MSI_NULL_INTEGER)
4014 index = 0;
4016 IShellLinkW_SetIconLocation(sl, path, index);
4017 msi_free(path);
4020 if (!MSI_RecordIsNull(row,11))
4021 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
4023 if (!MSI_RecordIsNull(row,12))
4025 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
4026 full_path = msi_get_target_folder( package, wkdir );
4027 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
4029 link_file = get_link_file(package, row);
4031 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
4032 IPersistFile_Save(pf, link_file, FALSE);
4033 msi_free(link_file);
4035 err:
4036 if (pf)
4037 IPersistFile_Release( pf );
4038 if (sl)
4039 IShellLinkW_Release( sl );
4041 return ERROR_SUCCESS;
4044 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4046 static const WCHAR query[] = {
4047 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4048 '`','S','h','o','r','t','c','u','t','`',0};
4049 MSIQUERY *view;
4050 HRESULT res;
4051 UINT rc;
4053 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4054 if (rc != ERROR_SUCCESS)
4055 return ERROR_SUCCESS;
4057 res = CoInitialize( NULL );
4059 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4060 msiobj_release(&view->hdr);
4062 if (SUCCEEDED(res)) CoUninitialize();
4063 return rc;
4066 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4068 MSIPACKAGE *package = param;
4069 LPWSTR link_file;
4070 LPCWSTR component;
4071 MSICOMPONENT *comp;
4073 component = MSI_RecordGetString( row, 4 );
4074 comp = msi_get_loaded_component( package, component );
4075 if (!comp)
4076 return ERROR_SUCCESS;
4078 comp->Action = msi_get_component_action( package, comp );
4079 if (comp->Action != INSTALLSTATE_ABSENT)
4081 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4082 return ERROR_SUCCESS;
4084 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
4086 link_file = get_link_file( package, row );
4088 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4089 if (!DeleteFileW( link_file ))
4091 WARN("Failed to remove shortcut file %u\n", GetLastError());
4093 msi_free( link_file );
4095 return ERROR_SUCCESS;
4098 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4100 static const WCHAR query[] = {
4101 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4102 '`','S','h','o','r','t','c','u','t','`',0};
4103 MSIQUERY *view;
4104 UINT rc;
4106 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4107 if (rc != ERROR_SUCCESS)
4108 return ERROR_SUCCESS;
4110 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4111 msiobj_release( &view->hdr );
4112 return rc;
4115 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4117 MSIPACKAGE* package = param;
4118 HANDLE the_file;
4119 LPWSTR FilePath;
4120 LPCWSTR FileName;
4121 CHAR buffer[1024];
4122 DWORD sz;
4123 UINT rc;
4125 FileName = MSI_RecordGetString(row,1);
4126 if (!FileName)
4128 ERR("Unable to get FileName\n");
4129 return ERROR_SUCCESS;
4132 FilePath = msi_build_icon_path(package, FileName);
4134 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4136 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4137 FILE_ATTRIBUTE_NORMAL, NULL);
4139 if (the_file == INVALID_HANDLE_VALUE)
4141 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4142 msi_free(FilePath);
4143 return ERROR_SUCCESS;
4148 DWORD write;
4149 sz = 1024;
4150 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4151 if (rc != ERROR_SUCCESS)
4153 ERR("Failed to get stream\n");
4154 DeleteFileW(FilePath);
4155 break;
4157 WriteFile(the_file,buffer,sz,&write,NULL);
4158 } while (sz == 1024);
4160 msi_free(FilePath);
4161 CloseHandle(the_file);
4163 return ERROR_SUCCESS;
4166 static UINT msi_publish_icons(MSIPACKAGE *package)
4168 static const WCHAR query[]= {
4169 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4170 '`','I','c','o','n','`',0};
4171 MSIQUERY *view;
4172 UINT r;
4174 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4175 if (r == ERROR_SUCCESS)
4177 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4178 msiobj_release(&view->hdr);
4179 if (r != ERROR_SUCCESS)
4180 return r;
4182 return ERROR_SUCCESS;
4185 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4187 UINT r;
4188 HKEY source;
4189 LPWSTR buffer;
4190 MSIMEDIADISK *disk;
4191 MSISOURCELISTINFO *info;
4193 r = RegCreateKeyW(hkey, szSourceList, &source);
4194 if (r != ERROR_SUCCESS)
4195 return r;
4197 RegCloseKey(source);
4199 buffer = strrchrW(package->PackagePath, '\\') + 1;
4200 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4201 package->Context, MSICODE_PRODUCT,
4202 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4203 if (r != ERROR_SUCCESS)
4204 return r;
4206 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4207 package->Context, MSICODE_PRODUCT,
4208 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4209 if (r != ERROR_SUCCESS)
4210 return r;
4212 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4213 package->Context, MSICODE_PRODUCT,
4214 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4215 if (r != ERROR_SUCCESS)
4216 return r;
4218 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4220 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4221 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4222 info->options, info->value);
4223 else
4224 MsiSourceListSetInfoW(package->ProductCode, NULL,
4225 info->context, info->options,
4226 info->property, info->value);
4229 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4231 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4232 disk->context, disk->options,
4233 disk->disk_id, disk->volume_label, disk->disk_prompt);
4236 return ERROR_SUCCESS;
4239 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4241 static const WCHAR szARPProductIcon[] =
4242 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4243 static const WCHAR szAssignment[] =
4244 {'A','s','s','i','g','n','m','e','n','t',0};
4245 static const WCHAR szAdvertiseFlags[] =
4246 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4247 static const WCHAR szClients[] =
4248 {'C','l','i','e','n','t','s',0};
4249 static const WCHAR szColon[] = {':',0};
4250 MSIHANDLE hdb, suminfo;
4251 WCHAR *buffer, *ptr, guids[MAX_PATH], packcode[SQUASHED_GUID_SIZE];
4252 DWORD langid, size;
4253 UINT r;
4255 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4256 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4257 msi_free(buffer);
4259 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4260 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4262 /* FIXME */
4263 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4265 buffer = msi_dup_property(package->db, szARPProductIcon);
4266 if (buffer)
4268 LPWSTR path = msi_build_icon_path(package, buffer);
4269 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4270 msi_free(path);
4271 msi_free(buffer);
4274 buffer = msi_dup_property(package->db, szProductVersion);
4275 if (buffer)
4277 DWORD verdword = msi_version_str_to_dword(buffer);
4278 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4279 msi_free(buffer);
4282 msi_reg_set_val_dword(hkey, szAssignment, 0);
4283 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4284 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4285 msi_reg_set_val_str(hkey, szClients, szColon);
4287 hdb = alloc_msihandle(&package->db->hdr);
4288 if (!hdb)
4289 return ERROR_NOT_ENOUGH_MEMORY;
4291 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4292 MsiCloseHandle(hdb);
4293 if (r != ERROR_SUCCESS)
4294 goto done;
4296 size = MAX_PATH;
4297 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4298 NULL, guids, &size);
4299 if (r != ERROR_SUCCESS)
4300 goto done;
4302 ptr = strchrW(guids, ';');
4303 if (ptr) *ptr = 0;
4304 squash_guid(guids, packcode);
4305 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4307 done:
4308 MsiCloseHandle(suminfo);
4309 return ERROR_SUCCESS;
4312 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4314 UINT r;
4315 HKEY hkey;
4316 WCHAR *upgrade, squashed_pc[SQUASHED_GUID_SIZE];
4318 upgrade = msi_dup_property(package->db, szUpgradeCode);
4319 if (!upgrade)
4320 return ERROR_SUCCESS;
4322 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4323 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4324 else
4325 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4327 if (r != ERROR_SUCCESS)
4329 WARN("failed to open upgrade code key\n");
4330 msi_free(upgrade);
4331 return ERROR_SUCCESS;
4333 squash_guid(package->ProductCode, squashed_pc);
4334 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4335 RegCloseKey(hkey);
4336 msi_free(upgrade);
4337 return ERROR_SUCCESS;
4340 static BOOL msi_check_publish(MSIPACKAGE *package)
4342 MSIFEATURE *feature;
4344 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4346 feature->Action = msi_get_feature_action( package, feature );
4347 if (feature->Action == INSTALLSTATE_LOCAL || feature->Action == INSTALLSTATE_SOURCE)
4348 return TRUE;
4351 return FALSE;
4354 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4356 MSIFEATURE *feature;
4358 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4360 feature->Action = msi_get_feature_action( package, feature );
4361 if (feature->Action != INSTALLSTATE_ABSENT)
4362 return FALSE;
4365 return TRUE;
4368 static UINT msi_publish_patches( MSIPACKAGE *package )
4370 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4371 WCHAR patch_squashed[GUID_SIZE];
4372 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4373 LONG res;
4374 MSIPATCHINFO *patch;
4375 UINT r;
4376 WCHAR *p, *all_patches = NULL;
4377 DWORD len = 0;
4379 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4380 if (r != ERROR_SUCCESS)
4381 return ERROR_FUNCTION_FAILED;
4383 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4384 if (res != ERROR_SUCCESS)
4386 r = ERROR_FUNCTION_FAILED;
4387 goto done;
4390 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4391 if (r != ERROR_SUCCESS)
4392 goto done;
4394 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4396 squash_guid( patch->patchcode, patch_squashed );
4397 len += strlenW( patch_squashed ) + 1;
4400 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4401 if (!all_patches)
4402 goto done;
4404 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4406 HKEY patch_key;
4408 squash_guid( patch->patchcode, p );
4409 p += strlenW( p ) + 1;
4411 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4412 (const BYTE *)patch->transforms,
4413 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4414 if (res != ERROR_SUCCESS)
4415 goto done;
4417 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4418 if (r != ERROR_SUCCESS)
4419 goto done;
4421 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4422 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4423 RegCloseKey( patch_key );
4424 if (res != ERROR_SUCCESS)
4425 goto done;
4427 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4429 res = GetLastError();
4430 ERR("Unable to copy patch package %d\n", res);
4431 goto done;
4433 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4434 if (res != ERROR_SUCCESS)
4435 goto done;
4437 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state,
4438 sizeof(patch->state) );
4439 if (res != ERROR_SUCCESS)
4441 RegCloseKey( patch_key );
4442 goto done;
4445 res = RegSetValueExW( patch_key, szUninstallable, 0, REG_DWORD, (const BYTE *)&patch->uninstallable,
4446 sizeof(patch->uninstallable) );
4447 RegCloseKey( patch_key );
4448 if (res != ERROR_SUCCESS)
4449 goto done;
4452 all_patches[len] = 0;
4453 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4454 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4455 if (res != ERROR_SUCCESS)
4456 goto done;
4458 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4459 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4460 if (res != ERROR_SUCCESS)
4461 r = ERROR_FUNCTION_FAILED;
4463 done:
4464 RegCloseKey( product_patches_key );
4465 RegCloseKey( patches_key );
4466 RegCloseKey( product_key );
4467 msi_free( all_patches );
4468 return r;
4471 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4473 UINT rc;
4474 HKEY hukey = NULL, hudkey = NULL;
4475 MSIRECORD *uirow;
4477 if (!list_empty(&package->patches))
4479 rc = msi_publish_patches(package);
4480 if (rc != ERROR_SUCCESS)
4481 goto end;
4484 /* FIXME: also need to publish if the product is in advertise mode */
4485 if (!msi_check_publish(package))
4486 return ERROR_SUCCESS;
4488 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4489 &hukey, TRUE);
4490 if (rc != ERROR_SUCCESS)
4491 goto end;
4493 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4494 NULL, &hudkey, TRUE);
4495 if (rc != ERROR_SUCCESS)
4496 goto end;
4498 rc = msi_publish_upgrade_code(package);
4499 if (rc != ERROR_SUCCESS)
4500 goto end;
4502 rc = msi_publish_product_properties(package, hukey);
4503 if (rc != ERROR_SUCCESS)
4504 goto end;
4506 rc = msi_publish_sourcelist(package, hukey);
4507 if (rc != ERROR_SUCCESS)
4508 goto end;
4510 rc = msi_publish_icons(package);
4512 end:
4513 uirow = MSI_CreateRecord( 1 );
4514 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4515 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4516 msiobj_release( &uirow->hdr );
4518 RegCloseKey(hukey);
4519 RegCloseKey(hudkey);
4520 return rc;
4523 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4525 WCHAR *filename, *ptr, *folder, *ret;
4526 const WCHAR *dirprop;
4528 filename = msi_dup_record_field( row, 2 );
4529 if (filename && (ptr = strchrW( filename, '|' )))
4530 ptr++;
4531 else
4532 ptr = filename;
4534 dirprop = MSI_RecordGetString( row, 3 );
4535 if (dirprop)
4537 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4538 if (!folder) folder = msi_dup_property( package->db, dirprop );
4540 else
4541 folder = msi_dup_property( package->db, szWindowsFolder );
4543 if (!folder)
4545 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4546 msi_free( filename );
4547 return NULL;
4550 ret = msi_build_directory_name( 2, folder, ptr );
4552 msi_free( filename );
4553 msi_free( folder );
4554 return ret;
4557 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4559 MSIPACKAGE *package = param;
4560 LPCWSTR component, section, key, value, identifier;
4561 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4562 MSIRECORD * uirow;
4563 INT action;
4564 MSICOMPONENT *comp;
4566 component = MSI_RecordGetString(row, 8);
4567 comp = msi_get_loaded_component(package,component);
4568 if (!comp)
4569 return ERROR_SUCCESS;
4571 comp->Action = msi_get_component_action( package, comp );
4572 if (comp->Action != INSTALLSTATE_LOCAL)
4574 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4575 return ERROR_SUCCESS;
4578 identifier = MSI_RecordGetString(row,1);
4579 section = MSI_RecordGetString(row,4);
4580 key = MSI_RecordGetString(row,5);
4581 value = MSI_RecordGetString(row,6);
4582 action = MSI_RecordGetInteger(row,7);
4584 deformat_string(package,section,&deformated_section);
4585 deformat_string(package,key,&deformated_key);
4586 deformat_string(package,value,&deformated_value);
4588 fullname = get_ini_file_name(package, row);
4590 if (action == 0)
4592 TRACE("Adding value %s to section %s in %s\n",
4593 debugstr_w(deformated_key), debugstr_w(deformated_section),
4594 debugstr_w(fullname));
4595 WritePrivateProfileStringW(deformated_section, deformated_key,
4596 deformated_value, fullname);
4598 else if (action == 1)
4600 WCHAR returned[10];
4601 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4602 returned, 10, fullname);
4603 if (returned[0] == 0)
4605 TRACE("Adding value %s to section %s in %s\n",
4606 debugstr_w(deformated_key), debugstr_w(deformated_section),
4607 debugstr_w(fullname));
4609 WritePrivateProfileStringW(deformated_section, deformated_key,
4610 deformated_value, fullname);
4613 else if (action == 3)
4614 FIXME("Append to existing section not yet implemented\n");
4616 uirow = MSI_CreateRecord(4);
4617 MSI_RecordSetStringW(uirow,1,identifier);
4618 MSI_RecordSetStringW(uirow,2,deformated_section);
4619 MSI_RecordSetStringW(uirow,3,deformated_key);
4620 MSI_RecordSetStringW(uirow,4,deformated_value);
4621 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4622 msiobj_release( &uirow->hdr );
4624 msi_free(fullname);
4625 msi_free(deformated_key);
4626 msi_free(deformated_value);
4627 msi_free(deformated_section);
4628 return ERROR_SUCCESS;
4631 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4633 static const WCHAR query[] = {
4634 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4635 '`','I','n','i','F','i','l','e','`',0};
4636 MSIQUERY *view;
4637 UINT rc;
4639 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4640 if (rc != ERROR_SUCCESS)
4641 return ERROR_SUCCESS;
4643 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4644 msiobj_release(&view->hdr);
4645 return rc;
4648 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4650 MSIPACKAGE *package = param;
4651 LPCWSTR component, section, key, value, identifier;
4652 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4653 MSICOMPONENT *comp;
4654 MSIRECORD *uirow;
4655 INT action;
4657 component = MSI_RecordGetString( row, 8 );
4658 comp = msi_get_loaded_component( package, component );
4659 if (!comp)
4660 return ERROR_SUCCESS;
4662 comp->Action = msi_get_component_action( package, comp );
4663 if (comp->Action != INSTALLSTATE_ABSENT)
4665 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4666 return ERROR_SUCCESS;
4669 identifier = MSI_RecordGetString( row, 1 );
4670 section = MSI_RecordGetString( row, 4 );
4671 key = MSI_RecordGetString( row, 5 );
4672 value = MSI_RecordGetString( row, 6 );
4673 action = MSI_RecordGetInteger( row, 7 );
4675 deformat_string( package, section, &deformated_section );
4676 deformat_string( package, key, &deformated_key );
4677 deformat_string( package, value, &deformated_value );
4679 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4681 filename = get_ini_file_name( package, row );
4683 TRACE("Removing key %s from section %s in %s\n",
4684 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4686 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4688 WARN("Unable to remove key %u\n", GetLastError());
4690 msi_free( filename );
4692 else
4693 FIXME("Unsupported action %d\n", action);
4696 uirow = MSI_CreateRecord( 4 );
4697 MSI_RecordSetStringW( uirow, 1, identifier );
4698 MSI_RecordSetStringW( uirow, 2, deformated_section );
4699 MSI_RecordSetStringW( uirow, 3, deformated_key );
4700 MSI_RecordSetStringW( uirow, 4, deformated_value );
4701 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4702 msiobj_release( &uirow->hdr );
4704 msi_free( deformated_key );
4705 msi_free( deformated_value );
4706 msi_free( deformated_section );
4707 return ERROR_SUCCESS;
4710 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4712 MSIPACKAGE *package = param;
4713 LPCWSTR component, section, key, value, identifier;
4714 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4715 MSICOMPONENT *comp;
4716 MSIRECORD *uirow;
4717 INT action;
4719 component = MSI_RecordGetString( row, 8 );
4720 comp = msi_get_loaded_component( package, component );
4721 if (!comp)
4722 return ERROR_SUCCESS;
4724 comp->Action = msi_get_component_action( package, comp );
4725 if (comp->Action != INSTALLSTATE_LOCAL)
4727 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4728 return ERROR_SUCCESS;
4731 identifier = MSI_RecordGetString( row, 1 );
4732 section = MSI_RecordGetString( row, 4 );
4733 key = MSI_RecordGetString( row, 5 );
4734 value = MSI_RecordGetString( row, 6 );
4735 action = MSI_RecordGetInteger( row, 7 );
4737 deformat_string( package, section, &deformated_section );
4738 deformat_string( package, key, &deformated_key );
4739 deformat_string( package, value, &deformated_value );
4741 if (action == msidbIniFileActionRemoveLine)
4743 filename = get_ini_file_name( package, row );
4745 TRACE("Removing key %s from section %s in %s\n",
4746 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4748 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4750 WARN("Unable to remove key %u\n", GetLastError());
4752 msi_free( filename );
4754 else
4755 FIXME("Unsupported action %d\n", action);
4757 uirow = MSI_CreateRecord( 4 );
4758 MSI_RecordSetStringW( uirow, 1, identifier );
4759 MSI_RecordSetStringW( uirow, 2, deformated_section );
4760 MSI_RecordSetStringW( uirow, 3, deformated_key );
4761 MSI_RecordSetStringW( uirow, 4, deformated_value );
4762 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4763 msiobj_release( &uirow->hdr );
4765 msi_free( deformated_key );
4766 msi_free( deformated_value );
4767 msi_free( deformated_section );
4768 return ERROR_SUCCESS;
4771 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4773 static const WCHAR query[] = {
4774 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4775 '`','I','n','i','F','i','l','e','`',0};
4776 static const WCHAR remove_query[] = {
4777 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4778 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4779 MSIQUERY *view;
4780 UINT rc;
4782 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4783 if (rc == ERROR_SUCCESS)
4785 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4786 msiobj_release( &view->hdr );
4787 if (rc != ERROR_SUCCESS)
4788 return rc;
4790 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4791 if (rc == ERROR_SUCCESS)
4793 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4794 msiobj_release( &view->hdr );
4795 if (rc != ERROR_SUCCESS)
4796 return rc;
4798 return ERROR_SUCCESS;
4801 static void register_dll( const WCHAR *dll, BOOL unregister )
4803 static const WCHAR regW[] =
4804 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4805 static const WCHAR unregW[] =
4806 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4807 PROCESS_INFORMATION pi;
4808 STARTUPINFOW si;
4809 WCHAR *cmd;
4811 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4813 if (unregister) sprintfW( cmd, unregW, dll );
4814 else sprintfW( cmd, regW, dll );
4816 memset( &si, 0, sizeof(STARTUPINFOW) );
4817 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4819 CloseHandle( pi.hThread );
4820 msi_dialog_check_messages( pi.hProcess );
4821 CloseHandle( pi.hProcess );
4823 msi_free( cmd );
4826 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4828 MSIPACKAGE *package = param;
4829 LPCWSTR filename;
4830 MSIFILE *file;
4831 MSIRECORD *uirow;
4833 filename = MSI_RecordGetString( row, 1 );
4834 file = msi_get_loaded_file( package, filename );
4835 if (!file)
4837 WARN("unable to find file %s\n", debugstr_w(filename));
4838 return ERROR_SUCCESS;
4840 file->Component->Action = msi_get_component_action( package, file->Component );
4841 if (file->Component->Action != INSTALLSTATE_LOCAL)
4843 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4844 return ERROR_SUCCESS;
4847 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4848 register_dll( file->TargetPath, FALSE );
4850 uirow = MSI_CreateRecord( 2 );
4851 MSI_RecordSetStringW( uirow, 1, file->File );
4852 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4853 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4854 msiobj_release( &uirow->hdr );
4856 return ERROR_SUCCESS;
4859 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4861 static const WCHAR query[] = {
4862 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4863 '`','S','e','l','f','R','e','g','`',0};
4864 MSIQUERY *view;
4865 UINT rc;
4867 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4868 if (rc != ERROR_SUCCESS)
4869 return ERROR_SUCCESS;
4871 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4872 msiobj_release(&view->hdr);
4873 return rc;
4876 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4878 MSIPACKAGE *package = param;
4879 LPCWSTR filename;
4880 MSIFILE *file;
4881 MSIRECORD *uirow;
4883 filename = MSI_RecordGetString( row, 1 );
4884 file = msi_get_loaded_file( package, filename );
4885 if (!file)
4887 WARN("unable to find file %s\n", debugstr_w(filename));
4888 return ERROR_SUCCESS;
4890 file->Component->Action = msi_get_component_action( package, file->Component );
4891 if (file->Component->Action != INSTALLSTATE_ABSENT)
4893 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4894 return ERROR_SUCCESS;
4897 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4898 register_dll( file->TargetPath, TRUE );
4900 uirow = MSI_CreateRecord( 2 );
4901 MSI_RecordSetStringW( uirow, 1, file->File );
4902 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4903 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4904 msiobj_release( &uirow->hdr );
4906 return ERROR_SUCCESS;
4909 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4911 static const WCHAR query[] = {
4912 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4913 '`','S','e','l','f','R','e','g','`',0};
4914 MSIQUERY *view;
4915 UINT rc;
4917 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4918 if (rc != ERROR_SUCCESS)
4919 return ERROR_SUCCESS;
4921 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4922 msiobj_release( &view->hdr );
4923 return rc;
4926 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4928 MSIFEATURE *feature;
4929 UINT rc;
4930 HKEY hkey = NULL, userdata = NULL;
4932 if (!msi_check_publish(package))
4933 return ERROR_SUCCESS;
4935 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4936 &hkey, TRUE);
4937 if (rc != ERROR_SUCCESS)
4938 goto end;
4940 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4941 &userdata, TRUE);
4942 if (rc != ERROR_SUCCESS)
4943 goto end;
4945 /* here the guids are base 85 encoded */
4946 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4948 ComponentList *cl;
4949 LPWSTR data = NULL;
4950 GUID clsid;
4951 INT size;
4952 BOOL absent = FALSE;
4953 MSIRECORD *uirow;
4955 if (feature->Level <= 0) continue;
4956 if (feature->Action == INSTALLSTATE_UNKNOWN &&
4957 feature->Installed != INSTALLSTATE_ABSENT) continue;
4959 if (feature->Action != INSTALLSTATE_LOCAL &&
4960 feature->Action != INSTALLSTATE_SOURCE &&
4961 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4963 size = 1;
4964 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4966 size += 21;
4968 if (feature->Feature_Parent)
4969 size += strlenW( feature->Feature_Parent )+2;
4971 data = msi_alloc(size * sizeof(WCHAR));
4973 data[0] = 0;
4974 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4976 MSICOMPONENT* component = cl->component;
4977 WCHAR buf[21];
4979 buf[0] = 0;
4980 if (component->ComponentId)
4982 TRACE("From %s\n",debugstr_w(component->ComponentId));
4983 CLSIDFromString(component->ComponentId, &clsid);
4984 encode_base85_guid(&clsid,buf);
4985 TRACE("to %s\n",debugstr_w(buf));
4986 strcatW(data,buf);
4990 if (feature->Feature_Parent)
4992 static const WCHAR sep[] = {'\2',0};
4993 strcatW(data,sep);
4994 strcatW(data,feature->Feature_Parent);
4997 msi_reg_set_val_str( userdata, feature->Feature, data );
4998 msi_free(data);
5000 size = 0;
5001 if (feature->Feature_Parent)
5002 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
5003 if (!absent)
5005 size += sizeof(WCHAR);
5006 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5007 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
5009 else
5011 size += 2*sizeof(WCHAR);
5012 data = msi_alloc(size);
5013 data[0] = 0x6;
5014 data[1] = 0;
5015 if (feature->Feature_Parent)
5016 strcpyW( &data[1], feature->Feature_Parent );
5017 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5018 (LPBYTE)data,size);
5019 msi_free(data);
5022 /* the UI chunk */
5023 uirow = MSI_CreateRecord( 1 );
5024 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5025 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5026 msiobj_release( &uirow->hdr );
5027 /* FIXME: call msi_ui_progress? */
5030 end:
5031 RegCloseKey(hkey);
5032 RegCloseKey(userdata);
5033 return rc;
5036 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
5038 UINT r;
5039 HKEY hkey;
5040 MSIRECORD *uirow;
5042 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
5044 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
5045 &hkey, FALSE);
5046 if (r == ERROR_SUCCESS)
5048 RegDeleteValueW(hkey, feature->Feature);
5049 RegCloseKey(hkey);
5052 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5053 &hkey, FALSE);
5054 if (r == ERROR_SUCCESS)
5056 RegDeleteValueW(hkey, feature->Feature);
5057 RegCloseKey(hkey);
5060 uirow = MSI_CreateRecord( 1 );
5061 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5062 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5063 msiobj_release( &uirow->hdr );
5065 return ERROR_SUCCESS;
5068 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5070 MSIFEATURE *feature;
5072 if (!msi_check_unpublish(package))
5073 return ERROR_SUCCESS;
5075 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5077 msi_unpublish_feature(package, feature);
5080 return ERROR_SUCCESS;
5083 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5085 SYSTEMTIME systime;
5086 DWORD size, langid;
5087 WCHAR date[9], *val, *buffer;
5088 const WCHAR *prop, *key;
5090 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5091 static const WCHAR modpath_fmt[] =
5092 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5093 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5094 static const WCHAR szModifyPath[] =
5095 {'M','o','d','i','f','y','P','a','t','h',0};
5096 static const WCHAR szUninstallString[] =
5097 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5098 static const WCHAR szEstimatedSize[] =
5099 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5100 static const WCHAR szDisplayVersion[] =
5101 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5102 static const WCHAR szInstallSource[] =
5103 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5104 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5105 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5106 static const WCHAR szAuthorizedCDFPrefix[] =
5107 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5108 static const WCHAR szARPCONTACT[] =
5109 {'A','R','P','C','O','N','T','A','C','T',0};
5110 static const WCHAR szContact[] =
5111 {'C','o','n','t','a','c','t',0};
5112 static const WCHAR szARPCOMMENTS[] =
5113 {'A','R','P','C','O','M','M','E','N','T','S',0};
5114 static const WCHAR szComments[] =
5115 {'C','o','m','m','e','n','t','s',0};
5116 static const WCHAR szProductName[] =
5117 {'P','r','o','d','u','c','t','N','a','m','e',0};
5118 static const WCHAR szDisplayName[] =
5119 {'D','i','s','p','l','a','y','N','a','m','e',0};
5120 static const WCHAR szARPHELPLINK[] =
5121 {'A','R','P','H','E','L','P','L','I','N','K',0};
5122 static const WCHAR szHelpLink[] =
5123 {'H','e','l','p','L','i','n','k',0};
5124 static const WCHAR szARPHELPTELEPHONE[] =
5125 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5126 static const WCHAR szHelpTelephone[] =
5127 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5128 static const WCHAR szARPINSTALLLOCATION[] =
5129 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5130 static const WCHAR szManufacturer[] =
5131 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5132 static const WCHAR szPublisher[] =
5133 {'P','u','b','l','i','s','h','e','r',0};
5134 static const WCHAR szARPREADME[] =
5135 {'A','R','P','R','E','A','D','M','E',0};
5136 static const WCHAR szReadme[] =
5137 {'R','e','a','d','M','e',0};
5138 static const WCHAR szARPSIZE[] =
5139 {'A','R','P','S','I','Z','E',0};
5140 static const WCHAR szSize[] =
5141 {'S','i','z','e',0};
5142 static const WCHAR szARPURLINFOABOUT[] =
5143 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5144 static const WCHAR szURLInfoAbout[] =
5145 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5146 static const WCHAR szARPURLUPDATEINFO[] =
5147 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5148 static const WCHAR szURLUpdateInfo[] =
5149 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5150 static const WCHAR szARPSYSTEMCOMPONENT[] =
5151 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5152 static const WCHAR szSystemComponent[] =
5153 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5155 static const WCHAR *propval[] = {
5156 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5157 szARPCONTACT, szContact,
5158 szARPCOMMENTS, szComments,
5159 szProductName, szDisplayName,
5160 szARPHELPLINK, szHelpLink,
5161 szARPHELPTELEPHONE, szHelpTelephone,
5162 szARPINSTALLLOCATION, szInstallLocation,
5163 szSourceDir, szInstallSource,
5164 szManufacturer, szPublisher,
5165 szARPREADME, szReadme,
5166 szARPSIZE, szSize,
5167 szARPURLINFOABOUT, szURLInfoAbout,
5168 szARPURLUPDATEINFO, szURLUpdateInfo,
5169 NULL
5171 const WCHAR **p = propval;
5173 while (*p)
5175 prop = *p++;
5176 key = *p++;
5177 val = msi_dup_property(package->db, prop);
5178 msi_reg_set_val_str(hkey, key, val);
5179 msi_free(val);
5182 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5183 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5185 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5187 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5188 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5189 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5190 msi_free(buffer);
5192 /* FIXME: Write real Estimated Size when we have it */
5193 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5195 GetLocalTime(&systime);
5196 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5197 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5199 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5200 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5202 buffer = msi_dup_property(package->db, szProductVersion);
5203 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5204 if (buffer)
5206 DWORD verdword = msi_version_str_to_dword(buffer);
5208 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5209 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5210 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5211 msi_free(buffer);
5214 return ERROR_SUCCESS;
5217 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5219 WCHAR *upgrade_code, squashed_pc[SQUASHED_GUID_SIZE];
5220 MSIRECORD *uirow;
5221 HKEY hkey, props, upgrade_key;
5222 UINT rc;
5224 /* FIXME: also need to publish if the product is in advertise mode */
5225 if (!msi_get_property_int( package->db, szProductToBeRegistered, 0 )
5226 && !msi_check_publish(package))
5227 return ERROR_SUCCESS;
5229 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5230 if (rc != ERROR_SUCCESS)
5231 return rc;
5233 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5234 if (rc != ERROR_SUCCESS)
5235 goto done;
5237 rc = msi_publish_install_properties(package, hkey);
5238 if (rc != ERROR_SUCCESS)
5239 goto done;
5241 rc = msi_publish_install_properties(package, props);
5242 if (rc != ERROR_SUCCESS)
5243 goto done;
5245 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5246 if (upgrade_code)
5248 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5249 if (rc == ERROR_SUCCESS)
5251 squash_guid( package->ProductCode, squashed_pc );
5252 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5253 RegCloseKey( upgrade_key );
5255 msi_free( upgrade_code );
5257 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5258 package->delete_on_close = FALSE;
5260 done:
5261 uirow = MSI_CreateRecord( 1 );
5262 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5263 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5264 msiobj_release( &uirow->hdr );
5266 RegCloseKey(hkey);
5267 return ERROR_SUCCESS;
5270 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5272 return execute_script(package, SCRIPT_INSTALL);
5275 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5277 MSIPACKAGE *package = param;
5278 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5279 WCHAR *p, *icon_path;
5281 if (!icon) return ERROR_SUCCESS;
5282 if ((icon_path = msi_build_icon_path( package, icon )))
5284 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5285 DeleteFileW( icon_path );
5286 if ((p = strrchrW( icon_path, '\\' )))
5288 *p = 0;
5289 RemoveDirectoryW( icon_path );
5291 msi_free( icon_path );
5293 return ERROR_SUCCESS;
5296 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5298 static const WCHAR query[]= {
5299 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5300 MSIQUERY *view;
5301 UINT r;
5303 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5304 if (r == ERROR_SUCCESS)
5306 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5307 msiobj_release( &view->hdr );
5308 if (r != ERROR_SUCCESS)
5309 return r;
5311 return ERROR_SUCCESS;
5314 static void remove_product_upgrade_code( MSIPACKAGE *package )
5316 WCHAR *code, product[SQUASHED_GUID_SIZE];
5317 HKEY hkey;
5318 LONG res;
5319 DWORD count;
5321 squash_guid( package->ProductCode, product );
5322 if (!(code = msi_dup_property( package->db, szUpgradeCode )))
5324 WARN( "upgrade code not found\n" );
5325 return;
5327 if (!MSIREG_OpenUpgradeCodesKey( code, &hkey, FALSE ))
5329 RegDeleteValueW( hkey, product );
5330 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5331 RegCloseKey( hkey );
5332 if (!res && !count) MSIREG_DeleteUpgradeCodesKey( code );
5334 if (!MSIREG_OpenUserUpgradeCodesKey( code, &hkey, FALSE ))
5336 RegDeleteValueW( hkey, product );
5337 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5338 RegCloseKey( hkey );
5339 if (!res && !count) MSIREG_DeleteUserUpgradeCodesKey( code );
5341 if (!MSIREG_OpenClassesUpgradeCodesKey( code, &hkey, FALSE ))
5343 RegDeleteValueW( hkey, product );
5344 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5345 RegCloseKey( hkey );
5346 if (!res && !count) MSIREG_DeleteClassesUpgradeCodesKey( code );
5349 msi_free( code );
5352 static UINT ACTION_UnpublishProduct(MSIPACKAGE *package)
5354 MSIPATCHINFO *patch;
5356 MSIREG_DeleteProductKey(package->ProductCode);
5357 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5358 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5360 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5361 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5362 MSIREG_DeleteUserProductKey(package->ProductCode);
5363 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5365 remove_product_upgrade_code( package );
5367 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5369 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5370 if (!strcmpW( package->ProductCode, patch->products ))
5372 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5373 patch->delete_on_close = TRUE;
5375 /* FIXME: remove local patch package if this is the last product */
5377 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5378 package->delete_on_close = TRUE;
5380 msi_unpublish_icons( package );
5381 return ERROR_SUCCESS;
5384 static BOOL is_full_uninstall( MSIPACKAGE *package )
5386 MSIFEATURE *feature;
5388 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5390 if (feature->Action != INSTALLSTATE_ABSENT &&
5391 (feature->Installed != INSTALLSTATE_ABSENT || feature->Action != INSTALLSTATE_UNKNOWN))
5392 return FALSE;
5395 return TRUE;
5398 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5400 UINT rc;
5402 /* first do the same as an InstallExecute */
5403 rc = execute_script(package, SCRIPT_INSTALL);
5404 if (rc != ERROR_SUCCESS)
5405 return rc;
5407 /* then handle commit actions */
5408 rc = execute_script(package, SCRIPT_COMMIT);
5409 if (rc != ERROR_SUCCESS)
5410 return rc;
5412 if (is_full_uninstall(package))
5413 rc = ACTION_UnpublishProduct(package);
5415 return rc;
5418 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5420 static const WCHAR RunOnce[] = {
5421 'S','o','f','t','w','a','r','e','\\',
5422 'M','i','c','r','o','s','o','f','t','\\',
5423 'W','i','n','d','o','w','s','\\',
5424 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5425 'R','u','n','O','n','c','e',0};
5426 static const WCHAR InstallRunOnce[] = {
5427 'S','o','f','t','w','a','r','e','\\',
5428 'M','i','c','r','o','s','o','f','t','\\',
5429 'W','i','n','d','o','w','s','\\',
5430 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5431 'I','n','s','t','a','l','l','e','r','\\',
5432 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5434 static const WCHAR msiexec_fmt[] = {
5435 '%','s',
5436 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5437 '\"','%','s','\"',0};
5438 static const WCHAR install_fmt[] = {
5439 '/','I',' ','\"','%','s','\"',' ',
5440 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5441 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5442 WCHAR buffer[256], sysdir[MAX_PATH], squashed_pc[SQUASHED_GUID_SIZE];
5443 HKEY hkey;
5445 squash_guid( package->ProductCode, squashed_pc );
5447 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5448 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5449 snprintfW( buffer, sizeof(buffer)/sizeof(buffer[0]), msiexec_fmt, sysdir, squashed_pc );
5451 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5452 RegCloseKey(hkey);
5454 TRACE("Reboot command %s\n",debugstr_w(buffer));
5456 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5457 sprintfW( buffer, install_fmt, package->ProductCode, squashed_pc );
5459 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5460 RegCloseKey(hkey);
5462 return ERROR_INSTALL_SUSPEND;
5465 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5467 DWORD attrib;
5468 UINT rc;
5471 * We are currently doing what should be done here in the top level Install
5472 * however for Administrative and uninstalls this step will be needed
5474 if (!package->PackagePath)
5475 return ERROR_SUCCESS;
5477 msi_set_sourcedir_props(package, TRUE);
5479 attrib = GetFileAttributesW(package->db->path);
5480 if (attrib == INVALID_FILE_ATTRIBUTES)
5482 MSIRECORD *record;
5483 LPWSTR prompt;
5484 DWORD size = 0;
5486 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5487 package->Context, MSICODE_PRODUCT,
5488 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5489 if (rc == ERROR_MORE_DATA)
5491 prompt = msi_alloc(size * sizeof(WCHAR));
5492 MsiSourceListGetInfoW(package->ProductCode, NULL,
5493 package->Context, MSICODE_PRODUCT,
5494 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5496 else
5497 prompt = strdupW(package->db->path);
5499 record = MSI_CreateRecord(2);
5500 MSI_RecordSetInteger(record, 1, MSIERR_INSERTDISK);
5501 MSI_RecordSetStringW(record, 2, prompt);
5502 msi_free(prompt);
5503 while(attrib == INVALID_FILE_ATTRIBUTES)
5505 MSI_RecordSetStringW(record, 0, NULL);
5506 rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ERROR, record);
5507 if (rc == IDCANCEL)
5508 return ERROR_INSTALL_USEREXIT;
5509 attrib = GetFileAttributesW(package->db->path);
5511 rc = ERROR_SUCCESS;
5513 else
5514 return ERROR_SUCCESS;
5516 return rc;
5519 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5521 HKEY hkey = 0;
5522 LPWSTR buffer, productid = NULL;
5523 UINT i, rc = ERROR_SUCCESS;
5524 MSIRECORD *uirow;
5526 static const WCHAR szPropKeys[][80] =
5528 {'P','r','o','d','u','c','t','I','D',0},
5529 {'U','S','E','R','N','A','M','E',0},
5530 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5531 {0},
5534 static const WCHAR szRegKeys[][80] =
5536 {'P','r','o','d','u','c','t','I','D',0},
5537 {'R','e','g','O','w','n','e','r',0},
5538 {'R','e','g','C','o','m','p','a','n','y',0},
5539 {0},
5542 if (msi_check_unpublish(package))
5544 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5545 goto end;
5548 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5549 if (!productid)
5550 goto end;
5552 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5553 NULL, &hkey, TRUE);
5554 if (rc != ERROR_SUCCESS)
5555 goto end;
5557 for( i = 0; szPropKeys[i][0]; i++ )
5559 buffer = msi_dup_property( package->db, szPropKeys[i] );
5560 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5561 msi_free( buffer );
5564 end:
5565 uirow = MSI_CreateRecord( 1 );
5566 MSI_RecordSetStringW( uirow, 1, productid );
5567 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5568 msiobj_release( &uirow->hdr );
5570 msi_free(productid);
5571 RegCloseKey(hkey);
5572 return rc;
5575 static UINT iterate_properties(MSIRECORD *record, void *param)
5577 static const WCHAR prop_template[] =
5578 {'P','r','o','p','e','r','t','y','(','S',')',':',' ','[','1',']',' ','=',' ','[','2',']',0};
5579 MSIRECORD *uirow;
5581 uirow = MSI_CloneRecord(record);
5582 if (!uirow) return ERROR_OUTOFMEMORY;
5583 MSI_RecordSetStringW(uirow, 0, prop_template);
5584 MSI_ProcessMessage(param, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow);
5585 msiobj_release(&uirow->hdr);
5587 return ERROR_SUCCESS;
5591 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5593 static const WCHAR prop_query[] =
5594 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','_','P','r','o','p','e','r','t','y','`',0};
5595 WCHAR *productname;
5596 WCHAR *action;
5597 WCHAR *info_template;
5598 MSIQUERY *view;
5599 MSIRECORD *uirow, *uirow_info;
5600 UINT rc;
5602 /* Send COMMONDATA and INFO messages. */
5603 /* FIXME: when should these messages be sent? [see also MsiOpenPackage()] */
5604 uirow = MSI_CreateRecord(3);
5605 if (!uirow) return ERROR_OUTOFMEMORY;
5606 MSI_RecordSetStringW(uirow, 0, NULL);
5607 MSI_RecordSetInteger(uirow, 1, 0);
5608 MSI_RecordSetInteger(uirow, 2, package->num_langids ? package->langids[0] : 0);
5609 MSI_RecordSetInteger(uirow, 3, msi_get_string_table_codepage(package->db->strings));
5610 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5611 /* FIXME: send INSTALLMESSAGE_PROGRESS */
5612 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5614 if (!(needs_ui_sequence(package) && ui_sequence_exists(package)))
5616 uirow_info = MSI_CreateRecord(0);
5617 if (!uirow_info)
5619 msiobj_release(&uirow->hdr);
5620 return ERROR_OUTOFMEMORY;
5622 info_template = msi_get_error_message(package->db, MSIERR_INFO_LOGGINGSTART);
5623 MSI_RecordSetStringW(uirow_info, 0, info_template);
5624 msi_free(info_template);
5625 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow_info);
5626 msiobj_release(&uirow_info->hdr);
5629 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5631 productname = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
5632 MSI_RecordSetInteger(uirow, 1, 1);
5633 MSI_RecordSetStringW(uirow, 2, productname);
5634 MSI_RecordSetStringW(uirow, 3, NULL);
5635 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5636 msiobj_release(&uirow->hdr);
5638 package->LastActionResult = MSI_NULL_INTEGER;
5640 action = msi_dup_property(package->db, szEXECUTEACTION);
5641 if (!action) action = msi_strdupW(szINSTALL, strlenW(szINSTALL));
5643 /* Perform the action. Top-level actions trigger a sequence. */
5644 if (!strcmpW(action, szINSTALL))
5646 /* Send ACTIONSTART/INFO and INSTALLSTART. */
5647 ui_actionstart(package, szINSTALL, NULL, NULL);
5648 ui_actioninfo(package, szINSTALL, TRUE, 0);
5649 uirow = MSI_CreateRecord(2);
5650 if (!uirow)
5652 rc = ERROR_OUTOFMEMORY;
5653 goto end;
5655 MSI_RecordSetStringW(uirow, 0, NULL);
5656 MSI_RecordSetStringW(uirow, 1, productname);
5657 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5658 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLSTART, uirow);
5659 msiobj_release(&uirow->hdr);
5661 /* Perform the installation. Always use the ExecuteSequence. */
5662 package->InWhatSequence |= SEQUENCE_EXEC;
5663 rc = ACTION_ProcessExecSequence(package);
5665 /* Send return value and INSTALLEND. */
5666 ui_actioninfo(package, szINSTALL, FALSE, !rc);
5667 uirow = MSI_CreateRecord(3);
5668 if (!uirow)
5670 rc = ERROR_OUTOFMEMORY;
5671 goto end;
5673 MSI_RecordSetStringW(uirow, 0, NULL);
5674 MSI_RecordSetStringW(uirow, 1, productname);
5675 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5676 MSI_RecordSetInteger(uirow, 3, !rc);
5677 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLEND, uirow);
5678 msiobj_release(&uirow->hdr);
5680 else
5681 rc = ACTION_PerformAction(package, action);
5683 /* Send all set properties. */
5684 if (!MSI_OpenQuery(package->db, &view, prop_query))
5686 MSI_IterateRecords(view, NULL, iterate_properties, package);
5687 msiobj_release(&view->hdr);
5690 /* And finally, toggle the cancel off and on. */
5691 uirow = MSI_CreateRecord(2);
5692 if (!uirow)
5694 rc = ERROR_OUTOFMEMORY;
5695 goto end;
5697 MSI_RecordSetStringW(uirow, 0, NULL);
5698 MSI_RecordSetInteger(uirow, 1, 2);
5699 MSI_RecordSetInteger(uirow, 2, 0);
5700 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5701 MSI_RecordSetInteger(uirow, 2, 1);
5702 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5703 msiobj_release(&uirow->hdr);
5705 end:
5706 msi_free(productname);
5707 msi_free(action);
5708 return rc;
5711 static UINT ACTION_INSTALL(MSIPACKAGE *package)
5713 msi_set_property(package->db, szEXECUTEACTION, szINSTALL, -1);
5714 if (needs_ui_sequence(package) && ui_sequence_exists(package))
5716 package->InWhatSequence |= SEQUENCE_UI;
5717 return ACTION_ProcessUISequence(package);
5719 else
5720 return ACTION_ExecuteAction(package);
5723 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5725 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5726 WCHAR productid_85[21], component_85[21], *ret;
5727 GUID clsid;
5728 DWORD sz;
5730 /* > is used if there is a component GUID and < if not. */
5732 productid_85[0] = 0;
5733 component_85[0] = 0;
5734 CLSIDFromString( package->ProductCode, &clsid );
5736 encode_base85_guid( &clsid, productid_85 );
5737 if (component)
5739 CLSIDFromString( component->ComponentId, &clsid );
5740 encode_base85_guid( &clsid, component_85 );
5743 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5744 debugstr_w(component_85));
5746 sz = 20 + strlenW( feature ) + 20 + 3;
5747 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5748 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5749 return ret;
5752 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5754 MSIPACKAGE *package = param;
5755 LPCWSTR compgroupid, component, feature, qualifier, text;
5756 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5757 HKEY hkey = NULL;
5758 UINT rc;
5759 MSICOMPONENT *comp;
5760 MSIFEATURE *feat;
5761 DWORD sz;
5762 MSIRECORD *uirow;
5763 int len;
5765 feature = MSI_RecordGetString(rec, 5);
5766 feat = msi_get_loaded_feature(package, feature);
5767 if (!feat)
5768 return ERROR_SUCCESS;
5770 feat->Action = msi_get_feature_action( package, feat );
5771 if (feat->Action != INSTALLSTATE_LOCAL &&
5772 feat->Action != INSTALLSTATE_SOURCE &&
5773 feat->Action != INSTALLSTATE_ADVERTISED)
5775 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5776 return ERROR_SUCCESS;
5779 component = MSI_RecordGetString(rec, 3);
5780 comp = msi_get_loaded_component(package, component);
5781 if (!comp)
5782 return ERROR_SUCCESS;
5784 compgroupid = MSI_RecordGetString(rec,1);
5785 qualifier = MSI_RecordGetString(rec,2);
5787 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5788 if (rc != ERROR_SUCCESS)
5789 goto end;
5791 advertise = msi_create_component_advertise_string( package, comp, feature );
5792 text = MSI_RecordGetString( rec, 4 );
5793 if (text)
5795 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5796 strcpyW( p, advertise );
5797 strcatW( p, text );
5798 msi_free( advertise );
5799 advertise = p;
5801 existing = msi_reg_get_val_str( hkey, qualifier );
5803 sz = strlenW( advertise ) + 1;
5804 if (existing)
5806 for (p = existing; *p; p += len)
5808 len = strlenW( p ) + 1;
5809 if (strcmpW( advertise, p )) sz += len;
5812 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5814 rc = ERROR_OUTOFMEMORY;
5815 goto end;
5817 q = output;
5818 if (existing)
5820 for (p = existing; *p; p += len)
5822 len = strlenW( p ) + 1;
5823 if (strcmpW( advertise, p ))
5825 memcpy( q, p, len * sizeof(WCHAR) );
5826 q += len;
5830 strcpyW( q, advertise );
5831 q[strlenW( q ) + 1] = 0;
5833 msi_reg_set_val_multi_str( hkey, qualifier, output );
5835 end:
5836 RegCloseKey(hkey);
5837 msi_free( output );
5838 msi_free( advertise );
5839 msi_free( existing );
5841 /* the UI chunk */
5842 uirow = MSI_CreateRecord( 2 );
5843 MSI_RecordSetStringW( uirow, 1, compgroupid );
5844 MSI_RecordSetStringW( uirow, 2, qualifier);
5845 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5846 msiobj_release( &uirow->hdr );
5847 /* FIXME: call ui_progress? */
5849 return rc;
5853 * At present I am ignorning the advertised components part of this and only
5854 * focusing on the qualified component sets
5856 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5858 static const WCHAR query[] = {
5859 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5860 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5861 MSIQUERY *view;
5862 UINT rc;
5864 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5865 if (rc != ERROR_SUCCESS)
5866 return ERROR_SUCCESS;
5868 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5869 msiobj_release(&view->hdr);
5870 return rc;
5873 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5875 static const WCHAR szInstallerComponents[] = {
5876 'S','o','f','t','w','a','r','e','\\',
5877 'M','i','c','r','o','s','o','f','t','\\',
5878 'I','n','s','t','a','l','l','e','r','\\',
5879 'C','o','m','p','o','n','e','n','t','s','\\',0};
5881 MSIPACKAGE *package = param;
5882 LPCWSTR compgroupid, component, feature, qualifier;
5883 MSICOMPONENT *comp;
5884 MSIFEATURE *feat;
5885 MSIRECORD *uirow;
5886 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5887 LONG res;
5889 feature = MSI_RecordGetString( rec, 5 );
5890 feat = msi_get_loaded_feature( package, feature );
5891 if (!feat)
5892 return ERROR_SUCCESS;
5894 feat->Action = msi_get_feature_action( package, feat );
5895 if (feat->Action != INSTALLSTATE_ABSENT)
5897 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5898 return ERROR_SUCCESS;
5901 component = MSI_RecordGetString( rec, 3 );
5902 comp = msi_get_loaded_component( package, component );
5903 if (!comp)
5904 return ERROR_SUCCESS;
5906 compgroupid = MSI_RecordGetString( rec, 1 );
5907 qualifier = MSI_RecordGetString( rec, 2 );
5909 squash_guid( compgroupid, squashed );
5910 strcpyW( keypath, szInstallerComponents );
5911 strcatW( keypath, squashed );
5913 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5914 if (res != ERROR_SUCCESS)
5916 WARN("Unable to delete component key %d\n", res);
5919 uirow = MSI_CreateRecord( 2 );
5920 MSI_RecordSetStringW( uirow, 1, compgroupid );
5921 MSI_RecordSetStringW( uirow, 2, qualifier );
5922 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5923 msiobj_release( &uirow->hdr );
5925 return ERROR_SUCCESS;
5928 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5930 static const WCHAR query[] = {
5931 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5932 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5933 MSIQUERY *view;
5934 UINT rc;
5936 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5937 if (rc != ERROR_SUCCESS)
5938 return ERROR_SUCCESS;
5940 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5941 msiobj_release( &view->hdr );
5942 return rc;
5945 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5947 static const WCHAR query[] =
5948 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5949 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5950 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5951 MSIPACKAGE *package = param;
5952 MSICOMPONENT *component;
5953 MSIRECORD *row;
5954 MSIFILE *file;
5955 SC_HANDLE hscm = NULL, service = NULL;
5956 LPCWSTR comp, key;
5957 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5958 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5959 DWORD serv_type, start_type, err_control;
5960 BOOL is_vital;
5961 SERVICE_DESCRIPTIONW sd = {NULL};
5962 UINT ret = ERROR_SUCCESS;
5964 comp = MSI_RecordGetString( rec, 12 );
5965 component = msi_get_loaded_component( package, comp );
5966 if (!component)
5968 WARN("service component not found\n");
5969 goto done;
5971 component->Action = msi_get_component_action( package, component );
5972 if (component->Action != INSTALLSTATE_LOCAL)
5974 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5975 goto done;
5977 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5978 if (!hscm)
5980 ERR("Failed to open the SC Manager!\n");
5981 goto done;
5984 start_type = MSI_RecordGetInteger(rec, 5);
5985 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5986 goto done;
5988 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5989 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5990 serv_type = MSI_RecordGetInteger(rec, 4);
5991 err_control = MSI_RecordGetInteger(rec, 6);
5992 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5993 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5994 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5995 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5996 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5997 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5999 /* Should the complete install fail if CreateService fails? */
6000 is_vital = (err_control & msidbServiceInstallErrorControlVital);
6002 /* Remove the msidbServiceInstallErrorControlVital-flag from err_control.
6003 CreateService (under Windows) would fail if not. */
6004 err_control &= ~msidbServiceInstallErrorControlVital;
6006 /* fetch the service path */
6007 row = MSI_QueryGetRecord(package->db, query, comp);
6008 if (!row)
6010 ERR("Query failed\n");
6011 goto done;
6013 if (!(key = MSI_RecordGetString(row, 6)))
6015 msiobj_release(&row->hdr);
6016 goto done;
6018 file = msi_get_loaded_file(package, key);
6019 msiobj_release(&row->hdr);
6020 if (!file)
6022 ERR("Failed to load the service file\n");
6023 goto done;
6026 if (!args || !args[0]) image_path = file->TargetPath;
6027 else
6029 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
6030 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
6032 ret = ERROR_OUTOFMEMORY;
6033 goto done;
6036 strcpyW(image_path, file->TargetPath);
6037 strcatW(image_path, szSpace);
6038 strcatW(image_path, args);
6040 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
6041 start_type, err_control, image_path, load_order,
6042 NULL, depends, serv_name, pass);
6044 if (!service)
6046 if (GetLastError() != ERROR_SERVICE_EXISTS)
6048 WARN("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
6049 if (is_vital)
6050 ret = ERROR_INSTALL_FAILURE;
6054 else if (sd.lpDescription)
6056 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
6057 WARN("failed to set service description %u\n", GetLastError());
6060 if (image_path != file->TargetPath) msi_free(image_path);
6061 done:
6062 if (service) CloseServiceHandle(service);
6063 if (hscm) CloseServiceHandle(hscm);
6064 msi_free(name);
6065 msi_free(disp);
6066 msi_free(sd.lpDescription);
6067 msi_free(load_order);
6068 msi_free(serv_name);
6069 msi_free(pass);
6070 msi_free(depends);
6071 msi_free(args);
6073 return ret;
6076 static UINT ACTION_InstallServices( MSIPACKAGE *package )
6078 static const WCHAR query[] = {
6079 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6080 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
6081 MSIQUERY *view;
6082 UINT rc;
6084 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6085 if (rc != ERROR_SUCCESS)
6086 return ERROR_SUCCESS;
6088 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
6089 msiobj_release(&view->hdr);
6090 return rc;
6093 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
6094 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
6096 LPCWSTR *vector, *temp_vector;
6097 LPWSTR p, q;
6098 DWORD sep_len;
6100 static const WCHAR separator[] = {'[','~',']',0};
6102 *numargs = 0;
6103 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
6105 if (!args)
6106 return NULL;
6108 vector = msi_alloc(sizeof(LPWSTR));
6109 if (!vector)
6110 return NULL;
6112 p = args;
6115 (*numargs)++;
6116 vector[*numargs - 1] = p;
6118 if ((q = strstrW(p, separator)))
6120 *q = '\0';
6122 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
6123 if (!temp_vector)
6125 msi_free(vector);
6126 return NULL;
6128 vector = temp_vector;
6130 p = q + sep_len;
6132 } while (q);
6134 return vector;
6137 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
6139 MSIPACKAGE *package = param;
6140 MSICOMPONENT *comp;
6141 MSIRECORD *uirow;
6142 SC_HANDLE scm = NULL, service = NULL;
6143 LPCWSTR component, *vector = NULL;
6144 LPWSTR name, args, display_name = NULL;
6145 DWORD event, numargs, len, wait, dummy;
6146 UINT r = ERROR_FUNCTION_FAILED;
6147 SERVICE_STATUS_PROCESS status;
6148 ULONGLONG start_time;
6150 component = MSI_RecordGetString(rec, 6);
6151 comp = msi_get_loaded_component(package, component);
6152 if (!comp)
6153 return ERROR_SUCCESS;
6155 event = MSI_RecordGetInteger( rec, 3 );
6156 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6158 comp->Action = msi_get_component_action( package, comp );
6159 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
6160 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
6162 TRACE("not starting %s\n", debugstr_w(name));
6163 msi_free( name );
6164 return ERROR_SUCCESS;
6167 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6168 wait = MSI_RecordGetInteger(rec, 5);
6170 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
6171 if (!scm)
6173 ERR("Failed to open the service control manager\n");
6174 goto done;
6177 len = 0;
6178 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6179 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6181 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6182 GetServiceDisplayNameW( scm, name, display_name, &len );
6185 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6186 if (!service)
6188 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6189 goto done;
6192 vector = msi_service_args_to_vector(args, &numargs);
6194 if (!StartServiceW(service, numargs, vector) &&
6195 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6197 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6198 goto done;
6201 r = ERROR_SUCCESS;
6202 if (wait)
6204 /* wait for at most 30 seconds for the service to be up and running */
6205 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6206 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6208 TRACE("failed to query service status (%u)\n", GetLastError());
6209 goto done;
6211 start_time = GetTickCount64();
6212 while (status.dwCurrentState == SERVICE_START_PENDING)
6214 if (GetTickCount64() - start_time > 30000) break;
6215 Sleep(1000);
6216 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6217 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6219 TRACE("failed to query service status (%u)\n", GetLastError());
6220 goto done;
6223 if (status.dwCurrentState != SERVICE_RUNNING)
6225 WARN("service failed to start %u\n", status.dwCurrentState);
6226 r = ERROR_FUNCTION_FAILED;
6230 done:
6231 uirow = MSI_CreateRecord( 2 );
6232 MSI_RecordSetStringW( uirow, 1, display_name );
6233 MSI_RecordSetStringW( uirow, 2, name );
6234 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6235 msiobj_release( &uirow->hdr );
6237 if (service) CloseServiceHandle(service);
6238 if (scm) CloseServiceHandle(scm);
6240 msi_free(name);
6241 msi_free(args);
6242 msi_free(vector);
6243 msi_free(display_name);
6244 return r;
6247 static UINT ACTION_StartServices( MSIPACKAGE *package )
6249 static const WCHAR query[] = {
6250 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6251 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6252 MSIQUERY *view;
6253 UINT rc;
6255 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6256 if (rc != ERROR_SUCCESS)
6257 return ERROR_SUCCESS;
6259 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6260 msiobj_release(&view->hdr);
6261 return rc;
6264 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6266 DWORD i, needed, count;
6267 ENUM_SERVICE_STATUSW *dependencies;
6268 SERVICE_STATUS ss;
6269 SC_HANDLE depserv;
6270 BOOL stopped, ret = FALSE;
6272 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6273 0, &needed, &count))
6274 return TRUE;
6276 if (GetLastError() != ERROR_MORE_DATA)
6277 return FALSE;
6279 dependencies = msi_alloc(needed);
6280 if (!dependencies)
6281 return FALSE;
6283 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6284 needed, &needed, &count))
6285 goto done;
6287 for (i = 0; i < count; i++)
6289 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6290 SERVICE_STOP | SERVICE_QUERY_STATUS);
6291 if (!depserv)
6292 goto done;
6294 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6295 CloseServiceHandle(depserv);
6296 if (!stopped)
6297 goto done;
6300 ret = TRUE;
6302 done:
6303 msi_free(dependencies);
6304 return ret;
6307 static UINT stop_service( LPCWSTR name )
6309 SC_HANDLE scm = NULL, service = NULL;
6310 SERVICE_STATUS status;
6311 SERVICE_STATUS_PROCESS ssp;
6312 DWORD needed;
6314 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6315 if (!scm)
6317 WARN("Failed to open the SCM: %d\n", GetLastError());
6318 goto done;
6321 service = OpenServiceW(scm, name,
6322 SERVICE_STOP |
6323 SERVICE_QUERY_STATUS |
6324 SERVICE_ENUMERATE_DEPENDENTS);
6325 if (!service)
6327 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6328 goto done;
6331 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6332 sizeof(SERVICE_STATUS_PROCESS), &needed))
6334 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6335 goto done;
6338 if (ssp.dwCurrentState == SERVICE_STOPPED)
6339 goto done;
6341 stop_service_dependents(scm, service);
6343 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6344 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6346 done:
6347 if (service) CloseServiceHandle(service);
6348 if (scm) CloseServiceHandle(scm);
6350 return ERROR_SUCCESS;
6353 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6355 MSIPACKAGE *package = param;
6356 MSICOMPONENT *comp;
6357 MSIRECORD *uirow;
6358 LPCWSTR component;
6359 WCHAR *name, *display_name = NULL;
6360 DWORD event, len;
6361 SC_HANDLE scm;
6363 component = MSI_RecordGetString( rec, 6 );
6364 comp = msi_get_loaded_component( package, component );
6365 if (!comp)
6366 return ERROR_SUCCESS;
6368 event = MSI_RecordGetInteger( rec, 3 );
6369 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6371 comp->Action = msi_get_component_action( package, comp );
6372 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6373 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6375 TRACE("not stopping %s\n", debugstr_w(name));
6376 msi_free( name );
6377 return ERROR_SUCCESS;
6380 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6381 if (!scm)
6383 ERR("Failed to open the service control manager\n");
6384 goto done;
6387 len = 0;
6388 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6389 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6391 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6392 GetServiceDisplayNameW( scm, name, display_name, &len );
6394 CloseServiceHandle( scm );
6396 stop_service( name );
6398 done:
6399 uirow = MSI_CreateRecord( 2 );
6400 MSI_RecordSetStringW( uirow, 1, display_name );
6401 MSI_RecordSetStringW( uirow, 2, name );
6402 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6403 msiobj_release( &uirow->hdr );
6405 msi_free( name );
6406 msi_free( display_name );
6407 return ERROR_SUCCESS;
6410 static UINT ACTION_StopServices( MSIPACKAGE *package )
6412 static const WCHAR query[] = {
6413 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6414 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6415 MSIQUERY *view;
6416 UINT rc;
6418 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6419 if (rc != ERROR_SUCCESS)
6420 return ERROR_SUCCESS;
6422 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6423 msiobj_release(&view->hdr);
6424 return rc;
6427 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6429 MSIPACKAGE *package = param;
6430 MSICOMPONENT *comp;
6431 MSIRECORD *uirow;
6432 LPWSTR name = NULL, display_name = NULL;
6433 DWORD event, len;
6434 SC_HANDLE scm = NULL, service = NULL;
6436 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6437 if (!comp)
6438 return ERROR_SUCCESS;
6440 event = MSI_RecordGetInteger( rec, 3 );
6441 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6443 comp->Action = msi_get_component_action( package, comp );
6444 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6445 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6447 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6448 msi_free( name );
6449 return ERROR_SUCCESS;
6451 stop_service( name );
6453 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6454 if (!scm)
6456 WARN("Failed to open the SCM: %d\n", GetLastError());
6457 goto done;
6460 len = 0;
6461 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6462 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6464 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6465 GetServiceDisplayNameW( scm, name, display_name, &len );
6468 service = OpenServiceW( scm, name, DELETE );
6469 if (!service)
6471 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6472 goto done;
6475 if (!DeleteService( service ))
6476 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6478 done:
6479 uirow = MSI_CreateRecord( 2 );
6480 MSI_RecordSetStringW( uirow, 1, display_name );
6481 MSI_RecordSetStringW( uirow, 2, name );
6482 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6483 msiobj_release( &uirow->hdr );
6485 if (service) CloseServiceHandle( service );
6486 if (scm) CloseServiceHandle( scm );
6487 msi_free( name );
6488 msi_free( display_name );
6490 return ERROR_SUCCESS;
6493 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6495 static const WCHAR query[] = {
6496 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6497 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6498 MSIQUERY *view;
6499 UINT rc;
6501 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6502 if (rc != ERROR_SUCCESS)
6503 return ERROR_SUCCESS;
6505 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6506 msiobj_release( &view->hdr );
6507 return rc;
6510 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6512 MSIPACKAGE *package = param;
6513 LPWSTR driver, driver_path, ptr;
6514 WCHAR outpath[MAX_PATH];
6515 MSIFILE *driver_file = NULL, *setup_file = NULL;
6516 MSICOMPONENT *comp;
6517 MSIRECORD *uirow;
6518 LPCWSTR desc, file_key, component;
6519 DWORD len, usage;
6520 UINT r = ERROR_SUCCESS;
6522 static const WCHAR driver_fmt[] = {
6523 'D','r','i','v','e','r','=','%','s',0};
6524 static const WCHAR setup_fmt[] = {
6525 'S','e','t','u','p','=','%','s',0};
6526 static const WCHAR usage_fmt[] = {
6527 'F','i','l','e','U','s','a','g','e','=','1',0};
6529 component = MSI_RecordGetString( rec, 2 );
6530 comp = msi_get_loaded_component( package, component );
6531 if (!comp)
6532 return ERROR_SUCCESS;
6534 comp->Action = msi_get_component_action( package, comp );
6535 if (comp->Action != INSTALLSTATE_LOCAL)
6537 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6538 return ERROR_SUCCESS;
6540 desc = MSI_RecordGetString(rec, 3);
6542 file_key = MSI_RecordGetString( rec, 4 );
6543 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6545 file_key = MSI_RecordGetString( rec, 5 );
6546 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6548 if (!driver_file)
6550 ERR("ODBC Driver entry not found!\n");
6551 return ERROR_FUNCTION_FAILED;
6554 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6555 if (setup_file)
6556 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6557 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6559 driver = msi_alloc(len * sizeof(WCHAR));
6560 if (!driver)
6561 return ERROR_OUTOFMEMORY;
6563 ptr = driver;
6564 lstrcpyW(ptr, desc);
6565 ptr += lstrlenW(ptr) + 1;
6567 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6568 ptr += len + 1;
6570 if (setup_file)
6572 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6573 ptr += len + 1;
6576 lstrcpyW(ptr, usage_fmt);
6577 ptr += lstrlenW(ptr) + 1;
6578 *ptr = '\0';
6580 if (!driver_file->TargetPath)
6582 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6583 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6585 driver_path = strdupW(driver_file->TargetPath);
6586 ptr = strrchrW(driver_path, '\\');
6587 if (ptr) *ptr = '\0';
6589 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6590 NULL, ODBC_INSTALL_COMPLETE, &usage))
6592 ERR("Failed to install SQL driver!\n");
6593 r = ERROR_FUNCTION_FAILED;
6596 uirow = MSI_CreateRecord( 5 );
6597 MSI_RecordSetStringW( uirow, 1, desc );
6598 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6599 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6600 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6601 msiobj_release( &uirow->hdr );
6603 msi_free(driver);
6604 msi_free(driver_path);
6606 return r;
6609 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6611 MSIPACKAGE *package = param;
6612 LPWSTR translator, translator_path, ptr;
6613 WCHAR outpath[MAX_PATH];
6614 MSIFILE *translator_file = NULL, *setup_file = NULL;
6615 MSICOMPONENT *comp;
6616 MSIRECORD *uirow;
6617 LPCWSTR desc, file_key, component;
6618 DWORD len, usage;
6619 UINT r = ERROR_SUCCESS;
6621 static const WCHAR translator_fmt[] = {
6622 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6623 static const WCHAR setup_fmt[] = {
6624 'S','e','t','u','p','=','%','s',0};
6626 component = MSI_RecordGetString( rec, 2 );
6627 comp = msi_get_loaded_component( package, component );
6628 if (!comp)
6629 return ERROR_SUCCESS;
6631 comp->Action = msi_get_component_action( package, comp );
6632 if (comp->Action != INSTALLSTATE_LOCAL)
6634 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6635 return ERROR_SUCCESS;
6637 desc = MSI_RecordGetString(rec, 3);
6639 file_key = MSI_RecordGetString( rec, 4 );
6640 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6642 file_key = MSI_RecordGetString( rec, 5 );
6643 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6645 if (!translator_file)
6647 ERR("ODBC Translator entry not found!\n");
6648 return ERROR_FUNCTION_FAILED;
6651 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6652 if (setup_file)
6653 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6655 translator = msi_alloc(len * sizeof(WCHAR));
6656 if (!translator)
6657 return ERROR_OUTOFMEMORY;
6659 ptr = translator;
6660 lstrcpyW(ptr, desc);
6661 ptr += lstrlenW(ptr) + 1;
6663 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6664 ptr += len + 1;
6666 if (setup_file)
6668 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6669 ptr += len + 1;
6671 *ptr = '\0';
6673 translator_path = strdupW(translator_file->TargetPath);
6674 ptr = strrchrW(translator_path, '\\');
6675 if (ptr) *ptr = '\0';
6677 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6678 NULL, ODBC_INSTALL_COMPLETE, &usage))
6680 ERR("Failed to install SQL translator!\n");
6681 r = ERROR_FUNCTION_FAILED;
6684 uirow = MSI_CreateRecord( 5 );
6685 MSI_RecordSetStringW( uirow, 1, desc );
6686 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6687 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6688 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6689 msiobj_release( &uirow->hdr );
6691 msi_free(translator);
6692 msi_free(translator_path);
6694 return r;
6697 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6699 MSIPACKAGE *package = param;
6700 MSICOMPONENT *comp;
6701 LPWSTR attrs;
6702 LPCWSTR desc, driver, component;
6703 WORD request = ODBC_ADD_SYS_DSN;
6704 INT registration;
6705 DWORD len;
6706 UINT r = ERROR_SUCCESS;
6707 MSIRECORD *uirow;
6709 static const WCHAR attrs_fmt[] = {
6710 'D','S','N','=','%','s',0 };
6712 component = MSI_RecordGetString( rec, 2 );
6713 comp = msi_get_loaded_component( package, component );
6714 if (!comp)
6715 return ERROR_SUCCESS;
6717 comp->Action = msi_get_component_action( package, comp );
6718 if (comp->Action != INSTALLSTATE_LOCAL)
6720 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6721 return ERROR_SUCCESS;
6724 desc = MSI_RecordGetString(rec, 3);
6725 driver = MSI_RecordGetString(rec, 4);
6726 registration = MSI_RecordGetInteger(rec, 5);
6728 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6729 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6731 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6732 attrs = msi_alloc(len * sizeof(WCHAR));
6733 if (!attrs)
6734 return ERROR_OUTOFMEMORY;
6736 len = sprintfW(attrs, attrs_fmt, desc);
6737 attrs[len + 1] = 0;
6739 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6741 ERR("Failed to install SQL data source!\n");
6742 r = ERROR_FUNCTION_FAILED;
6745 uirow = MSI_CreateRecord( 5 );
6746 MSI_RecordSetStringW( uirow, 1, desc );
6747 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6748 MSI_RecordSetInteger( uirow, 3, request );
6749 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6750 msiobj_release( &uirow->hdr );
6752 msi_free(attrs);
6754 return r;
6757 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6759 static const WCHAR driver_query[] = {
6760 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6761 'O','D','B','C','D','r','i','v','e','r',0};
6762 static const WCHAR translator_query[] = {
6763 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6764 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6765 static const WCHAR source_query[] = {
6766 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6767 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6768 MSIQUERY *view;
6769 UINT rc;
6771 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6772 if (rc == ERROR_SUCCESS)
6774 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6775 msiobj_release(&view->hdr);
6776 if (rc != ERROR_SUCCESS)
6777 return rc;
6779 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6780 if (rc == ERROR_SUCCESS)
6782 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6783 msiobj_release(&view->hdr);
6784 if (rc != ERROR_SUCCESS)
6785 return rc;
6787 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6788 if (rc == ERROR_SUCCESS)
6790 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6791 msiobj_release(&view->hdr);
6792 if (rc != ERROR_SUCCESS)
6793 return rc;
6795 return ERROR_SUCCESS;
6798 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6800 MSIPACKAGE *package = param;
6801 MSICOMPONENT *comp;
6802 MSIRECORD *uirow;
6803 DWORD usage;
6804 LPCWSTR desc, component;
6806 component = MSI_RecordGetString( rec, 2 );
6807 comp = msi_get_loaded_component( package, component );
6808 if (!comp)
6809 return ERROR_SUCCESS;
6811 comp->Action = msi_get_component_action( package, comp );
6812 if (comp->Action != INSTALLSTATE_ABSENT)
6814 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6815 return ERROR_SUCCESS;
6818 desc = MSI_RecordGetString( rec, 3 );
6819 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6821 WARN("Failed to remove ODBC driver\n");
6823 else if (!usage)
6825 FIXME("Usage count reached 0\n");
6828 uirow = MSI_CreateRecord( 2 );
6829 MSI_RecordSetStringW( uirow, 1, desc );
6830 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6831 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6832 msiobj_release( &uirow->hdr );
6834 return ERROR_SUCCESS;
6837 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6839 MSIPACKAGE *package = param;
6840 MSICOMPONENT *comp;
6841 MSIRECORD *uirow;
6842 DWORD usage;
6843 LPCWSTR desc, component;
6845 component = MSI_RecordGetString( rec, 2 );
6846 comp = msi_get_loaded_component( package, component );
6847 if (!comp)
6848 return ERROR_SUCCESS;
6850 comp->Action = msi_get_component_action( package, comp );
6851 if (comp->Action != INSTALLSTATE_ABSENT)
6853 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6854 return ERROR_SUCCESS;
6857 desc = MSI_RecordGetString( rec, 3 );
6858 if (!SQLRemoveTranslatorW( desc, &usage ))
6860 WARN("Failed to remove ODBC translator\n");
6862 else if (!usage)
6864 FIXME("Usage count reached 0\n");
6867 uirow = MSI_CreateRecord( 2 );
6868 MSI_RecordSetStringW( uirow, 1, desc );
6869 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6870 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6871 msiobj_release( &uirow->hdr );
6873 return ERROR_SUCCESS;
6876 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6878 MSIPACKAGE *package = param;
6879 MSICOMPONENT *comp;
6880 MSIRECORD *uirow;
6881 LPWSTR attrs;
6882 LPCWSTR desc, driver, component;
6883 WORD request = ODBC_REMOVE_SYS_DSN;
6884 INT registration;
6885 DWORD len;
6887 static const WCHAR attrs_fmt[] = {
6888 'D','S','N','=','%','s',0 };
6890 component = MSI_RecordGetString( rec, 2 );
6891 comp = msi_get_loaded_component( package, component );
6892 if (!comp)
6893 return ERROR_SUCCESS;
6895 comp->Action = msi_get_component_action( package, comp );
6896 if (comp->Action != INSTALLSTATE_ABSENT)
6898 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6899 return ERROR_SUCCESS;
6902 desc = MSI_RecordGetString( rec, 3 );
6903 driver = MSI_RecordGetString( rec, 4 );
6904 registration = MSI_RecordGetInteger( rec, 5 );
6906 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6907 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6909 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6910 attrs = msi_alloc( len * sizeof(WCHAR) );
6911 if (!attrs)
6912 return ERROR_OUTOFMEMORY;
6914 FIXME("Use ODBCSourceAttribute table\n");
6916 len = sprintfW( attrs, attrs_fmt, desc );
6917 attrs[len + 1] = 0;
6919 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6921 WARN("Failed to remove ODBC data source\n");
6923 msi_free( attrs );
6925 uirow = MSI_CreateRecord( 3 );
6926 MSI_RecordSetStringW( uirow, 1, desc );
6927 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6928 MSI_RecordSetInteger( uirow, 3, request );
6929 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6930 msiobj_release( &uirow->hdr );
6932 return ERROR_SUCCESS;
6935 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6937 static const WCHAR driver_query[] = {
6938 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6939 'O','D','B','C','D','r','i','v','e','r',0};
6940 static const WCHAR translator_query[] = {
6941 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6942 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6943 static const WCHAR source_query[] = {
6944 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6945 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6946 MSIQUERY *view;
6947 UINT rc;
6949 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6950 if (rc == ERROR_SUCCESS)
6952 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6953 msiobj_release( &view->hdr );
6954 if (rc != ERROR_SUCCESS)
6955 return rc;
6957 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6958 if (rc == ERROR_SUCCESS)
6960 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6961 msiobj_release( &view->hdr );
6962 if (rc != ERROR_SUCCESS)
6963 return rc;
6965 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6966 if (rc == ERROR_SUCCESS)
6968 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6969 msiobj_release( &view->hdr );
6970 if (rc != ERROR_SUCCESS)
6971 return rc;
6973 return ERROR_SUCCESS;
6976 #define ENV_ACT_SETALWAYS 0x1
6977 #define ENV_ACT_SETABSENT 0x2
6978 #define ENV_ACT_REMOVE 0x4
6979 #define ENV_ACT_REMOVEMATCH 0x8
6981 #define ENV_MOD_MACHINE 0x20000000
6982 #define ENV_MOD_APPEND 0x40000000
6983 #define ENV_MOD_PREFIX 0x80000000
6984 #define ENV_MOD_MASK 0xC0000000
6986 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6988 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6990 LPCWSTR cptr = *name;
6992 static const WCHAR prefix[] = {'[','~',']',0};
6993 static const int prefix_len = 3;
6995 *flags = 0;
6996 while (*cptr)
6998 if (*cptr == '=')
6999 *flags |= ENV_ACT_SETALWAYS;
7000 else if (*cptr == '+')
7001 *flags |= ENV_ACT_SETABSENT;
7002 else if (*cptr == '-')
7003 *flags |= ENV_ACT_REMOVE;
7004 else if (*cptr == '!')
7005 *flags |= ENV_ACT_REMOVEMATCH;
7006 else if (*cptr == '*')
7007 *flags |= ENV_MOD_MACHINE;
7008 else
7009 break;
7011 cptr++;
7012 (*name)++;
7015 if (!*cptr)
7017 ERR("Missing environment variable\n");
7018 return ERROR_FUNCTION_FAILED;
7021 if (*value)
7023 LPCWSTR ptr = *value;
7024 if (!strncmpW(ptr, prefix, prefix_len))
7026 if (ptr[prefix_len] == szSemiColon[0])
7028 *flags |= ENV_MOD_APPEND;
7029 *value += lstrlenW(prefix);
7031 else
7033 *value = NULL;
7036 else if (lstrlenW(*value) >= prefix_len)
7038 ptr += lstrlenW(ptr) - prefix_len;
7039 if (!strcmpW( ptr, prefix ))
7041 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
7043 *flags |= ENV_MOD_PREFIX;
7044 /* the "[~]" will be removed by deformat_string */;
7046 else
7048 *value = NULL;
7054 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
7055 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
7056 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
7057 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
7059 ERR("Invalid flags: %08x\n", *flags);
7060 return ERROR_FUNCTION_FAILED;
7063 if (!*flags)
7064 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
7066 return ERROR_SUCCESS;
7069 static UINT open_env_key( DWORD flags, HKEY *key )
7071 static const WCHAR user_env[] =
7072 {'E','n','v','i','r','o','n','m','e','n','t',0};
7073 static const WCHAR machine_env[] =
7074 {'S','y','s','t','e','m','\\',
7075 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
7076 'C','o','n','t','r','o','l','\\',
7077 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
7078 'E','n','v','i','r','o','n','m','e','n','t',0};
7079 const WCHAR *env;
7080 HKEY root;
7081 LONG res;
7083 if (flags & ENV_MOD_MACHINE)
7085 env = machine_env;
7086 root = HKEY_LOCAL_MACHINE;
7088 else
7090 env = user_env;
7091 root = HKEY_CURRENT_USER;
7094 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
7095 if (res != ERROR_SUCCESS)
7097 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
7098 return ERROR_FUNCTION_FAILED;
7101 return ERROR_SUCCESS;
7104 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
7106 MSIPACKAGE *package = param;
7107 LPCWSTR name, value, component;
7108 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
7109 DWORD flags, type, size, len, len_value = 0;
7110 UINT res;
7111 HKEY env = NULL;
7112 MSICOMPONENT *comp;
7113 MSIRECORD *uirow;
7114 int action = 0, found = 0;
7116 component = MSI_RecordGetString(rec, 4);
7117 comp = msi_get_loaded_component(package, component);
7118 if (!comp)
7119 return ERROR_SUCCESS;
7121 comp->Action = msi_get_component_action( package, comp );
7122 if (comp->Action != INSTALLSTATE_LOCAL)
7124 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
7125 return ERROR_SUCCESS;
7127 name = MSI_RecordGetString(rec, 2);
7128 value = MSI_RecordGetString(rec, 3);
7130 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7132 res = env_parse_flags(&name, &value, &flags);
7133 if (res != ERROR_SUCCESS || !value)
7134 goto done;
7136 if (value && !deformat_string(package, value, &deformatted))
7138 res = ERROR_OUTOFMEMORY;
7139 goto done;
7142 if ((value = deformatted))
7144 if (flags & ENV_MOD_PREFIX)
7146 p = strrchrW( value, ';' );
7147 len_value = p - value;
7149 else if (flags & ENV_MOD_APPEND)
7151 value = strchrW( value, ';' ) + 1;
7152 len_value = strlenW( value );
7154 else len_value = strlenW( value );
7157 res = open_env_key( flags, &env );
7158 if (res != ERROR_SUCCESS)
7159 goto done;
7161 if (flags & ENV_MOD_MACHINE)
7162 action |= 0x20000000;
7164 size = 0;
7165 type = REG_SZ;
7166 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
7167 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7168 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7169 goto done;
7171 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
7173 action = 0x2;
7175 /* Nothing to do. */
7176 if (!value)
7178 res = ERROR_SUCCESS;
7179 goto done;
7181 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
7182 newval = strdupW(value);
7183 if (!newval)
7185 res = ERROR_OUTOFMEMORY;
7186 goto done;
7189 else
7191 action = 0x1;
7193 /* Contrary to MSDN, +-variable to [~];path works */
7194 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7196 res = ERROR_SUCCESS;
7197 goto done;
7200 if (!(p = q = data = msi_alloc( size )))
7202 msi_free(deformatted);
7203 RegCloseKey(env);
7204 return ERROR_OUTOFMEMORY;
7207 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
7208 if (res != ERROR_SUCCESS)
7209 goto done;
7211 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7213 action = 0x4;
7214 res = RegDeleteValueW(env, name);
7215 if (res != ERROR_SUCCESS)
7216 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7217 goto done;
7220 for (;;)
7222 while (*q && *q != ';') q++;
7223 len = q - p;
7224 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
7225 (!p[len] || p[len] == ';'))
7227 found = 1;
7228 break;
7230 if (!*q) break;
7231 p = ++q;
7234 if (found)
7236 TRACE("string already set\n");
7237 goto done;
7240 size = (len_value + 1 + strlenW( data ) + 1) * sizeof(WCHAR);
7241 if (!(p = newval = msi_alloc( size )))
7243 res = ERROR_OUTOFMEMORY;
7244 goto done;
7247 if (flags & ENV_MOD_PREFIX)
7249 memcpy( newval, value, len_value * sizeof(WCHAR) );
7250 newval[len_value] = ';';
7251 p = newval + len_value + 1;
7252 action |= 0x80000000;
7255 strcpyW( p, data );
7257 if (flags & ENV_MOD_APPEND)
7259 p += strlenW( data );
7260 *p++ = ';';
7261 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
7262 action |= 0x40000000;
7265 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7266 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
7267 if (res)
7269 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7272 done:
7273 uirow = MSI_CreateRecord( 3 );
7274 MSI_RecordSetStringW( uirow, 1, name );
7275 MSI_RecordSetStringW( uirow, 2, newval );
7276 MSI_RecordSetInteger( uirow, 3, action );
7277 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7278 msiobj_release( &uirow->hdr );
7280 if (env) RegCloseKey(env);
7281 msi_free(deformatted);
7282 msi_free(data);
7283 msi_free(newval);
7284 return res;
7287 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7289 static const WCHAR query[] = {
7290 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7291 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7292 MSIQUERY *view;
7293 UINT rc;
7295 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7296 if (rc != ERROR_SUCCESS)
7297 return ERROR_SUCCESS;
7299 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7300 msiobj_release(&view->hdr);
7301 return rc;
7304 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7306 MSIPACKAGE *package = param;
7307 LPCWSTR name, value, component;
7308 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7309 DWORD flags, type, size, len, len_value = 0, len_new_value;
7310 HKEY env;
7311 MSICOMPONENT *comp;
7312 MSIRECORD *uirow;
7313 int action = 0;
7314 LONG res;
7315 UINT r;
7317 component = MSI_RecordGetString( rec, 4 );
7318 comp = msi_get_loaded_component( package, component );
7319 if (!comp)
7320 return ERROR_SUCCESS;
7322 comp->Action = msi_get_component_action( package, comp );
7323 if (comp->Action != INSTALLSTATE_ABSENT)
7325 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7326 return ERROR_SUCCESS;
7328 name = MSI_RecordGetString( rec, 2 );
7329 value = MSI_RecordGetString( rec, 3 );
7331 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7333 r = env_parse_flags( &name, &value, &flags );
7334 if (r != ERROR_SUCCESS)
7335 return r;
7337 if (!(flags & ENV_ACT_REMOVE))
7339 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7340 return ERROR_SUCCESS;
7343 if (value && !deformat_string( package, value, &deformatted ))
7344 return ERROR_OUTOFMEMORY;
7346 if ((value = deformatted))
7348 if (flags & ENV_MOD_PREFIX)
7350 p = strchrW( value, ';' );
7351 len_value = p - value;
7353 else if (flags & ENV_MOD_APPEND)
7355 value = strchrW( value, ';' ) + 1;
7356 len_value = strlenW( value );
7358 else len_value = strlenW( value );
7361 r = open_env_key( flags, &env );
7362 if (r != ERROR_SUCCESS)
7364 r = ERROR_SUCCESS;
7365 goto done;
7368 if (flags & ENV_MOD_MACHINE)
7369 action |= 0x20000000;
7371 size = 0;
7372 type = REG_SZ;
7373 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7374 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7375 goto done;
7377 if (!(new_value = msi_alloc( size ))) goto done;
7379 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7380 if (res != ERROR_SUCCESS)
7381 goto done;
7383 len_new_value = size / sizeof(WCHAR) - 1;
7384 p = q = new_value;
7385 for (;;)
7387 while (*q && *q != ';') q++;
7388 len = q - p;
7389 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7391 if (*q == ';') q++;
7392 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7393 break;
7395 if (!*q) break;
7396 p = ++q;
7399 if (!new_value[0] || !value)
7401 TRACE("removing %s\n", debugstr_w(name));
7402 res = RegDeleteValueW( env, name );
7403 if (res != ERROR_SUCCESS)
7404 WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
7406 else
7408 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7409 size = (strlenW( new_value ) + 1) * sizeof(WCHAR);
7410 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7411 if (res != ERROR_SUCCESS)
7412 WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7415 done:
7416 uirow = MSI_CreateRecord( 3 );
7417 MSI_RecordSetStringW( uirow, 1, name );
7418 MSI_RecordSetStringW( uirow, 2, value );
7419 MSI_RecordSetInteger( uirow, 3, action );
7420 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7421 msiobj_release( &uirow->hdr );
7423 if (env) RegCloseKey( env );
7424 msi_free( deformatted );
7425 msi_free( new_value );
7426 return r;
7429 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7431 static const WCHAR query[] = {
7432 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7433 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7434 MSIQUERY *view;
7435 UINT rc;
7437 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7438 if (rc != ERROR_SUCCESS)
7439 return ERROR_SUCCESS;
7441 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7442 msiobj_release( &view->hdr );
7443 return rc;
7446 UINT msi_validate_product_id( MSIPACKAGE *package )
7448 LPWSTR key, template, id;
7449 UINT r = ERROR_SUCCESS;
7451 id = msi_dup_property( package->db, szProductID );
7452 if (id)
7454 msi_free( id );
7455 return ERROR_SUCCESS;
7457 template = msi_dup_property( package->db, szPIDTemplate );
7458 key = msi_dup_property( package->db, szPIDKEY );
7459 if (key && template)
7461 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7462 r = msi_set_property( package->db, szProductID, key, -1 );
7464 msi_free( template );
7465 msi_free( key );
7466 return r;
7469 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7471 return msi_validate_product_id( package );
7474 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7476 TRACE("\n");
7477 package->need_reboot_at_end = 1;
7478 return ERROR_SUCCESS;
7481 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7483 static const WCHAR szAvailableFreeReg[] =
7484 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7485 MSIRECORD *uirow;
7486 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7488 TRACE("%p %d kilobytes\n", package, space);
7490 uirow = MSI_CreateRecord( 1 );
7491 MSI_RecordSetInteger( uirow, 1, space );
7492 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7493 msiobj_release( &uirow->hdr );
7495 return ERROR_SUCCESS;
7498 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7500 TRACE("%p\n", package);
7502 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7503 return ERROR_SUCCESS;
7506 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7508 FIXME("%p\n", package);
7509 return ERROR_SUCCESS;
7512 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7514 static const WCHAR driver_query[] = {
7515 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7516 'O','D','B','C','D','r','i','v','e','r',0};
7517 static const WCHAR translator_query[] = {
7518 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7519 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7520 MSIQUERY *view;
7521 UINT r, count;
7523 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7524 if (r == ERROR_SUCCESS)
7526 count = 0;
7527 r = MSI_IterateRecords( view, &count, NULL, package );
7528 msiobj_release( &view->hdr );
7529 if (r != ERROR_SUCCESS)
7530 return r;
7531 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7533 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7534 if (r == ERROR_SUCCESS)
7536 count = 0;
7537 r = MSI_IterateRecords( view, &count, NULL, package );
7538 msiobj_release( &view->hdr );
7539 if (r != ERROR_SUCCESS)
7540 return r;
7541 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7543 return ERROR_SUCCESS;
7546 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7548 static const WCHAR fmtW[] =
7549 {'m','s','i','e','x','e','c',' ','/','q','n',' ','/','i',' ','%','s',' ',
7550 'R','E','M','O','V','E','=','%','s',0};
7551 MSIPACKAGE *package = param;
7552 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7553 int attrs = MSI_RecordGetInteger( rec, 5 );
7554 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7555 WCHAR *product, *features, *cmd;
7556 STARTUPINFOW si;
7557 PROCESS_INFORMATION info;
7558 BOOL ret;
7560 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7561 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7563 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7565 len += strlenW( product );
7566 if (features)
7567 len += strlenW( features );
7568 else
7569 len += sizeof(szAll) / sizeof(szAll[0]);
7571 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7573 msi_free( product );
7574 msi_free( features );
7575 return ERROR_OUTOFMEMORY;
7577 sprintfW( cmd, fmtW, product, features ? features : szAll );
7578 msi_free( product );
7579 msi_free( features );
7581 memset( &si, 0, sizeof(STARTUPINFOW) );
7582 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7583 msi_free( cmd );
7584 if (!ret) return GetLastError();
7585 CloseHandle( info.hThread );
7587 WaitForSingleObject( info.hProcess, INFINITE );
7588 CloseHandle( info.hProcess );
7589 return ERROR_SUCCESS;
7592 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7594 static const WCHAR query[] = {
7595 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7596 MSIQUERY *view;
7597 UINT r;
7599 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7600 if (r == ERROR_SUCCESS)
7602 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7603 msiobj_release( &view->hdr );
7604 if (r != ERROR_SUCCESS)
7605 return r;
7607 return ERROR_SUCCESS;
7610 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7612 MSIPACKAGE *package = param;
7613 int attributes = MSI_RecordGetInteger( rec, 5 );
7615 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7617 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7618 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7619 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7620 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7621 HKEY hkey;
7622 UINT r;
7624 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7626 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7627 if (r != ERROR_SUCCESS)
7628 return ERROR_SUCCESS;
7630 else
7632 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7633 if (r != ERROR_SUCCESS)
7634 return ERROR_SUCCESS;
7636 RegCloseKey( hkey );
7638 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7639 debugstr_w(upgrade_code), debugstr_w(version_min),
7640 debugstr_w(version_max), debugstr_w(language));
7642 return ERROR_SUCCESS;
7645 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7647 static const WCHAR query[] = {
7648 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7649 'U','p','g','r','a','d','e',0};
7650 MSIQUERY *view;
7651 UINT r;
7653 if (msi_get_property_int( package->db, szInstalled, 0 ))
7655 TRACE("product is installed, skipping action\n");
7656 return ERROR_SUCCESS;
7658 if (msi_get_property_int( package->db, szPreselected, 0 ))
7660 TRACE("Preselected property is set, not migrating feature states\n");
7661 return ERROR_SUCCESS;
7663 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7664 if (r == ERROR_SUCCESS)
7666 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7667 msiobj_release( &view->hdr );
7668 if (r != ERROR_SUCCESS)
7669 return r;
7671 return ERROR_SUCCESS;
7674 static void bind_image( const char *filename, const char *path )
7676 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7678 WARN("failed to bind image %u\n", GetLastError());
7682 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7684 UINT i;
7685 MSIFILE *file;
7686 MSIPACKAGE *package = param;
7687 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7688 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7689 char *filenameA, *pathA;
7690 WCHAR *pathW, **path_list;
7692 if (!(file = msi_get_loaded_file( package, key )))
7694 WARN("file %s not found\n", debugstr_w(key));
7695 return ERROR_SUCCESS;
7697 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7698 path_list = msi_split_string( paths, ';' );
7699 if (!path_list) bind_image( filenameA, NULL );
7700 else
7702 for (i = 0; path_list[i] && path_list[i][0]; i++)
7704 deformat_string( package, path_list[i], &pathW );
7705 if ((pathA = strdupWtoA( pathW )))
7707 bind_image( filenameA, pathA );
7708 msi_free( pathA );
7710 msi_free( pathW );
7713 msi_free( path_list );
7714 msi_free( filenameA );
7715 return ERROR_SUCCESS;
7718 static UINT ACTION_BindImage( MSIPACKAGE *package )
7720 static const WCHAR query[] = {
7721 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7722 'B','i','n','d','I','m','a','g','e',0};
7723 MSIQUERY *view;
7724 UINT r;
7726 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7727 if (r == ERROR_SUCCESS)
7729 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7730 msiobj_release( &view->hdr );
7731 if (r != ERROR_SUCCESS)
7732 return r;
7734 return ERROR_SUCCESS;
7737 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7739 static const WCHAR query[] = {
7740 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7741 MSIQUERY *view;
7742 DWORD count = 0;
7743 UINT r;
7745 r = MSI_OpenQuery( package->db, &view, query, table );
7746 if (r == ERROR_SUCCESS)
7748 r = MSI_IterateRecords(view, &count, NULL, package);
7749 msiobj_release(&view->hdr);
7750 if (r != ERROR_SUCCESS)
7751 return r;
7753 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7754 return ERROR_SUCCESS;
7757 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7759 static const WCHAR table[] = {
7760 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7761 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7764 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7766 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7767 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7770 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7772 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7773 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7776 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7778 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7779 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7782 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7784 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7785 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7788 static const struct
7790 const WCHAR *action;
7791 const UINT description;
7792 const UINT template;
7793 UINT (*handler)(MSIPACKAGE *);
7794 const WCHAR *action_rollback;
7796 StandardActions[] =
7798 { szAllocateRegistrySpace, IDS_DESC_ALLOCATEREGISTRYSPACE, IDS_TEMP_ALLOCATEREGISTRYSPACE, ACTION_AllocateRegistrySpace, NULL },
7799 { szAppSearch, IDS_DESC_APPSEARCH, IDS_TEMP_APPSEARCH, ACTION_AppSearch, NULL },
7800 { szBindImage, IDS_DESC_BINDIMAGE, IDS_TEMP_BINDIMAGE, ACTION_BindImage, NULL },
7801 { szCCPSearch, IDS_DESC_CCPSEARCH, 0, ACTION_CCPSearch, NULL },
7802 { szCostFinalize, IDS_DESC_COSTFINALIZE, 0, ACTION_CostFinalize, NULL },
7803 { szCostInitialize, IDS_DESC_COSTINITIALIZE, 0, ACTION_CostInitialize, NULL },
7804 { szCreateFolders, IDS_DESC_CREATEFOLDERS, IDS_TEMP_CREATEFOLDERS, ACTION_CreateFolders, szRemoveFolders },
7805 { szCreateShortcuts, IDS_DESC_CREATESHORTCUTS, IDS_TEMP_CREATESHORTCUTS, ACTION_CreateShortcuts, szRemoveShortcuts },
7806 { szDeleteServices, IDS_DESC_DELETESERVICES, IDS_TEMP_DELETESERVICES, ACTION_DeleteServices, szInstallServices },
7807 { szDisableRollback, 0, 0, ACTION_DisableRollback, NULL },
7808 { szDuplicateFiles, IDS_DESC_DUPLICATEFILES, IDS_TEMP_DUPLICATEFILES, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7809 { szExecuteAction, 0, 0, ACTION_ExecuteAction, NULL },
7810 { szFileCost, IDS_DESC_FILECOST, 0, ACTION_FileCost, NULL },
7811 { szFindRelatedProducts, IDS_DESC_FINDRELATEDPRODUCTS, IDS_TEMP_FINDRELATEDPRODUCTS, ACTION_FindRelatedProducts, NULL },
7812 { szForceReboot, 0, 0, ACTION_ForceReboot, NULL },
7813 { szInstallAdminPackage, IDS_DESC_INSTALLADMINPACKAGE, IDS_TEMP_INSTALLADMINPACKAGE, ACTION_InstallAdminPackage, NULL },
7814 { szInstallExecute, 0, 0, ACTION_InstallExecute, NULL },
7815 { szInstallExecuteAgain, 0, 0, ACTION_InstallExecute, NULL },
7816 { szInstallFiles, IDS_DESC_INSTALLFILES, IDS_TEMP_INSTALLFILES, ACTION_InstallFiles, szRemoveFiles },
7817 { szInstallFinalize, 0, 0, ACTION_InstallFinalize, NULL },
7818 { szInstallInitialize, 0, 0, ACTION_InstallInitialize, NULL },
7819 { szInstallODBC, IDS_DESC_INSTALLODBC, 0, ACTION_InstallODBC, szRemoveODBC },
7820 { szInstallServices, IDS_DESC_INSTALLSERVICES, IDS_TEMP_INSTALLSERVICES, ACTION_InstallServices, szDeleteServices },
7821 { szInstallSFPCatalogFile, IDS_DESC_INSTALLSFPCATALOGFILE, IDS_TEMP_INSTALLSFPCATALOGFILE, ACTION_InstallSFPCatalogFile, NULL },
7822 { szInstallValidate, IDS_DESC_INSTALLVALIDATE, 0, ACTION_InstallValidate, NULL },
7823 { szIsolateComponents, 0, 0, ACTION_IsolateComponents, NULL },
7824 { szLaunchConditions, IDS_DESC_LAUNCHCONDITIONS, 0, ACTION_LaunchConditions, NULL },
7825 { szMigrateFeatureStates, IDS_DESC_MIGRATEFEATURESTATES, IDS_TEMP_MIGRATEFEATURESTATES, ACTION_MigrateFeatureStates, NULL },
7826 { szMoveFiles, IDS_DESC_MOVEFILES, IDS_TEMP_MOVEFILES, ACTION_MoveFiles, NULL },
7827 { szMsiPublishAssemblies, IDS_DESC_MSIPUBLISHASSEMBLIES, IDS_TEMP_MSIPUBLISHASSEMBLIES, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7828 { szMsiUnpublishAssemblies, IDS_DESC_MSIUNPUBLISHASSEMBLIES, IDS_TEMP_MSIUNPUBLISHASSEMBLIES, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7829 { szPatchFiles, IDS_DESC_PATCHFILES, IDS_TEMP_PATCHFILES, ACTION_PatchFiles, NULL },
7830 { szProcessComponents, IDS_DESC_PROCESSCOMPONENTS, 0, ACTION_ProcessComponents, szProcessComponents },
7831 { szPublishComponents, IDS_DESC_PUBLISHCOMPONENTS, IDS_TEMP_PUBLISHCOMPONENTS, ACTION_PublishComponents, szUnpublishComponents },
7832 { szPublishFeatures, IDS_DESC_PUBLISHFEATURES, IDS_TEMP_PUBLISHFEATURES, ACTION_PublishFeatures, szUnpublishFeatures },
7833 { szPublishProduct, IDS_DESC_PUBLISHPRODUCT, 0, ACTION_PublishProduct, szUnpublishProduct },
7834 { szRegisterClassInfo, IDS_DESC_REGISTERCLASSINFO, IDS_TEMP_REGISTERCLASSINFO, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7835 { szRegisterComPlus, IDS_DESC_REGISTERCOMPLUS, IDS_TEMP_REGISTERCOMPLUS, ACTION_RegisterComPlus, szUnregisterComPlus },
7836 { szRegisterExtensionInfo, IDS_DESC_REGISTEREXTENSIONINFO, 0, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7837 { szRegisterFonts, IDS_DESC_REGISTERFONTS, IDS_TEMP_REGISTERFONTS, ACTION_RegisterFonts, szUnregisterFonts },
7838 { szRegisterMIMEInfo, IDS_DESC_REGISTERMIMEINFO, IDS_TEMP_REGISTERMIMEINFO, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7839 { szRegisterProduct, IDS_DESC_REGISTERPRODUCT, 0, ACTION_RegisterProduct, NULL },
7840 { szRegisterProgIdInfo, IDS_DESC_REGISTERPROGIDINFO, IDS_TEMP_REGISTERPROGIDINFO, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7841 { szRegisterTypeLibraries, IDS_DESC_REGISTERTYPELIBRARIES, IDS_TEMP_REGISTERTYPELIBRARIES, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7842 { szRegisterUser, IDS_DESC_REGISTERUSER, 0, ACTION_RegisterUser, NULL },
7843 { szRemoveDuplicateFiles, IDS_DESC_REMOVEDUPLICATEFILES, IDS_TEMP_REMOVEDUPLICATEFILES, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7844 { szRemoveEnvironmentStrings, IDS_DESC_REMOVEENVIRONMENTSTRINGS, IDS_TEMP_REMOVEENVIRONMENTSTRINGS, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7845 { szRemoveExistingProducts, IDS_DESC_REMOVEEXISTINGPRODUCTS, IDS_TEMP_REMOVEEXISTINGPRODUCTS, ACTION_RemoveExistingProducts, NULL },
7846 { szRemoveFiles, IDS_DESC_REMOVEFILES, IDS_TEMP_REMOVEFILES, ACTION_RemoveFiles, szInstallFiles },
7847 { szRemoveFolders, IDS_DESC_REMOVEFOLDERS, IDS_TEMP_REMOVEFOLDERS, ACTION_RemoveFolders, szCreateFolders },
7848 { szRemoveIniValues, IDS_DESC_REMOVEINIVALUES, IDS_TEMP_REMOVEINIVALUES, ACTION_RemoveIniValues, szWriteIniValues },
7849 { szRemoveODBC, IDS_DESC_REMOVEODBC, 0, ACTION_RemoveODBC, szInstallODBC },
7850 { szRemoveRegistryValues, IDS_DESC_REMOVEREGISTRYVALUES, IDS_TEMP_REMOVEREGISTRYVALUES, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7851 { szRemoveShortcuts, IDS_DESC_REMOVESHORTCUTS, IDS_TEMP_REMOVESHORTCUTS, ACTION_RemoveShortcuts, szCreateShortcuts },
7852 { szResolveSource, 0, 0, ACTION_ResolveSource, NULL },
7853 { szRMCCPSearch, IDS_DESC_RMCCPSEARCH, 0, ACTION_RMCCPSearch, NULL },
7854 { szScheduleReboot, 0, 0, ACTION_ScheduleReboot, NULL },
7855 { szSelfRegModules, IDS_DESC_SELFREGMODULES, IDS_TEMP_SELFREGMODULES, ACTION_SelfRegModules, szSelfUnregModules },
7856 { szSelfUnregModules, IDS_DESC_SELFUNREGMODULES, IDS_TEMP_SELFUNREGMODULES, ACTION_SelfUnregModules, szSelfRegModules },
7857 { szSetODBCFolders, IDS_DESC_SETODBCFOLDERS, 0, ACTION_SetODBCFolders, NULL },
7858 { szStartServices, IDS_DESC_STARTSERVICES, IDS_TEMP_STARTSERVICES, ACTION_StartServices, szStopServices },
7859 { szStopServices, IDS_DESC_STOPSERVICES, IDS_TEMP_STOPSERVICES, ACTION_StopServices, szStartServices },
7860 { szUnpublishComponents, IDS_DESC_UNPUBLISHCOMPONENTS, IDS_TEMP_UNPUBLISHCOMPONENTS, ACTION_UnpublishComponents, szPublishComponents },
7861 { szUnpublishFeatures, IDS_DESC_UNPUBLISHFEATURES, IDS_TEMP_UNPUBLISHFEATURES, ACTION_UnpublishFeatures, szPublishFeatures },
7862 { szUnpublishProduct, IDS_DESC_UNPUBLISHPRODUCT, 0, ACTION_UnpublishProduct, NULL }, /* for rollback only */
7863 { szUnregisterClassInfo, IDS_DESC_UNREGISTERCLASSINFO, IDS_TEMP_UNREGISTERCLASSINFO, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7864 { szUnregisterComPlus, IDS_DESC_UNREGISTERCOMPLUS, IDS_TEMP_UNREGISTERCOMPLUS, ACTION_UnregisterComPlus, szRegisterComPlus },
7865 { szUnregisterExtensionInfo, IDS_DESC_UNREGISTEREXTENSIONINFO, IDS_TEMP_UNREGISTEREXTENSIONINFO, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7866 { szUnregisterFonts, IDS_DESC_UNREGISTERFONTS, IDS_TEMP_UNREGISTERFONTS, ACTION_UnregisterFonts, szRegisterFonts },
7867 { szUnregisterMIMEInfo, IDS_DESC_UNREGISTERMIMEINFO, IDS_TEMP_UNREGISTERMIMEINFO, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7868 { szUnregisterProgIdInfo, IDS_DESC_UNREGISTERPROGIDINFO, IDS_TEMP_UNREGISTERPROGIDINFO, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7869 { szUnregisterTypeLibraries, IDS_DESC_UNREGISTERTYPELIBRARIES, IDS_TEMP_UNREGISTERTYPELIBRARIES, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7870 { szValidateProductID, 0, 0, ACTION_ValidateProductID, NULL },
7871 { szWriteEnvironmentStrings, IDS_DESC_WRITEENVIRONMENTSTRINGS, IDS_TEMP_WRITEENVIRONMENTSTRINGS, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7872 { szWriteIniValues, IDS_DESC_WRITEINIVALUES, IDS_TEMP_WRITEINIVALUES, ACTION_WriteIniValues, szRemoveIniValues },
7873 { szWriteRegistryValues, IDS_DESC_WRITEREGISTRYVALUES, IDS_TEMP_WRITEREGISTRYVALUES, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7874 { szINSTALL, 0, 0, ACTION_INSTALL, NULL },
7875 { 0 }
7878 static UINT ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action)
7880 UINT rc = ERROR_FUNCTION_NOT_CALLED;
7881 void *cookie;
7882 UINT i;
7884 if (is_wow64 && package->platform == PLATFORM_X64)
7885 Wow64DisableWow64FsRedirection(&cookie);
7887 i = 0;
7888 while (StandardActions[i].action != NULL)
7890 if (!strcmpW( StandardActions[i].action, action ))
7892 WCHAR description[100] = {0}, template[100] = {0};
7894 if (StandardActions[i].description != 0)
7895 LoadStringW(msi_hInstance, StandardActions[i].description, (LPWSTR)&description, 100);
7896 if (StandardActions[i].template != 0)
7897 LoadStringW(msi_hInstance, StandardActions[i].template, (LPWSTR)&template, 100);
7899 ui_actionstart(package, action, description, template);
7900 if (StandardActions[i].handler)
7902 ui_actioninfo( package, action, TRUE, 0 );
7903 rc = StandardActions[i].handler( package );
7904 ui_actioninfo( package, action, FALSE, !rc );
7906 if (StandardActions[i].action_rollback && !package->need_rollback)
7908 TRACE("scheduling rollback action\n");
7909 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7912 else
7914 FIXME("unhandled standard action %s\n", debugstr_w(action));
7915 rc = ERROR_SUCCESS;
7917 break;
7919 i++;
7922 if (is_wow64 && package->platform == PLATFORM_X64)
7923 Wow64RevertWow64FsRedirection(cookie);
7925 return rc;
7928 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action)
7930 UINT rc;
7932 TRACE("Performing action (%s)\n", debugstr_w(action));
7934 package->action_progress_increment = 0;
7935 rc = ACTION_HandleStandardAction(package, action);
7937 if (rc == ERROR_FUNCTION_NOT_CALLED)
7938 rc = ACTION_HandleCustomAction(package, action);
7940 if (rc == ERROR_FUNCTION_NOT_CALLED)
7941 WARN("unhandled msi action %s\n", debugstr_w(action));
7943 return rc;
7946 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7948 UINT rc = ERROR_SUCCESS;
7949 MSIRECORD *row;
7951 static const WCHAR query[] =
7952 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7953 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7954 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7955 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7956 static const WCHAR ui_query[] =
7957 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7958 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7959 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7960 ' ', '=',' ','%','i',0};
7962 if (needs_ui_sequence(package))
7963 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7964 else
7965 row = MSI_QueryGetRecord(package->db, query, seq);
7967 if (row)
7969 LPCWSTR action, cond;
7971 TRACE("Running the actions\n");
7973 /* check conditions */
7974 cond = MSI_RecordGetString(row, 2);
7976 /* this is a hack to skip errors in the condition code */
7977 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7979 msiobj_release(&row->hdr);
7980 return ERROR_SUCCESS;
7983 action = MSI_RecordGetString(row, 1);
7984 if (!action)
7986 ERR("failed to fetch action\n");
7987 msiobj_release(&row->hdr);
7988 return ERROR_FUNCTION_FAILED;
7991 rc = ACTION_PerformAction(package, action);
7993 msiobj_release(&row->hdr);
7996 return rc;
7999 /****************************************************
8000 * TOP level entry points
8001 *****************************************************/
8003 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
8004 LPCWSTR szCommandLine )
8006 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
8007 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
8008 WCHAR *reinstall = NULL, *productcode, *action;
8009 UINT rc;
8010 DWORD len = 0;
8012 if (szPackagePath)
8014 LPWSTR p, dir;
8015 LPCWSTR file;
8017 dir = strdupW(szPackagePath);
8018 p = strrchrW(dir, '\\');
8019 if (p)
8021 *(++p) = 0;
8022 file = szPackagePath + (p - dir);
8024 else
8026 msi_free(dir);
8027 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
8028 GetCurrentDirectoryW(MAX_PATH, dir);
8029 lstrcatW(dir, szBackSlash);
8030 file = szPackagePath;
8033 msi_free( package->PackagePath );
8034 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
8035 if (!package->PackagePath)
8037 msi_free(dir);
8038 return ERROR_OUTOFMEMORY;
8041 lstrcpyW(package->PackagePath, dir);
8042 lstrcatW(package->PackagePath, file);
8043 msi_free(dir);
8045 msi_set_sourcedir_props(package, FALSE);
8048 rc = msi_parse_command_line( package, szCommandLine, FALSE );
8049 if (rc != ERROR_SUCCESS)
8050 return rc;
8052 msi_apply_transforms( package );
8053 msi_apply_patches( package );
8055 if (msi_get_property( package->db, szAction, NULL, &len ))
8056 msi_set_property( package->db, szAction, szINSTALL, -1 );
8057 action = msi_dup_property( package->db, szAction );
8058 CharUpperW(action);
8060 msi_set_original_database_property( package->db, szPackagePath );
8061 msi_parse_command_line( package, szCommandLine, FALSE );
8062 msi_adjust_privilege_properties( package );
8063 msi_set_context( package );
8065 productcode = msi_dup_property( package->db, szProductCode );
8066 if (strcmpiW( productcode, package->ProductCode ))
8068 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
8069 msi_free( package->ProductCode );
8070 package->ProductCode = productcode;
8072 else msi_free( productcode );
8074 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
8076 TRACE("disabling rollback\n");
8077 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
8080 rc = ACTION_PerformAction(package, action);
8082 /* process the ending type action */
8083 if (rc == ERROR_SUCCESS)
8084 ACTION_PerformActionSequence(package, -1);
8085 else if (rc == ERROR_INSTALL_USEREXIT)
8086 ACTION_PerformActionSequence(package, -2);
8087 else if (rc == ERROR_INSTALL_SUSPEND)
8088 ACTION_PerformActionSequence(package, -4);
8089 else /* failed */
8091 ACTION_PerformActionSequence(package, -3);
8092 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
8094 package->need_rollback = TRUE;
8098 /* finish up running custom actions */
8099 ACTION_FinishCustomActions(package);
8101 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
8103 WARN("installation failed, running rollback script\n");
8104 execute_script( package, SCRIPT_ROLLBACK );
8106 msi_free( reinstall );
8107 msi_free( action );
8109 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
8110 return ERROR_SUCCESS_REBOOT_REQUIRED;
8112 return rc;